diff --git a/javascript-engine/Cargo.toml b/javascript-engine/Cargo.toml index 63671b0..ee3cd4a 100644 --- a/javascript-engine/Cargo.toml +++ b/javascript-engine/Cargo.toml @@ -9,9 +9,10 @@ edition = "2021" crate-type = ['cdylib'] [dependencies] -wit-bindgen-core = { path = '../wit-bindgen/crates/bindgen-core' } -wit-bindgen-guest-rust = { path = '../wit-bindgen/crates/guest-rust' } -boa_engine = "0.16.0" -getrandom = { version = "0.2.8", features = ["js"] } +boa_engine = { version = "0.16.0", path = "external/boa/boa_engine"} serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" + +[patch.crates-io] +iana-time-zone = { path = "external/iana-time-zone" } +getrandom = { path = "external/getrandom" } diff --git a/javascript-engine/external/boa/.config/nextest.toml b/javascript-engine/external/boa/.config/nextest.toml new file mode 100644 index 0000000..18e4061 --- /dev/null +++ b/javascript-engine/external/boa/.config/nextest.toml @@ -0,0 +1,3 @@ +[profile.ci] +# Don't fail fast in CI to run the full test suite. +fail-fast = false diff --git a/javascript-engine/external/boa/.editorconfig b/javascript-engine/external/boa/.editorconfig new file mode 100644 index 0000000..4e2d43c --- /dev/null +++ b/javascript-engine/external/boa/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[{Makefile,**.mk}] +# Use tabs for indentation (Makefiles require tabs) +indent_style = tab + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/javascript-engine/external/boa/.gitattributes b/javascript-engine/external/boa/.gitattributes new file mode 100644 index 0000000..951b3ca --- /dev/null +++ b/javascript-engine/external/boa/.gitattributes @@ -0,0 +1,30 @@ +# Handle line endings automatically for files detected as text +# and leave all files detected as binary untouched. +* text=auto + +# +# The above will handle all files NOT found below +# +# These files are text and should be normalized (Convert crlf => lf) +*.css eol=lf +*.htm eol=lf +*.html eol=lf +*.js eol=lf +*.json eol=lf +*.sh eol=lf +*.txt eol=lf +*.yml eol=lf +*.rs eol=lf +*.toml eol=lf +*.lock eol=lf +*.md eol=lf +*.svg eol=lf + +# These files are binary and should be left untouched +# (binary is a macro for -text -diff) +*.gif binary +*.ico binary +*.jar binary +*.jpg binary +*.jpeg binary +*.png binary diff --git a/javascript-engine/external/boa/.github/FUNDING.yml b/javascript-engine/external/boa/.github/FUNDING.yml new file mode 100644 index 0000000..1cb1d55 --- /dev/null +++ b/javascript-engine/external/boa/.github/FUNDING.yml @@ -0,0 +1 @@ +open_collective: boa diff --git a/javascript-engine/external/boa/.github/ISSUE_TEMPLATE/bug_report.md b/javascript-engine/external/boa/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..6e24984 --- /dev/null +++ b/javascript-engine/external/boa/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,52 @@ +--- +name: "\U0001F41B Bug report" +about: Create a report to help us improve +title: "" +labels: bug +assignees: "" +--- + + + +**Describe the bug** +A clear and concise description of what the bug is. + + + +**To Reproduce** +Steps to reproduce the issue, or JavaScript code that causes this failure. + + + +**Expected behavior** +Explain what you expected to happen, and what is happening instead. + + + +**Build environment (please complete the following information):** + +- OS: [e.g. Fedora Linux] +- Version: [e.g. 32] +- Target triple: [e.g. x86_64-unknown-linux-gnu] +- Rustc version: [e.g. rustc 1.43.0 (4fb7144ed 2020-04-20), running `rustc -V`] + +**Additional context** +Add any other context about the problem here. + + diff --git a/javascript-engine/external/boa/.github/ISSUE_TEMPLATE/config.yml b/javascript-engine/external/boa/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..d1962be --- /dev/null +++ b/javascript-engine/external/boa/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Discord channel + url: https://discord.gg/tUFFk9Y + about: Please ask and answer questions here. diff --git a/javascript-engine/external/boa/.github/ISSUE_TEMPLATE/custom.md b/javascript-engine/external/boa/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000..56ac092 --- /dev/null +++ b/javascript-engine/external/boa/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,13 @@ +--- +name: Custom +about: Open an issue in the repo that is neither a bug or a feature, such a new idea +title: "" +labels: "" +assignees: "" +--- + + + +E.g.: I think we should improve the way the JavaScript interpreter works by... diff --git a/javascript-engine/external/boa/.github/ISSUE_TEMPLATE/feature_request.md b/javascript-engine/external/boa/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..7687b7b --- /dev/null +++ b/javascript-engine/external/boa/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,43 @@ +--- +name: "\U0001F680 Feature request" +about: Suggest a new ECMAScript feature to be implemented, or a new capability of the engine. +title: "" +labels: enhancement +assignees: "" +--- + + + +**ECMASCript feature** +Explain the ECMAScript feature that you'd like to see implemented. + + + +**Example code** +Give a code example that should work after the implementation of this feature. + + diff --git a/javascript-engine/external/boa/.github/PULL_REQUEST_TEMPLATE.md b/javascript-engine/external/boa/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..3ac43fb --- /dev/null +++ b/javascript-engine/external/boa/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,12 @@ + + +This Pull Request fixes/closes #{issue_num}. + +It changes the following: + +- +- +- diff --git a/javascript-engine/external/boa/.github/codecov.yml b/javascript-engine/external/boa/.github/codecov.yml new file mode 100644 index 0000000..e377752 --- /dev/null +++ b/javascript-engine/external/boa/.github/codecov.yml @@ -0,0 +1,10 @@ +github_checks: + annotations: false + +coverage: + status: + project: + default: + threshold: 5% # allow 5% coverage variance + + patch: off diff --git a/javascript-engine/external/boa/.github/dependabot.yml b/javascript-engine/external/boa/.github/dependabot.yml new file mode 100644 index 0000000..9397dd8 --- /dev/null +++ b/javascript-engine/external/boa/.github/dependabot.yml @@ -0,0 +1,50 @@ +version: 2 +updates: + - package-ecosystem: npm + directory: / + schedule: + interval: daily + - package-ecosystem: github-actions + directory: / + schedule: + interval: daily + - package-ecosystem: cargo + directory: / + schedule: + interval: daily + - package-ecosystem: cargo + directory: /boa_cli/ + schedule: + interval: daily + - package-ecosystem: cargo + directory: /boa_engine/ + schedule: + interval: daily + - package-ecosystem: cargo + directory: /boa_gc/ + schedule: + interval: daily + - package-ecosystem: cargo + directory: /boa_interner/ + schedule: + interval: daily + - package-ecosystem: cargo + directory: /boa_profiler/ + schedule: + interval: daily + - package-ecosystem: cargo + directory: /boa_tester/ + schedule: + interval: daily + - package-ecosystem: cargo + directory: /boa_unicode/ + schedule: + interval: daily + - package-ecosystem: cargo + directory: /boa_wasm/ + schedule: + interval: daily + - package-ecosystem: gitsubmodule + directory: / + schedule: + interval: weekly diff --git a/javascript-engine/external/boa/.github/release.yml b/javascript-engine/external/boa/.github/release.yml new file mode 100644 index 0000000..20e969d --- /dev/null +++ b/javascript-engine/external/boa/.github/release.yml @@ -0,0 +1,16 @@ +# .github/release.yml + +changelog: + exclude: + authors: + - dependabot + categories: + - title: Feature Enhancements + labels: + - enhancement + - title: Bug Fixes + labels: + - bug + - title: Internal Improvements + labels: + - Internal diff --git a/javascript-engine/external/boa/.github/workflows/master.yml b/javascript-engine/external/boa/.github/workflows/master.yml new file mode 100644 index 0000000..e0b328c --- /dev/null +++ b/javascript-engine/external/boa/.github/workflows/master.yml @@ -0,0 +1,54 @@ +name: Main workflows +on: + push: + branches: + - main + +jobs: + benchmark: + if: ${{ github.actor != 'dependabot[bot]' }} + name: Upload docs and run benchmarks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1.0.7 + with: + toolchain: stable + override: true + profile: minimal + - name: Cache cargo + uses: actions/cache@v3 + with: + path: | + target + !target/doc_upload + ~/.cargo/git + ~/.cargo/registry + key: ${{ runner.os }}-cargo-doc-bench-${{ hashFiles('**/Cargo.lock') }} + - name: Generate documentation + uses: actions-rs/cargo@v1 + with: + command: doc + args: -v --document-private-items --all-features --workspace --no-deps --exclude boa_examples + - run: echo "" > target/doc/index.html + - run: | + if [ -d target/doc_upload ]; then rm -rf target/doc_upload; fi + mkdir target/doc_upload && mv target/doc target/doc_upload/doc + - name: Upload documentation + uses: crazy-max/ghaction-github-pages@v3.1.0 + with: + target_branch: gh-pages + keep_history: true + build_dir: target/doc_upload + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Run benchmark + run: cargo bench -p boa_engine -- --output-format bencher | tee output.txt + - name: Store benchmark result + uses: benchmark-action/github-action-benchmark@v1.15.0 + with: + name: Boa Benchmarks + tool: "cargo" + output-file-path: output.txt + auto-push: true + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/javascript-engine/external/boa/.github/workflows/pull_request.yml b/javascript-engine/external/boa/.github/workflows/pull_request.yml new file mode 100644 index 0000000..ff10cc1 --- /dev/null +++ b/javascript-engine/external/boa/.github/workflows/pull_request.yml @@ -0,0 +1,34 @@ +name: Benchmarks + +on: + pull_request: + branches: + - main + +jobs: + runBenchmark: + if: contains(github.event.pull_request.labels.*.name, 'run-benchmark') + name: run benchmark + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: actions-rs/toolchain@v1.0.7 + with: + toolchain: stable + override: true + profile: minimal + - name: Cache cargo + uses: actions/cache@v3 + with: + path: | + target + ~/.cargo/git + ~/.cargo/registry + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - uses: boa-dev/criterion-compare-action@v3.2.4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + branchName: ${{ github.base_ref }} + cwd: ./boa_engine diff --git a/javascript-engine/external/boa/.github/workflows/release.yml b/javascript-engine/external/boa/.github/workflows/release.yml new file mode 100644 index 0000000..bf19966 --- /dev/null +++ b/javascript-engine/external/boa/.github/workflows/release.yml @@ -0,0 +1,62 @@ +name: Publish Release +on: + release: + types: [published] + +jobs: + publish: + name: publish + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + + - name: Install cargo-workspaces + uses: actions-rs/install@v0.1 + with: + crate: cargo-workspaces + + - name: Release + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + PATCH: ${{ github.run_number }} + shell: bash + run: | + git config --global user.email "runner@gha.local" + git config --global user.name "Github Action" + cargo workspaces publish --from-git --yes minor + doc-publish: + # needs: publish + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install wasm-pack + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + - uses: actions/setup-node@v3 + with: + node-version: "16" + - run: npm ci + - name: Cache npm build + uses: actions/cache@v3 + with: + path: | + node_modules + target + boa_wasm/pkg + ~/.cargo/git + ~/.cargo/registry + key: ${{ runner.os }}-npm-build-target-${{ hashFiles('**/package-lock.json') }} + - run: npm run build:prod + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + with: + publish_dir: ./dist + destination_dir: playground + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/javascript-engine/external/boa/.github/workflows/rust.yml b/javascript-engine/external/boa/.github/workflows/rust.yml new file mode 100644 index 0000000..6b7195d --- /dev/null +++ b/javascript-engine/external/boa/.github/workflows/rust.yml @@ -0,0 +1,88 @@ +name: Continuous integration + +on: + pull_request: + branches: + - main + push: + branches: + - main + - staging # bors + - trying # bors + +jobs: + coverage: + name: Coverage + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1.0.7 + with: + toolchain: stable + override: true + profile: minimal + - uses: Swatinem/rust-cache@v2 + with: + key: tarpaulin + - name: Run cargo-tarpaulin + uses: actions-rs/tarpaulin@v0.1 + with: + args: --features intl --ignore-tests --engine llvm + - name: Upload to codecov.io + uses: codecov/codecov-action@v3 + + tests: + name: Build and Test + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: + - macos-latest + - windows-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1.0.7 + with: + toolchain: stable + override: true + profile: minimal + - uses: Swatinem/rust-cache@v2 + - name: Build tests + run: cargo test --no-run --profile ci + # this order is faster according to rust-analyzer + - name: Build + run: cargo build --all-targets --quiet --profile ci + - name: Install latest nextest + uses: taiki-e/install-action@nextest + - name: Test with nextest + run: cargo nextest run --profile ci --cargo-profile ci --features intl + + misc: + name: Misc + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1.0.7 + with: + toolchain: stable + override: true + profile: minimal + components: rustfmt, clippy + - uses: Swatinem/rust-cache@v2 + with: + key: misc + - name: Format (rustfmt) + run: cargo fmt --all --check + - name: Lint (All features) + run: cargo clippy --all-features --all-targets + - name: Lint (No features) + run: cargo clippy --no-default-features --all-targets + - name: Generate documentation + run: cargo doc -v --document-private-items --all-features + - name: Build + run: cargo build --all-targets --quiet --profile ci + - run: cd boa_examples + - name: Build examples + run: cargo build --quiet --profile ci + - name: Run example classes + run: cargo run --bin classes --profile ci diff --git a/javascript-engine/external/boa/.github/workflows/security_audit.yml b/javascript-engine/external/boa/.github/workflows/security_audit.yml new file mode 100644 index 0000000..d54cb3c --- /dev/null +++ b/javascript-engine/external/boa/.github/workflows/security_audit.yml @@ -0,0 +1,12 @@ +name: Security audit +on: + schedule: + - cron: "0 0 * * *" +jobs: + audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/audit-check@v1.2.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/javascript-engine/external/boa/.github/workflows/test262.yml b/javascript-engine/external/boa/.github/workflows/test262.yml new file mode 100644 index 0000000..84f8eb5 --- /dev/null +++ b/javascript-engine/external/boa/.github/workflows/test262.yml @@ -0,0 +1,118 @@ +name: EcmaScript official test suite (test262) +on: + push: + branches: + - main + tags: + - v* + pull_request: + branches: + - main + +jobs: + run_test262: + name: Run the test262 test suite + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v3 + with: + submodules: true + path: boa + - name: Install the Rust toolchain + uses: actions-rs/toolchain@v1.0.7 + with: + toolchain: stable + override: true + profile: minimal + - name: Cache cargo + uses: actions/cache@v3 + with: + path: | + target + ~/.cargo/git + ~/.cargo/registry + key: ${{ runner.os }}-cargo-test262-${{ hashFiles('**/Cargo.lock') }} + + # Run the test suite and upload the results + - name: Checkout GitHub pages + uses: actions/checkout@v3 + with: + ref: gh-pages + path: gh-pages + + - name: Run the test262 test suite + run: | + cd boa + mkdir ../results + cargo run --release --bin boa_tester -- run -v -o ../results/test262 + cd .. + + # Run the results comparison + - name: Compare results + if: github.event_name == 'pull_request' + id: compare-non-vm + shell: bash + run: | + cd boa + comment="$(./target/release/boa_tester compare ../gh-pages/test262/refs/heads/main/latest.json ../results/test262/pull/latest.json -m)" + echo "comment<> $GITHUB_OUTPUT + echo "$comment" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Get the PR number + if: github.event_name == 'pull_request' + id: pr-number + uses: kkak10/pr-number-action@v1.3 + + - name: Find Previous Comment + if: github.event_name == 'pull_request' + uses: peter-evans/find-comment@v2 + id: previous-comment + with: + issue-number: ${{ steps.pr-number.outputs.pr }} + body-includes: Test262 conformance changes + + - name: Update comment + if: github.event_name == 'pull_request' && steps.previous-comment.outputs.comment-id + uses: peter-evans/create-or-update-comment@v2 + continue-on-error: true + with: + comment-id: ${{ steps.previous-comment.outputs.comment-id }} + body: | + ### Test262 conformance changes + + ${{ steps.compare-non-vm.outputs.comment }} + ${{ steps.compare-vm.outputs.comment }} + edit-mode: replace + + - name: Write a new comment + if: github.event_name == 'pull_request' && !steps.previous-comment.outputs.comment-id + uses: peter-evans/create-or-update-comment@v2 + continue-on-error: true + with: + issue-number: ${{ steps.pr-number.outputs.pr }} + body: | + ### Test262 conformance changes + + ${{ steps.compare-non-vm.outputs.comment }} + ${{ steps.compare-vm.outputs.comment }} + + # Commit changes to GitHub pages. + - name: Commit files + if: github.event_name == 'push' + run: | + cp -r ./results/test262/* ./gh-pages/test262/ + cd gh-pages + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add test262 + git commit -m "Add new test262 results" -a + cd .. + - name: Upload results + if: github.event_name == 'push' + uses: ad-m/github-push-action@v0.6.0 + with: + directory: gh-pages + branch: gh-pages + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/javascript-engine/external/boa/.github/workflows/webassembly.yml b/javascript-engine/external/boa/.github/workflows/webassembly.yml new file mode 100644 index 0000000..6f2c769 --- /dev/null +++ b/javascript-engine/external/boa/.github/workflows/webassembly.yml @@ -0,0 +1,49 @@ +on: + pull_request: + branches: + - main + push: + branches: + - main + +name: Webassembly demo + +jobs: + check_style: + name: Check webassembly demo style + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Check code formatting + run: npx prettier --check . + + build: + name: Build webassembly demo + runs-on: ubuntu-latest + env: + WASM_PACK_PATH: ~/.cargo/bin/wasm-pack + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1.0.7 + with: + toolchain: stable + override: true + profile: minimal + - name: Install wasm-pack + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + - name: Cache npm build + uses: actions/cache@v3 + with: + path: | + node_modules + target + ~/.cargo/git + ~/.cargo/registry + boa_wasm/pkg + key: ${{ runner.os }}-npm-build-target-${{ hashFiles('**/package-lock.json') }} + - uses: actions/setup-node@v3 + with: + node-version: "16" + - run: npm ci + - run: npm run build diff --git a/javascript-engine/external/boa/.gitignore b/javascript-engine/external/boa/.gitignore new file mode 100644 index 0000000..56396d4 --- /dev/null +++ b/javascript-engine/external/boa/.gitignore @@ -0,0 +1,34 @@ +# IDE +.idea/ +*.iml + +# Vim +*.*.swp +*.*.swo + +# Build +target +dist +**/*.rs.bk +node_modules +.DS_Store +yarn-error.log +.vscode/settings.json + +# tests/js/test.js is used for testing changes locally +tests/js/test.js +.boa_history + +# Profiling +*.string_data +*.string_index +*.events +chrome_profiler.json +*.mm_profdata + +# Logs +*.log + +# Yarn +.yarn +.yarnrc.yml diff --git a/javascript-engine/external/boa/.gitmodules b/javascript-engine/external/boa/.gitmodules new file mode 100644 index 0000000..c41542f --- /dev/null +++ b/javascript-engine/external/boa/.gitmodules @@ -0,0 +1,3 @@ +[submodule "test262"] + path = test262 + url = https://github.com/tc39/test262.git diff --git a/javascript-engine/external/boa/.prettierignore b/javascript-engine/external/boa/.prettierignore new file mode 100644 index 0000000..ce0118b --- /dev/null +++ b/javascript-engine/external/boa/.prettierignore @@ -0,0 +1,10 @@ +# Ignore artifacts: +*.rs +target +node_modules +boa_engine/benches/bench_scripts/mini_js.js +boa_engine/benches/bench_scripts/clean_js.js +boa_wasm/pkg +dist +test262 +tests/js/test.js diff --git a/javascript-engine/external/boa/.vscode/launch.json b/javascript-engine/external/boa/.vscode/launch.json new file mode 100644 index 0000000..ce5770f --- /dev/null +++ b/javascript-engine/external/boa/.vscode/launch.json @@ -0,0 +1,35 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Launch", + "windows": { + "program": "${workspaceFolder}/target/debug/boa.exe" + }, + "program": "${workspaceFolder}/target/debug/boa", + "args": ["${workspaceFolder}/tests/js/test.js"], + "sourceLanguages": ["rust"] + }, + { + "type": "lldb", + "request": "launch", + "name": "Launch (VM)", + "cargo": { + "args": [ + "run", + "--manifest-path", + "./boa_cli/Cargo.toml", + "--features", + "vm" + ] + }, + "args": ["-t", "${workspaceFolder}/tests/js/test.js"], + "sourceLanguages": ["rust"] + } + ] +} diff --git a/javascript-engine/external/boa/.vscode/tasks.json b/javascript-engine/external/boa/.vscode/tasks.json new file mode 100644 index 0000000..018fc7e --- /dev/null +++ b/javascript-engine/external/boa/.vscode/tasks.json @@ -0,0 +1,90 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "type": "process", + "label": "Cargo Run", + "command": "cargo", + "args": ["run", "--bin", "boa", "./tests/js/test.js"], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "clear": true + }, + "options": { + "env": { + "RUST_BACKTRACE": "1" + } + }, + "problemMatcher": [] + }, + { + "type": "process", + "label": "Cargo Run (Profiler)", + "command": "cargo", + "args": [ + "run", + "--features", + "boa_engine/profiler", + "../tests/js/test.js" + ], + "group": "build", + "options": { + "env": { + "RUST_BACKTRACE": "full" + }, + "cwd": "${workspaceFolder}/boa_cli" + }, + "presentation": { + "clear": true + }, + "problemMatcher": [] + }, + { + "type": "process", + "label": "Run with VM trace", + "command": "cargo", + "args": ["run", "--bin", "boa", "--", "-t", "./tests/js/test.js"], + "group": "build", + "presentation": { + "clear": true + }, + "problemMatcher": [] + }, + { + "type": "process", + "label": "Get AST", + "command": "cargo", + "args": ["run", "--bin", "boa", "--", "-a=Debug", "./tests/js/test.js"], + "group": "build", + "presentation": { + "clear": true + }, + "problemMatcher": [] + }, + { + "type": "process", + "label": "Cargo Test", + "command": "cargo", + "args": ["test"], + "group": { + "kind": "test", + "isDefault": true + }, + "presentation": { + "clear": true + } + }, + { + "type": "process", + "label": "Cargo Test Build", + "command": "cargo", + "args": ["test", "--no-run"], + "group": "build" + } + ] +} diff --git a/javascript-engine/external/boa/CHANGELOG.md b/javascript-engine/external/boa/CHANGELOG.md new file mode 100644 index 0000000..c54af09 --- /dev/null +++ b/javascript-engine/external/boa/CHANGELOG.md @@ -0,0 +1,1268 @@ +# CHANGELOG + +## What's Changed + +# [0.16.0 (2022-09-25)](https://github.com/boa-dev/boa/compare/v0.15...v0.16) + +### Feature Enhancements + +- Implement getter and setter of `Object.prototype.__proto__` by @CYBAI in [#2110](https://github.com/boa-dev/boa/pull/2110) +- Execution stack & promises by @Razican in [#2107](https://github.com/boa-dev/boa/pull/2107) +- Add the `[[Done]]` field to iterators by @Razican in [#2125](https://github.com/boa-dev/boa/pull/2125) +- Fix for in/of loop initializer environment by @raskad in [#2135](https://github.com/boa-dev/boa/pull/2135) +- Implement `Promise.all` by @raskad in [#2140](https://github.com/boa-dev/boa/pull/2140) +- Implement `Promise.any` by @raskad in [#2145](https://github.com/boa-dev/boa/pull/2145) +- Implement `Promise.allSettled` by @raskad in [#2146](https://github.com/boa-dev/boa/pull/2146) +- Implement `super` expressions by @raskad in [#2116](https://github.com/boa-dev/boa/pull/2116) +- Implement `async function` and `await` by @raskad in [#2158](https://github.com/boa-dev/boa/pull/2158) +- Implementation of `JsMap` Wrapper by @nekevss in [#2115](https://github.com/boa-dev/boa/pull/2115) +- Safe wrapper for `JsSet` by @anuvratsingh in [#2162](https://github.com/boa-dev/boa/pull/2162) +- Implement `JsArrayBuffer` by @HalidOdat in [#2170](https://github.com/boa-dev/boa/pull/2170) +- Implement arrow function parsing based on `CoverParenthesizedExpressionAndArrowParameterList` by @raskad in [#2171](https://github.com/boa-dev/boa/pull/2171) +- Implement Generator Function Constructor by @raskad in [#2174](https://github.com/boa-dev/boa/pull/2174) +- Parse class private async generator methods by @raskad in [#2220](https://github.com/boa-dev/boa/pull/2220) +- Implement Async Generators by @raskad in [#2200](https://github.com/boa-dev/boa/pull/2200) +- Add field accessors to destructing assignment by @raskad in [#2213](https://github.com/boa-dev/boa/pull/2213) +- Added a bit more integer operation consistency to ByteDataBlock creation by @Razican in [#2272](https://github.com/boa-dev/boa/pull/2272) +- Implement Async-from-Sync Iterator Objects by @raskad in [#2234](https://github.com/boa-dev/boa/pull/2234) +- Add URI encoding and decoding functions by @Razican in [#2267](https://github.com/boa-dev/boa/pull/2267) +- Implement `for await...of` loops by @raskad in [#2286](https://github.com/boa-dev/boa/pull/2286) + +### Bug Fixes + +- Fix `eval` attributes by @raskad in [#2130](https://github.com/boa-dev/boa/pull/2130) +- Fix `this` in function calls by @raskad in [#2153](https://github.com/boa-dev/boa/pull/2153) +- Fix remaining `Promise` bugs by @raskad in [#2156](https://github.com/boa-dev/boa/pull/2156) +- Fix length/index in `32bit` architectures by @HalidOdat in [#2196](https://github.com/boa-dev/boa/pull/2196) +- Fix `yield` expression to end on line terminator by @raskad in [#2232](https://github.com/boa-dev/boa/pull/2232) +- Fix spread arguments in function calls by @raskad in [#2216](https://github.com/boa-dev/boa/pull/2216) +- Fix `arguments` object iterator function by @raskad in [#2231](https://github.com/boa-dev/boa/pull/2231) +- check history file exist if not create it by @udhaykumarbala in [#2245](https://github.com/boa-dev/boa/pull/2245) +- Do not auto-insert semicolon in `VariableDeclarationList` by @tunz in [#2266](https://github.com/boa-dev/boa/pull/2266) +- Fix property access of call expression by @tunz in [#2273](https://github.com/boa-dev/boa/pull/2273) +- fix computed property methods can call super methods by @creampnx-x in [#2274](https://github.com/boa-dev/boa/pull/2274) +- Fix regex literal `/[/]/` by @tunz in [#2277](https://github.com/boa-dev/boa/pull/2277) +- Fixed assignment expression parsing by @Razican in [#2268](https://github.com/boa-dev/boa/pull/2268) +- Fix labelled block statement by @creampnx-x in [#2285](https://github.com/boa-dev/boa/pull/2285) +- Implement missing global object internal methods by @raskad in [#2287](https://github.com/boa-dev/boa/pull/2287) + +### Internal Improvements + +- Fix spec links for some object operation methods by @CYBAI in [#2111](https://github.com/boa-dev/boa/pull/2111) +- Only run benchmarks on PRs when a label is set by @raskad in [#2114](https://github.com/boa-dev/boa/pull/2114) +- Refactor `construct` and `PromiseCapability` to preserve `JsObject` invariants by @jedel1043 in [#2136](https://github.com/boa-dev/boa/pull/2136) +- Remove `string-interner` dependency and implement custom string `Interner` by @jedel1043 in [#2147](https://github.com/boa-dev/boa/pull/2147) +- Fix clippy 1.62.0 lints by @raskad in [#2154](https://github.com/boa-dev/boa/pull/2154) +- Store call frames in `Vec` instead of singly-linked list by @HalidOdat in [#2164](https://github.com/boa-dev/boa/pull/2164) +- Dense/Packed JavaScript arrays by @HalidOdat in [#2167](https://github.com/boa-dev/boa/pull/2167) +- Fix Rust 1.63 clippy lints by @raskad in [#2230](https://github.com/boa-dev/boa/pull/2230) +- Removed some `unsafe_empty_trace!()` calls to improve performance by @Razican in [#2233](https://github.com/boa-dev/boa/pull/2233) +- Add integer type to fast path of `to_property_key` by @tunz in [#2261](https://github.com/boa-dev/boa/pull/2261) + +**Full Changelog**: https://github.com/boa-dev/boa/compare/v0.14...v0.15 + +# [0.15.0 (2022-06-10)](https://github.com/boa-dev/boa/compare/v0.14...v0.15) + +### Feature Enhancements + +- Deploy playground to custom destination dir by @jedel1043 in [#1943](https://github.com/boa-dev/boa/pull/1943) +- add README for crates.io publish by @superhawk610 in [#1952](https://github.com/boa-dev/boa/pull/1952) +- migrated to clap 3 by @manthanabc in [#1957](https://github.com/boa-dev/boa/pull/1957) +- Implement unscopables for Array.prototype by @NorbertGarfield in [#1963](https://github.com/boa-dev/boa/pull/1963) +- Retrieve feature-based results for Test262 runs by @NorbertGarfield in [#1980](https://github.com/boa-dev/boa/pull/1980) +- Added better error handling for the Boa tester by @Razican in [#1984](https://github.com/boa-dev/boa/pull/1984) +- Add From for JsValue by @lastmjs in [#1990](https://github.com/boa-dev/boa/pull/1990) +- Implement Classes by @raskad in [#1976](https://github.com/boa-dev/boa/pull/1976) +- Allow `PropertyName`s in `BindingProperty`in `ObjectBindingPattern` by @raskad in [#2022](https://github.com/boa-dev/boa/pull/2022) +- Allow `Initializer` after `ArrayBindingPattern` in `FormalParameter` by @raskad in [#2002](https://github.com/boa-dev/boa/pull/2002) +- Allow unicode escaped characters in identifiers that are keywords by @raskad in [#2021](https://github.com/boa-dev/boa/pull/2021) +- Feature `JsTypedArray`s by @HalidOdat in [#2003](https://github.com/boa-dev/boa/pull/2003) +- Allow creating object with true/false property names by @lupd in [#2028](https://github.com/boa-dev/boa/pull/2028) +- Implement `get RegExp.prototype.hasIndices` by @HalidOdat in [#2031](https://github.com/boa-dev/boa/pull/2031) +- Partial implementation for Intl.DateTimeFormat by @NorbertGarfield in [#2025](https://github.com/boa-dev/boa/pull/2025) +- Allow `let` as variable declaration name by @raskad in [#2044](https://github.com/boa-dev/boa/pull/2044) +- cargo workspaces fixes #2001 by @jasonwilliams in [#2026](https://github.com/boa-dev/boa/pull/2026) +- Move redeclaration errors to parser by @raskad in [#2027](https://github.com/boa-dev/boa/pull/2027) +- Feature `JsFunction` by @HalidOdat in [#2015](https://github.com/boa-dev/boa/pull/2015) +- Improve `JsString` performance by @YXL76 in [#2042](https://github.com/boa-dev/boa/pull/2042) +- Implement ResolveLocale helper by @NorbertGarfield in [#2036](https://github.com/boa-dev/boa/pull/2036) +- Refactor `IdentifierReference` parsing by @raskad in [#2055](https://github.com/boa-dev/boa/pull/2055) +- Implement the global `eval()` function by @raskad in [#2041](https://github.com/boa-dev/boa/pull/2041) +- DateTimeFormat helpers by @NorbertGarfield in [#2064](https://github.com/boa-dev/boa/pull/2064) +- Create `Date` standard constructor by @jedel1043 in [#2079](https://github.com/boa-dev/boa/pull/2079) +- Implement `ProxyBuilder` by @jedel1043 in [#2076](https://github.com/boa-dev/boa/pull/2076) +- Remove `strict` flag from `Context` by @raskad in [#2069](https://github.com/boa-dev/boa/pull/2069) +- Integrate ICU4X into `Intl` module by @jedel1043 in [#2083](https://github.com/boa-dev/boa/pull/2083) +- Implement `Function` constructor by @raskad in [#2090](https://github.com/boa-dev/boa/pull/2090) +- Parse private generator methods in classes by @raskad in [#2092](https://github.com/boa-dev/boa/pull/2092) + +### Bug Fixes + +- Fix link to the playground by @raskad in [#1947](https://github.com/boa-dev/boa/pull/1947) +- convert inner datetime to local in `to_date_string` by @superhawk610 in [#1953](https://github.com/boa-dev/boa/pull/1953) +- Fix panic on AST dump in JSON format by @kilotaras in [#1959](https://github.com/boa-dev/boa/pull/1959) +- Fix panic in do while by @pdogr in [#1968](https://github.com/boa-dev/boa/pull/1968) +- Support numbers with multiple leading zeroes by @lupd in [#1979](https://github.com/boa-dev/boa/pull/1979) +- Fix length properties on array methods by @lupd in [#1983](https://github.com/boa-dev/boa/pull/1983) +- Allow boolean/null as property identifier by dot operator assignment by @lupd in [#1985](https://github.com/boa-dev/boa/pull/1985) +- fix(vm): off-by-one in code block stringification. by @tsutton in [#1999](https://github.com/boa-dev/boa/pull/1999) +- Indicate bigint has constructor by @lupd in [#2008](https://github.com/boa-dev/boa/pull/2008) +- Change `ArrayBuffer` `byteLength` to accessor property by @lupd in [#2010](https://github.com/boa-dev/boa/pull/2010) +- Fix `ArrayBuffer.isView()` by @HalidOdat in [#2019](https://github.com/boa-dev/boa/pull/2019) +- Fix casting negative number to usize in `Array.splice` by @lupd in [#2030](https://github.com/boa-dev/boa/pull/2030) +- Fix `Symbol` and `BigInt` constructors by @HalidOdat in [#2032](https://github.com/boa-dev/boa/pull/2032) +- Make `Array.prototype` an array object by @HalidOdat in [#2033](https://github.com/boa-dev/boa/pull/2033) +- Fix early return in `for in loop` head by @raskad in [#2043](https://github.com/boa-dev/boa/pull/2043) + +### Internal Improvements + +- docs: update README by structuring the topics by @ftonato in [#1958](https://github.com/boa-dev/boa/pull/1958) +- Migrate to NPM and cleanup Playground by @jedel1043 in [#1951](https://github.com/boa-dev/boa/pull/1951) +- Fix performance bottleneck in VM by @pdogr in [#1973](https://github.com/boa-dev/boa/pull/1973) +- Remove `git2` and `hex` dependencies by @raskad in [#1992](https://github.com/boa-dev/boa/pull/1992) +- Fix rust 1.60 clippy lints by @raskad in [#2014](https://github.com/boa-dev/boa/pull/2014) +- Refactor `RegExp` constructor methods by @raskad in [#2049](https://github.com/boa-dev/boa/pull/2049) +- Fixing build for changes in clippy for Rust 1.61 by @Razican in [#2082](https://github.com/boa-dev/boa/pull/2082) + +**Full Changelog**: https://github.com/boa-dev/boa/compare/v0.14...v0.15 + +# [0.14.0 (2022-03-15) - Virtual Machine](https://github.com/boa-dev/boa/compare/v0.13...v0.14) + +### Feature Enhancements + +- Implement functions for vm by @HalidOdat in [#1433](https://github.com/boa-dev/boa/pull/1433) +- Implement Object.getOwnPropertyNames and Object.getOwnPropertySymbols by @kevinputera in [#1606](https://github.com/boa-dev/boa/pull/1606) +- Implement `Symbol.prototype.valueOf` by @hle0 in [#1618](https://github.com/boa-dev/boa/pull/1618) +- Implement Array.prototype.at() by @nekevss in [#1613](https://github.com/boa-dev/boa/pull/1613) +- Implement Array.from by @nrabulinski [#1831](https://github.com/boa-dev/boa/pull/1831) +- Implement String.fromCharCode by @hle0 in [#1619](https://github.com/boa-dev/boa/pull/1619) +- Implement `Typed Array` built-in by @Razican in [#1552](https://github.com/boa-dev/boa/pull/1552) +- Implement arguments exotic objects by @jedel1043 in [#1522](https://github.com/boa-dev/boa/pull/1522) +- Allow `BindingPattern`s as `CatchParameter` by @lowr in [#1628](https://github.com/boa-dev/boa/pull/1628) +- Implement `Symbol.prototype[ @@toPrimitive ]` by @Nimpruda in [#1634](https://github.com/boa-dev/boa/pull/1634) +- Implement Generator parsing by @raskad in [#1575](https://github.com/boa-dev/boa/pull/1575) +- Implement Object.hasOwn and improve Object.prototype.hasOwnProperty by @kevinputera in [#1639](https://github.com/boa-dev/boa/pull/1639) +- Hashbang lexer support by @nekevss in [#1631](https://github.com/boa-dev/boa/pull/1631) +- Implement `delete` operator in the vm by @raskad in [#1649](https://github.com/boa-dev/boa/pull/1649) +- Implement Object.fromEntries by @kevinputera in [#1660](https://github.com/boa-dev/boa/pull/1660) +- Initial implementation for increment/decrement in VM by @abhishekc-sharma in [#1621](https://github.com/boa-dev/boa/pull/1621) +- Implement `Proxy` object by @raskad in [#1664](https://github.com/boa-dev/boa/pull/1664) +- Implement object literals for vm by @raskad in [#1668](https://github.com/boa-dev/boa/pull/1668) +- Implement Array findLast and findLastIndex by @bsinky in [#1665](https://github.com/boa-dev/boa/pull/1665) +- Implement `DataView` built-in object by @Nimpruda in [#1662](https://github.com/boa-dev/boa/pull/1662) +- Clean-up contribution guidelines, dependencies, Test262, MSRV by @Razican in [#1683](https://github.com/boa-dev/boa/pull/1683) +- Implement Async Generator Parsing by @nekevss in [#1669](https://github.com/boa-dev/boa/pull/1669) +- Implement prototype of `Intl` built-in by @hle0 in [#1622](https://github.com/boa-dev/boa/pull/1622) +- Add limited console.trace implementation by @osman-turan in [#1623](https://github.com/boa-dev/boa/pull/1623) +- Allow `BindingPattern` in function parameters by @am-a-man in [#1666](https://github.com/boa-dev/boa/pull/1666) +- Small test ux improvements by @orndorffgrant in [#1704](https://github.com/boa-dev/boa/pull/1704) +- Implement missing vm operations by @raskad in [#1697](https://github.com/boa-dev/boa/pull/1697) +- Added fallible allocation to data blocks by @Razican in [#1728](https://github.com/boa-dev/boa/pull/1728) +- Document CodeBlock by @TheDoctor314 in [#1691](https://github.com/boa-dev/boa/pull/1691) +- Generic `JsResult` in `context.throw_` methods by @HalidOdat in [#1734](https://github.com/boa-dev/boa/pull/1734) +- Implement `String.raw( template, ...substitutions )` by @HalidOdat in [#1741](https://github.com/boa-dev/boa/pull/1741) +- Updated test262 suite and dependencies by @Razican in [#1755](https://github.com/boa-dev/boa/pull/1755) +- Lexer string interning by @Razican in [#1758](https://github.com/boa-dev/boa/pull/1758) +- Adjust `compile` and `execute` to avoid clones by @Razican in [#1778](https://github.com/boa-dev/boa/pull/1778) +- Interner support in the parser by @Razican in [#1765](https://github.com/boa-dev/boa/pull/1765) +- Convert `Codeblock` variables to `Sym` by @raskad in [#1798](https://github.com/boa-dev/boa/pull/1798) +- Using production builds for WebAssembly by @Razican in [#1825](https://github.com/boa-dev/boa/pull/1825) +- Give the arrow function its proper name by @rumpl in [#1832](https://github.com/boa-dev/boa/pull/1832) +- Unwrap removal by @Razican in [#1842](https://github.com/boa-dev/boa/pull/1842) +- Feature `JsArray` by @HalidOdat in [#1746](https://github.com/boa-dev/boa/pull/1746) +- Rename "Boa" to boa_engine, moved GC and profiler to their crates by @Razican in [#1844](https://github.com/boa-dev/boa/pull/1844) +- Added conversions from and to serde_json's Value type by @Razican in [#1851](https://github.com/boa-dev/boa/pull/1851) +- Toggleable `JsValue` internals displaying by @HalidOdat in [#1865](https://github.com/boa-dev/boa/pull/1865) +- Implement generator execution by @raskad in [#1790](https://github.com/boa-dev/boa/pull/1790) +- Feature arrays with empty elements by @HalidOdat in [#1870](https://github.com/boa-dev/boa/pull/1870) +- Removed reference counted pointers from `JsValue` variants by @Razican in [#1866](https://github.com/boa-dev/boa/pull/1866) +- Implement `Object.prototype.toLocaleString()` by @HalidOdat in [#1875](https://github.com/boa-dev/boa/pull/1875) +- Implement `AggregateError` by @HalidOdat in [#1888](https://github.com/boa-dev/boa/pull/1888) +- Implement destructing assignments for assignment expressions by @raskad in [#1895](https://github.com/boa-dev/boa/pull/1895) +- Added boa examples by @elasmojs in [#1161](https://github.com/boa-dev/boa/pull/1161) + +### Bug Fixes + +- Fix BigInt and Number comparison by @HalidOdat [#1887](https://github.com/boa-dev/boa/pull/1887) +- Fix broken structure links in the documentation by @abhishekc-sharma in [#1612](https://github.com/boa-dev/boa/pull/1612) +- Use function name from identifiers in assignment expressions by @raskad [#1908](https://github.com/boa-dev/boa/pull/1908) +- Fix integer parsing by @nrabulinski in [#1614](https://github.com/boa-dev/boa/pull/1614) +- Fix `Number.toExponential` and `Number.toFixed` by @nrabulinski in [#1620](https://github.com/boa-dev/boa/pull/1620) +- Badge updates by @atouchet in [#1638](https://github.com/boa-dev/boa/pull/1638) +- refactor: fix construct_error functions by @RageKnify in [#1703](https://github.com/boa-dev/boa/pull/1703) +- Fix internal vm tests by @raskad in [#1718](https://github.com/boa-dev/boa/pull/1718) +- Removed a bunch of warnings and clippy errors by @Razican in [#1754](https://github.com/boa-dev/boa/pull/1754) +- Fix some broken links in the profiler documentation by @Razican in [#1762](https://github.com/boa-dev/boa/pull/1762) +- Add proxy handling in `isArray` method by @raskad in [#1777](https://github.com/boa-dev/boa/pull/1777) +- Copy/paste fix in Proxy error message by @icecream17 in [#1787](https://github.com/boa-dev/boa/pull/1787) +- Fixed #1768 by @Razican in [#1820](https://github.com/boa-dev/boa/pull/1820) +- Fix string.prototype methods and add static string methods by @jevancc in [#1123](https://github.com/boa-dev/boa/pull/1123) +- Handle allocation errors by @y21 in [#1850](https://github.com/boa-dev/boa/pull/1850) +- Fix wasm use outside browsers by @Razican in [#1846](https://github.com/boa-dev/boa/pull/1846) +- 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) +- Correct reference error message by @aaronmunsters in [#1855](https://github.com/boa-dev/boa/pull/1855) +- Fixing main branch workflows by @Razican in [#1858](https://github.com/boa-dev/boa/pull/1858) +- Correct pop_on_return behaviour by @VTCAKAVSMoACE in [#1853](https://github.com/boa-dev/boa/pull/1853) +- Fix equality between objects and `undefined` or `null` by @HalidOdat in [#1872](https://github.com/boa-dev/boa/pull/1872) +- Removing the panic in favour of an error result by @Razican in [#1874](https://github.com/boa-dev/boa/pull/1874) +- Make `Object.getOwnPropertyDescriptors` spec compliant by @HalidOdat in [#1876](https://github.com/boa-dev/boa/pull/1876) +- Make `Error` and `%NativeError%` spec compliant by @HalidOdat in [#1879](https://github.com/boa-dev/boa/pull/1879) +- Fix `Number.prototype.toString` when passing `undefined` as radix by @HalidOdat in [#1877](https://github.com/boa-dev/boa/pull/1877) +- Cleanup vm stack on function return by @raskad in [#1880](https://github.com/boa-dev/boa/pull/1880) +- `%NativeError%.[[prototype]]` should be `Error` constructor by @HalidOdat in [#1883](https://github.com/boa-dev/boa/pull/1883) +- Make `StringToNumber` spec compliant by @HalidOdat in [#1881](https://github.com/boa-dev/boa/pull/1881) +- Fix `PropertyKey` to `JsValue` conversion by @HalidOdat in [#1886](https://github.com/boa-dev/boa/pull/1886) +- Make iterator spec complaint by @HalidOdat in [#1889](https://github.com/boa-dev/boa/pull/1889) +- Implement `Number.parseInt` and `Number.parseFloat` by @HalidOdat in [#1894](https://github.com/boa-dev/boa/pull/1894) +- Fix unreachable panics in compile_access by @VTCAKAVSMoACE in [#1861](https://github.com/boa-dev/boa/pull/1861) +- Continue panic fixes by @VTCAKAVSMoACE in [#1896](https://github.com/boa-dev/boa/pull/1896) +- Deny const declarations without initializer inside for loops by @jedel1043 in [#1903](https://github.com/boa-dev/boa/pull/1903) +- Fix try/catch/finally related bugs and add tests by @jedel1043 in [#1901](https://github.com/boa-dev/boa/pull/1901) +- Compile StatementList after parse passes on negative tests by @raskad in [#1906](https://github.com/boa-dev/boa/pull/1906) +- Prevent breaks without loop or switch from causing panics by @VTCAKAVSMoACE in [#1860](https://github.com/boa-dev/boa/pull/1860) +- Fix postfix increment and decrement return values by @raskad in [#1913](https://github.com/boa-dev/boa/pull/1913) + +### Internal Improvements + +- Rewrite initialization of builtins to use the `BuiltIn` trait by @jedel1043 in [#1586](https://github.com/boa-dev/boa/pull/1586) +- Unify object creation with `empty` and `from_proto_and_data` methods by @jedel1043 in [#1567](https://github.com/boa-dev/boa/pull/1567) +- VM Tidy Up by @jasonwilliams in [#1610](https://github.com/boa-dev/boa/pull/1610) +- Fix master refs to main by @jasonwilliams in [#1637](https://github.com/boa-dev/boa/pull/1637) +- Refresh vm docs and fix bytecode trace output by @raskad [#1921](https://github.com/boa-dev/boa/pull/1921) +- Change type of object prototypes to `Option` by @jedel1043 in [#1640](https://github.com/boa-dev/boa/pull/1640) +- Refactor `Function` internal methods and implement `BoundFunction` objects by @jedel1043 in [#1583](https://github.com/boa-dev/boa/pull/1583) +- change that verbosity comparison to > 2 by @praveenbakkal in [#1680](https://github.com/boa-dev/boa/pull/1680) +- Respect rust 1.56 by @RageKnify in [#1681](https://github.com/boa-dev/boa/pull/1681) +- Add bors to CI by @RageKnify in [#1684](https://github.com/boa-dev/boa/pull/1684) +- Adding VM conformance output to PR checks by @Razican in [#1685](https://github.com/boa-dev/boa/pull/1685) +- Start removing non-VM path by @jasonwilliams in [#1747](https://github.com/boa-dev/boa/pull/1747) +- Using upstream benchmark action by @Razican in [#1753](https://github.com/boa-dev/boa/pull/1753) +- Fix bors hanging by @RageKnify in [#1767](https://github.com/boa-dev/boa/pull/1767) +- add more timers on object functions by @jasonwilliams in [#1775](https://github.com/boa-dev/boa/pull/1775) +- Update the PR benchmarks action by @Razican in [#1774](https://github.com/boa-dev/boa/pull/1774) +- General code clean-up and new lint addition by @Razican in [#1809](https://github.com/boa-dev/boa/pull/1809) +- Reduced the size of AST nodes by @Razican in [#1821](https://github.com/boa-dev/boa/pull/1821) +- Using the new formatting arguments from Rust 1.58 by @Razican in [#1834](https://github.com/boa-dev/boa/pull/1834) +- Rework RegExp struct to include bitflags field by @aaronmunsters in [#1837](https://github.com/boa-dev/boa/pull/1837) +- Ignore wastefull `RegExp` tests by @raskad in [#1840](https://github.com/boa-dev/boa/pull/1840) +- Refactor the environment for runtime performance by @raskad in [#1829](https://github.com/boa-dev/boa/pull/1829) +- Refactor mapped `Arguments` object by @raskad in [#1849](https://github.com/boa-dev/boa/pull/1849) +- Fixed dependabot for submodule by @Razican in [#1856](https://github.com/boa-dev/boa/pull/1856) +- Refactorings for Rust 1.59 by @RageKnify in [#1867](https://github.com/boa-dev/boa/pull/1867) +- Removing internal deprecated functions by @HalidOdat in [#1854](https://github.com/boa-dev/boa/pull/1854) +- Remove `toInteger` and document the `string` builtin by @jedel1043 in [#1884](https://github.com/boa-dev/boa/pull/1884) +- Extract `Intrinsics` struct from `Context` and cleanup names by @jedel1043 in [#1890](https://github.com/boa-dev/boa/pull/1890) + +**Full Changelog**: https://github.com/boa-dev/boa/compare/v0.13...v0.14 + +# [0.13.0 (2021-09-30) - Many new features and refactors](https://github.com/boa-dev/boa/compare/v0.12.0...v0.13.0) + +Feature Enhancements: + +- [FEATURE #1526](https://github.com/boa-dev/boa/pull/1526): Implement ComputedPropertyName for accessor properties in ObjectLiteral (@raskad) +- [FEATURE #1365](https://github.com/boa-dev/boa/pull/1365): Implement splice method (@neeldug) +- [FEATURE #1364](https://github.com/boa-dev/boa/pull/1364): Implement spread for objects (@FrancisMurillo) +- [FEATURE #1525](https://github.com/boa-dev/boa/pull/1525): Implement Object.preventExtensions() and Object.isExtensible() (@HalidOdat) +- [FEATURE #1508](https://github.com/boa-dev/boa/pull/1508): Implement Object.values() (@HalidOdat) +- [FEATURE #1332](https://github.com/boa-dev/boa/pull/1332): Implement Array.prototype.sort (@jedel1043) +- [FEATURE #1417](https://github.com/boa-dev/boa/pull/1471): Implement Object.keys and Object.entries (@skyne98) +- [FEATURE #1406](https://github.com/boa-dev/boa/pull/1406): Implement destructuring assignments (@raskad) +- [FEATURE #1469](https://github.com/boa-dev/boa/pull/1469): Implement String.prototype.replaceAll (@raskad) +- [FEATURE #1442](https://github.com/boa-dev/boa/pull/1442): Implement closure functions (@HalidOdat) +- [FEATURE #1390](https://github.com/boa-dev/boa/pull/1390): Implement RegExp named capture groups (@raskad) +- [FEATURE #1424](https://github.com/boa-dev/boa/pull/1424): Implement Symbol.for and Symbol.keyFor (@HalidOdat) +- [FEATURE #1375](https://github.com/boa-dev/boa/pull/1375): Implement `at` method for string (@neeldug) +- [FEATURE #1369](https://github.com/boa-dev/boa/pull/1369): Implement normalize method (@neeldug) +- [FEATURE #1334](https://github.com/boa-dev/boa/pull/1334): Implement Array.prototype.copyWithin (@jedel1043) +- [FEATURE #1326](https://github.com/boa-dev/boa/pull/1326): Implement get RegExp[@@species] (@raskad) +- [FEATURE #1314](https://github.com/boa-dev/boa/pull/1314): Implement RegExp.prototype [ @@search ] ( string ) (@raskad) +- [FEATURE #1451](https://github.com/boa-dev/boa/pull/1451): Feature prelude module (@HalidOdat) +- [FEATURE #1523](https://github.com/boa-dev/boa/pull/1523): Allow moving NativeObject variables into closures as external captures (@jedel1043) + +Bug Fixes: + +- [BUG #1521](https://github.com/boa-dev/boa/pull/1521): Added "js" feature for getrandom for WebAssembly builds (@Razican) +- [BUG #1528](https://github.com/boa-dev/boa/pull/1528): Always return undefined from functions that do not return (@raskad) +- [BUG #1518](https://github.com/boa-dev/boa/pull/1518): Moving a JsObject inside a closure caused a panic (@jedel1043) +- [BUG #1502](https://github.com/boa-dev/boa/pull/1502): Adjust EnumerableOwnPropertyNames to use all String type property keys (@raskad) +- [BUG #1415](https://github.com/boa-dev/boa/pull/1415): Fix panic on bigint size (@neeldug) +- [BUG #1477](https://github.com/boa-dev/boa/pull/1477): Properly handle NaN in new Date() (@raskad) +- [BUG #1449](https://github.com/boa-dev/boa/pull/1449): Make Array.prototype methods spec compliant (@HalidOdat) +- [BUG #1353](https://github.com/boa-dev/boa/pull/1353): Make Array.prototype.concat spec compliant (@neeldug) +- [BUG #1384](https://github.com/boa-dev/boa/pull/1384): bitwise not operation (spec improvements) (@neeldug) +- [BUG #1374](https://github.com/boa-dev/boa/pull/1374): Match and regexp construct fixes (@neeldug) +- [BUG #1366](https://github.com/boa-dev/boa/pull/1366): Use lock for map iteration (@joshwd36) +- [BUG #1360](https://github.com/boa-dev/boa/pull/1360): Adjust a comment to be next to the correct module (@teymour-aldridge) +- [BUG #1349](https://github.com/boa-dev/boa/pull/1349): Fixes Array.protoype.includes (@neeldug) +- [BUG #1348](https://github.com/boa-dev/boa/pull/1348): Fixes unshift maximum size (@neeldug) +- [BUG #1339](https://github.com/boa-dev/boa/pull/1339): Scripts should not be considered in a block (@macmv) +- [BUG #1312](https://github.com/boa-dev/boa/pull/1312): Fix display for nodes (@macmv) +- [BUG #1347](https://github.com/boa-dev/boa/pull/1347): Fix stringpad abstract operation (@neeldug) +- [BUG #1584](https://github.com/boa-dev/boa/pull/1584): Refactor the Math builtin object (spec compliant) (@jedel1043) +- [BUG #1535](https://github.com/boa-dev/boa/pull/1535): Refactor JSON.parse (@raskad) +- [BUG #1572](https://github.com/boa-dev/boa/pull/1572): Refactor builtin Map intrinsics to follow more closely the spec (@jedel1043) +- [BUG #1445](https://github.com/boa-dev/boa/pull/1445): improve map conformance without losing perf (@neeldug) +- [BUG #1488](https://github.com/boa-dev/boa/pull/1488): Date refactor (@raskad) +- [BUG #1463](https://github.com/boa-dev/boa/pull/1463): Return function execution result from constructor if the function returned (@raskad) +- [BUG #1434](https://github.com/boa-dev/boa/pull/1434): Refactor regexp costructor (@raskad) +- [BUG #1350](https://github.com/boa-dev/boa/pull/1350): Refactor / Implement RegExp functions (@RageKnify) (@raskad) +- [BUG #1331](https://github.com/boa-dev/boa/pull/1331): Implement missing species getters (@raskad) + +Internal Improvements: + +- [INTERNAL #1569](https://github.com/boa-dev/boa/pull/1569): Refactor EnvironmentRecordTrait functions (@raskad) +- [INTERNAL #1464](https://github.com/boa-dev/boa/pull/1464): Optimize integer negation (@HalidOdat) +- [INTERNAL #1550](https://github.com/boa-dev/boa/pull/1550): Add strict mode flag to Context (@raskad) +- [INTERNAL #1561](https://github.com/boa-dev/boa/pull/1561): Implement abstract operation GetPrototypeFromConstructor (@jedel1043) +- [INTERNAL #1309](https://github.com/boa-dev/boa/pull/1309): Implement Display for function objects(@kvnvelasco) +- [INTERNAL #1492](https://github.com/boa-dev/boa/pull/1492): Implement new get_or_undefined method for `JsValue` (@jedel1043) +- [INTERNAL #1553](https://github.com/boa-dev/boa/pull/1553): Fix benchmark action in CI (@jedel1043) +- [INTERNAL #1547](https://github.com/boa-dev/boa/pull/1547): Replace FxHashMap with IndexMap in object properties (@raskad) +- [INTERNAL #1435](https://github.com/boa-dev/boa/pull/1435): Constant JsStrings (@HalidOdat) +- [INTERNAL #1499](https://github.com/boa-dev/boa/pull/1499): Updated the Test262 submodule (@Razican) +- [INTERNAL #1458](https://github.com/boa-dev/boa/pull/1458): Refactor the JS testing system (@bartlomieju) +- [INTERNAL #1485](https://github.com/boa-dev/boa/pull/1485): Implement abstract operation CreateArrayFromList (@jedel1043) +- [INTERNAL #1465](https://github.com/boa-dev/boa/pull/1465): Feature throw Error object (@HalidOdat) +- [INTERNAL #1493](https://github.com/boa-dev/boa/pull/1493): Rename boa::Result to JsResult (@bartlomieju) +- [INTERNAL #1457](https://github.com/boa-dev/boa/pull/1457): Rename Value to JsValue (@HalidOdat) +- [INTERNAL #1460](https://github.com/boa-dev/boa/pull/1460): Change StringGetOwnProperty to produce the same strings that the lexer produces (@raskad) +- [INTERNAL #1425](https://github.com/boa-dev/boa/pull/1425): Extract PropertyMap struct from Object (@jedel1043) +- [INTERNAL #1432](https://github.com/boa-dev/boa/pull/1432): Proposal of new PropertyDescriptor design (@jedel1043) +- [INTERNAL #1383](https://github.com/boa-dev/boa/pull/1383): clippy lints and cleanup of old todos (@neeldug) +- [INTERNAL #1346](https://github.com/boa-dev/boa/pull/1346): Implement gh-page workflow on release (@FrancisMurillo) +- [INTERNAL #1422](https://github.com/boa-dev/boa/pull/1422): Refactor internal methods and make some builtins spec compliant (@HalidOdat) +- [INTERNAL #1419](https://github.com/boa-dev/boa/pull/1419): Fix DataDescriptor Value to possibly be empty (@raskad) +- [INTERNAL #1357](https://github.com/boa-dev/boa/pull/1357): Add Example to Execute a Function of a Script File (@schrieveslaach) +- [INTERNAL #1408](https://github.com/boa-dev/boa/pull/1408): Refactor JavaScript bigint rust type (@HalidOdat) +- [INTERNAL #1380](https://github.com/boa-dev/boa/pull/1380): Custom JavaScript string rust type (@HalidOdat) +- [INTERNAL #1382](https://github.com/boa-dev/boa/pull/1382): Refactor JavaScript symbol rust type (@HalidOdat) +- [INTERNAL #1361](https://github.com/boa-dev/boa/pull/1361): Redesign bytecode virtual machine (@HalidOdat) +- [INTERNAL #1381](https://github.com/boa-dev/boa/pull/1381): Fixed documentation warnings (@Razican) +- [INTERNAL #1352](https://github.com/boa-dev/boa/pull/1352): Respect Rust 1.53 (@RageKnify) +- [INTERNAL #1356](https://github.com/boa-dev/boa/pull/1356): Respect Rust fmt updates (@RageKnify) +- [INTERNAL #1338](https://github.com/boa-dev/boa/pull/1338): Fix cargo check errors (@neeldug) +- [INTERNAL #1329](https://github.com/boa-dev/boa/pull/1329): Allow Value.set_field to throw (@raskad) +- [INTERNAL #1333](https://github.com/boa-dev/boa/pull/1333): adds condition to avoid triggers from dependabot (@neeldug) +- [INTERNAL #1337](https://github.com/boa-dev/boa/pull/1337): Fix github actions (@neeldug) + +# [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) + +Feature Enhancements: + +- [FEATURE #1085](https://github.com/boa-dev/boa/pull/1085): Add primitive promotion for method calls on `GetField` (@RageKnify) +- [FEATURE #1033](https://github.com/boa-dev/boa/pull/1033): Implement `Reflect` built-in object (@tofpie) +- [FEATURE #1151](https://github.com/boa-dev/boa/pull/1151): Fully implement `EmptyStatement` (@SamuelQZQ) +- [FEATURE #1158](https://github.com/boa-dev/boa/pull/1158): Include name in verbose results output of `boa-tester` (@0x7D2B) +- [FEATURE #1225](https://github.com/boa-dev/boa/pull/1225): Implement `Math[ @@toStringTag ]` (@HalidOdat) +- [FEATURE #1224](https://github.com/boa-dev/boa/pull/1224): Implement `JSON[ @@toStringTag ]` (@HalidOdat) +- [FEATURE #1222](https://github.com/boa-dev/boa/pull/1222): Implement `Symbol.prototype.description` accessor (@HalidOdat) +- [FEATURE #1221](https://github.com/boa-dev/boa/pull/1221): Implement `RegExp` flag accessors (@HalidOdat) +- [FEATURE #1240](https://github.com/boa-dev/boa/pull/1240): Stop ignoring a bunch of tests (@Razican) +- [FEATURE #1132](https://github.com/boa-dev/boa/pull/1132): Implement `Array.prototype.flat`/`flatMap` (@davimiku) +- [FEATURE #1235](https://github.com/boa-dev/boa/pull/1235): Implement `Object.assign( target, ...sources )` (@HalidOdat) +- [FEATURE #1243](https://github.com/boa-dev/boa/pull/1243): Cross realm symbols (@HalidOdat) +- [FEATURE #1249](https://github.com/boa-dev/boa/pull/1249): Implement `Map.prototype[ @@toStringTag ]` (@wylie39) +- [FEATURE #1111](https://github.com/boa-dev/boa/pull/1111): Implement `Set` builtin object (@RageKnify) +- [FEATURE #1265](https://github.com/boa-dev/boa/pull/1265): Implement `BigInt.prototype[ @@toStringTag ]` (@n14littl) +- [FEATURE #1102](https://github.com/boa-dev/boa/pull/1102): Support Unicode escape in identifier names (@jevancc) +- [FEATURE #1273](https://github.com/boa-dev/boa/pull/1273): Add default parameter support (@0x7D2B) +- [FEATURE #1292](https://github.com/boa-dev/boa/pull/1292): Implement `symbol.prototype[ @@ToStringTag ]` (@moadmmh) +- [FEATURE #1291](https://github.com/boa-dev/boa/pull/1291): Support `GetOwnProperty` for `string` exotic object (@jarkonik) +- [FEATURE #1296](https://github.com/boa-dev/boa/pull/1296): Added the `$262` object to the Test262 test runner (@Razican) +- [FEATURE #1127](https://github.com/boa-dev/boa/pull/1127): Implement `Array.of` (@camc) + +Bug Fixes: + +- [BUG #1071](https://github.com/boa-dev/boa/pull/1071): Fix attribute configurable of the length property of arguments (@tofpie) +- [BUG #1073](https://github.com/boa-dev/boa/pull/1073): Fixed spelling (@vishalsodani) +- [BUG #1072](https://github.com/boa-dev/boa/pull/1072): Fix `get`/`set` as short method name in `object` (@tofpie) +- [BUG #1077](https://github.com/boa-dev/boa/pull/1077): Fix panics from multiple borrows of `Map` (@joshwd36) +- [BUG #1079](https://github.com/boa-dev/boa/pull/1079): Fix lexing escapes in string literal (@jevancc) +- [BUG #1075](https://github.com/boa-dev/boa/pull/1075): Fix out-of-range panics of `Date` (@jevancc) +- [BUG #1084](https://github.com/boa-dev/boa/pull/1084): Fix line terminator in string literal (@jevancc) +- [BUG #1110](https://github.com/boa-dev/boa/pull/1110): Fix parsing floats panics and bugs (@jevancc) +- [BUG #1202](https://github.com/boa-dev/boa/pull/1202): Fix a typo in `gc.rs` (@teymour-aldridge) +- [BUG #1201](https://github.com/boa-dev/boa/pull/1201): Return optional value in `to_json` functions (@fermian) +- [BUG #1223](https://github.com/boa-dev/boa/pull/1223): Update cli name in Readme (@sphinxc0re) +- [BUG #1175](https://github.com/boa-dev/boa/pull/1175): Handle early errors for declarations in `StatementList` (@0x7D2B) +- [BUG #1270](https://github.com/boa-dev/boa/pull/1270): Fix `Context::register_global_function()` (@HalidOdat) +- [BUG #1135](https://github.com/boa-dev/boa/pull/1135): Fix of instructions.rs comment, to_precision impl and rfc changes (@NathanRoyer) +- [BUG #1272](https://github.com/boa-dev/boa/pull/1272): Fix `Array.prototype.filter` (@tofpie & @Razican) +- [BUG #1280](https://github.com/boa-dev/boa/pull/1280): Fix slice index panic in `add_rest_param` (@0x7D2B) +- [BUG #1284](https://github.com/boa-dev/boa/pull/1284): Fix `GcObject` `to_json` mutable borrow panic (@0x7D2B) +- [BUG #1283](https://github.com/boa-dev/boa/pull/1283): Fix panic in regex execution (@0x7D2B) +- [BUG #1286](https://github.com/boa-dev/boa/pull/1286): Fix construct usage (@0x7D2B) +- [BUG #1288](https://github.com/boa-dev/boa/pull/1288): Fixed `Math.hypot.length` bug (@moadmmh) +- [BUG #1285](https://github.com/boa-dev/boa/pull/1285): Fix environment record panics (@0x7D2B) +- [BUG #1302](https://github.com/boa-dev/boa/pull/1302): Fix VM branch (@jasonwilliams) + +Internal Improvements: + +- [INTERNAL #1067](https://github.com/boa-dev/boa/pull/1067): Change `Realm::global_object` field from `Value` to `GcObject` (@RageKnify) +- [INTERNAL #1048](https://github.com/boa-dev/boa/pull/1048): VM Trace output fixes (@jasonwilliams) +- [INTERNAL #1109](https://github.com/boa-dev/boa/pull/1109): Define all property methods of constructors (@RageKnify) +- [INTERNAL #1126](https://github.com/boa-dev/boa/pull/1126): Remove unnecessary wraps for non built-in functions (@RageKnify) +- [INTERNAL #1044](https://github.com/boa-dev/boa/pull/1044): Removed duplicated code in `vm.run` using macros (@stephanemagnenat) +- [INTERNAL #1103](https://github.com/boa-dev/boa/pull/1103): Lazy evaluation for cooked template string (@jevancc) +- [INTERNAL #1156](https://github.com/boa-dev/boa/pull/1156): Rework environment records (@0x7D2B) +- [INTERNAL #1181](https://github.com/boa-dev/boa/pull/1181): Merge `Const`/`Let`/`Var` `DeclList` into `DeclarationList` (@0x7D2B) +- [INTERNAL #1234](https://github.com/boa-dev/boa/pull/1234): Separate `Symbol` builtin (@HalidOdat) +- [INTERNAL #1131](https://github.com/boa-dev/boa/pull/1131): Make environment methods take `&mut Context` (@HalidOdat) +- [INTERNAL #1271](https://github.com/boa-dev/boa/pull/1271): Make `same_value` and `same_value_zero` static methods (@HalidOdat) +- [INTERNAL #1276](https://github.com/boa-dev/boa/pull/1276): Cleanup (@Razican) +- [INTERNAL #1279](https://github.com/boa-dev/boa/pull/1279): Add test comparison to Test262 result compare (@Razican) +- [INTERNAL #1293](https://github.com/boa-dev/boa/pull/1293): Fix test262 comment formatting (@0x7D2B) +- [INTERNAL #1294](https://github.com/boa-dev/boa/pull/1294): Don't consider panic fixes as "new failures" (@Razican) + +# [0.11.0 (2021-01-14) - Faster Parsing & Better compliance](https://github.com/boa-dev/boa/compare/v0.10.0...v0.11.0) + +Feature Enhancements: + +- [FEATURE #836](https://github.com/boa-dev/boa/pull/836): + Async/Await parse (@Lan2u) +- [FEATURE #704](https://github.com/boa-dev/boa/pull/704): + Implement for...of loops (@joshwd36) +- [FEATURE #770](https://github.com/boa-dev/boa/pull/770): + Support for symbols as property keys for `Object.defineProperty` (@georgeroman) +- [FEATURE #717](https://github.com/boa-dev/boa/pull/717): + Strict Mode Lex/Parse (@Lan2u) +- [FEATURE #800](https://github.com/boa-dev/boa/pull/800): + Implement `console` crate feature - Put `console` object behind a feature flag (@HalidOdat) +- [FEATURE #804](https://github.com/boa-dev/boa/pull/804): + Implement `EvalError` (@HalidOdat) +- [FEATURE #805](https://github.com/boa-dev/boa/pull/805): + Implement `Function.prototype.call` (@RageKnify) +- [FEATURE #806](https://github.com/boa-dev/boa/pull/806): + Implement `URIError` (@HalidOdat) +- [FEATURE #811](https://github.com/boa-dev/boa/pull/811): + Implement spread operator using iterator (@croraf) +- [FEATURE #844](https://github.com/boa-dev/boa/pull/844): + Allow UnaryExpression with prefix increment/decrement (@croraf) +- [FEATURE #798](https://github.com/boa-dev/boa/pull/798): + Implement Object.getOwnPropertyDescriptor() and Object.getOwnPropertyDescriptors() (@JohnDoneth) +- [FEATURE #847](https://github.com/boa-dev/boa/pull/847): + Implement Map.prototype.entries() (@croraf) +- [FEATURE #859](https://github.com/boa-dev/boa/pull/859): + Implement spec compliant Array constructor (@georgeroman) +- [FEATURE #874](https://github.com/boa-dev/boa/pull/874): + Implement Map.prototype.values and Map.prototype.keys (@croraf) +- [FEATURE #877](https://github.com/boa-dev/boa/pull/877): + Implement Function.prototype.apply (@georgeroman) +- [FEATURE #908](https://github.com/boa-dev/boa/pull/908): + Implementation of `instanceof` operator (@morrien) +- [FEATURE #935](https://github.com/boa-dev/boa/pull/935): + Implement String.prototype.codePointAt (@devinus) +- [FEATURE #961](https://github.com/boa-dev/boa/pull/961): + Implement the optional `space` parameter in `JSON.stringify` (@tofpie) +- [FEATURE #962](https://github.com/boa-dev/boa/pull/962): + Implement Number.prototype.toPrecision (@NathanRoyer) +- [FEATURE #983](https://github.com/boa-dev/boa/pull/983): + Implement Object.prototype.isPrototypeOf (@tofpie) +- [FEATURE #995](https://github.com/boa-dev/boa/pull/995): + Support Numeric separators (@tofpie) +- [FEATURE #1013](https://github.com/boa-dev/boa/pull/1013): + Implement nullish coalescing (?? and ??=) (@tofpie) +- [FEATURE #987](https://github.com/boa-dev/boa/pull/987): + Implement property accessors (@tofpie) +- [FEATURE #1018](https://github.com/boa-dev/boa/pull/1018): + Implement logical assignment operators (&&= and ||=) (@tofpie) +- [FEATURE #1019](https://github.com/boa-dev/boa/pull/1019): + Implement early errors for non-assignable nodes in assignment (@tofpie) +- [FEATURE #1020](https://github.com/boa-dev/boa/pull/1020): + Implement Symbol.toPrimitive (@tofpie) +- [FEATURE #976](https://github.com/boa-dev/boa/pull/976): + Implement for..in (@tofpie) +- [FEATURE #1026](https://github.com/boa-dev/boa/pull/1026): + Implement String.prototype.split (@jevancc) +- [FEATURE #1047](https://github.com/boa-dev/boa/pull/1047): + Added syntax highlighting for numbers, identifiers and template literals (@Razican) +- [FEATURE #1003](https://github.com/boa-dev/boa/pull/1003): + Improve Unicode support for identifier names (@jevancc) + +Bug Fixes: + +- [BUG #782](https://github.com/boa-dev/boa/pull/782): + Throw TypeError if regexp is passed to startsWith, endsWith, includes (@pt2121) +- [BUG #788](https://github.com/boa-dev/boa/pull/788): + Fixing a duplicated attribute in test262 results (@Razican) +- [BUG #790](https://github.com/boa-dev/boa/pull/790): + Throw RangeError when BigInt division by zero occurs (@JohnDoneth) +- [BUG #785](https://github.com/boa-dev/boa/pull/785): + Fix zero argument panic in JSON.parse() (@JohnDoneth) +- [BUG #749](https://github.com/boa-dev/boa/pull/749): + Fix Error constructors to return rather than throw (@RageKnify) +- [BUG #777](https://github.com/boa-dev/boa/pull/777): + Fix cyclic JSON.stringify / primitive conversion stack overflows (@vgel) +- [BUG #799](https://github.com/boa-dev/boa/pull/799): + Fix lexer span panic with carriage return (@vgel) +- [BUG #812](https://github.com/boa-dev/boa/pull/812): + Fix 2 bugs that caused Test262 to fail (@RageKnify) +- [BUG #826](https://github.com/boa-dev/boa/pull/826): + Fix tokenizing Unicode escape sequence in string literal (@HalidOdat) +- [BUG #825](https://github.com/boa-dev/boa/pull/825): + calling "new" on a primitive value throw a type error (@dlemel8) +- [BUG #853](https://github.com/boa-dev/boa/pull/853) + Handle invalid Unicode code point in the string literals (@jevancc) +- [BUG #870](https://github.com/boa-dev/boa/pull/870) + Fix JSON stringification for fractional numbers (@georgeroman) +- [BUG #807](https://github.com/boa-dev/boa/pull/807): + Make boa::parse emit error on invalid input, not panic (@georgeroman) +- [BUG #880](https://github.com/boa-dev/boa/pull/880): + Support more number literals in BigInt's from string constructor (@georgeroman) +- [BUG #885](https://github.com/boa-dev/boa/pull/885): + Fix `BigInt.prototype.toString()` radix checks (@georgeroman) +- [BUG #882](https://github.com/boa-dev/boa/pull/882): + Fix (panic) remainder by zero (@georgeroman) +- [BUG #884](https://github.com/boa-dev/boa/pull/884): + Fix some panics related to BigInt operations (@georgeroman) +- [BUG #888](https://github.com/boa-dev/boa/pull/888): + Fix some panics in String.prototype properties (@georgeroman) +- [BUG #902](https://github.com/boa-dev/boa/pull/902): + Fix Accessors panics (@HalidOdat) +- [BUG #959](https://github.com/boa-dev/boa/pull/959): + Fix Unicode character escape sequence parsing (@tofpie) +- [BUG #964](https://github.com/boa-dev/boa/pull/964): + Fix single line comment lexing with CRLF line ending (@tofpie) +- [BUG #919](https://github.com/boa-dev/boa/pull/919): + Reduce the number of `Array`-related panics (@jakubfijalkowski) +- [BUG #968](https://github.com/boa-dev/boa/pull/968): + Fix unit tests that can be failed due to daylight saving time (@tofpie) +- [BUG #972](https://github.com/boa-dev/boa/pull/972): + Fix enumerable attribute on array length property (@tofpie) +- [BUG #974](https://github.com/boa-dev/boa/pull/974): + Fix enumerable attribute on string length property (@tofpie) +- [BUG #981](https://github.com/boa-dev/boa/pull/981): + Fix prototypes for Number, String and Boolean (@tofpie) +- [BUG #999](https://github.com/boa-dev/boa/pull/999): + Fix logical expressions evaluation (@tofpie) +- [BUG #1001](https://github.com/boa-dev/boa/pull/1001): + Fix comparison with infinity (@tofpie) +- [BUG #1004](https://github.com/boa-dev/boa/pull/1004): + Fix panics surrounding `Object.prototype.hasOwnProperty()` (@HalidOdat) +- [BUG #1005](https://github.com/boa-dev/boa/pull/1005): + Fix panics surrounding `Object.defineProperty()` (@HalidOdat) +- [BUG #1021](https://github.com/boa-dev/boa/pull/1021): + Fix spread in new and call expressions (@tofpie) +- [BUG #1023](https://github.com/boa-dev/boa/pull/1023): + Fix attributes on properties of functions and constructors (@tofpie) +- [BUG #1017](https://github.com/boa-dev/boa/pull/1017): + Don't panic when function parameters share names (@AnnikaCodes) +- [BUG #1024](https://github.com/boa-dev/boa/pull/1024): + Fix delete when the property is not configurable (@tofpie) +- [BUG #1027](https://github.com/boa-dev/boa/pull/1027): + Supress regress errors on invalid escapes for regex (@jasonwilliams +- [BUG #1031](https://github.com/boa-dev/boa/pull/1031): + Fixed some extra regex panics (@Razican) +- [BUG #1049](https://github.com/boa-dev/boa/pull/1049): + Support overriding the `arguments` variable (@AnnikaCodes) +- [BUG #1050](https://github.com/boa-dev/boa/pull/1050): + Remove panic on named capture groups (@Razican) +- [BUG #1046](https://github.com/boa-dev/boa/pull/1046): + Remove a few different panics (@Razican) +- [BUG #1051](https://github.com/boa-dev/boa/pull/1051): + Fix parsing of arrow functions with 1 argument (@Lan2u) +- [BUG #1045](https://github.com/boa-dev/boa/pull/1045): + Add newTarget to construct (@tofpie) +- [BUG #659](https://github.com/boa-dev/boa/pull/659): + Error handling in environment (@54k1) + +Internal Improvements: + +- [INTERNAL #735](https://github.com/boa-dev/boa/pull/735): + Move exec implementations together with AST node structs (@georgeroman) +- [INTERNAL #724](https://github.com/boa-dev/boa/pull/724): + Ignore tests for code coverage count (@HalidOdat) +- [INTERNAL #768](https://github.com/boa-dev/boa/pull/768) + Update the benchmark Github action (@Razican) +- [INTERNAL #722](https://github.com/boa-dev/boa/pull/722): + `ConstructorBuilder`, `ObjectInitializer`, cache standard objects and fix global object attributes (@HalidOdat) +- [INTERNAL #783](https://github.com/boa-dev/boa/pull/783): + New test262 results format (This also reduces the payload size for the website) (@Razican) +- [INTERNAL #787](https://github.com/boa-dev/boa/pull/787): + Refactor ast/node/expression into ast/node/call and ast/node/new (@croraf) +- [INTERNAL #802](https://github.com/boa-dev/boa/pull/802): + Make `Function.prototype` a function (@HalidOdat) +- [INTERNAL #746](https://github.com/boa-dev/boa/pull/746): + Add Object.defineProperties and handle props argument in Object.create (@dvtkrlbs) +- [INTERNAL #774](https://github.com/boa-dev/boa/pull/774): + Switch from `regex` to `regress` for ECMA spec-compliant regex implementation (@neeldug) +- [INTERNAL #794](https://github.com/boa-dev/boa/pull/794): + Refactor `PropertyDescriptor` (Improved performance) (@HalidOdat) +- [INTERNAL #824](https://github.com/boa-dev/boa/pull/824): + [parser Expression] minor expression macro simplification (@croraf) +- [INTERNAL #833](https://github.com/boa-dev/boa/pull/833): + Using unstable sort for sorting keys on `to_json()` for GC objects (@Razican) +- [INTERNAL #837](https://github.com/boa-dev/boa/pull/837): + Set default-run to `boa` removing need for `--bin` (@RageKnify) +- [INTERNAL #841](https://github.com/boa-dev/boa/pull/841): + Minor refactor and rename in eval() method (@croraf) +- [INTERNAL #840](https://github.com/boa-dev/boa/pull/840): + fix(profiler): update profiler to match current measureme api (@neeldug) +- [INTERNAL #838](https://github.com/boa-dev/boa/pull/838): + style(boa): minor cleanup (@neeldug) +- [INTERNAL #869](https://github.com/boa-dev/boa/pull/869): + Updated cache in workflows (@Razican) +- [INTERNAL #873](https://github.com/boa-dev/boa/pull/873) + Removed cache from MacOS builds (@Razican) +- [INTERNAL #835](https://github.com/boa-dev/boa/pull/835): + Move `Object` internal object methods to `GcObject` (@HalidOdat) +- [INTERNAL #886](https://github.com/boa-dev/boa/pull/886): + Support running a specific test/suite in boa_tester (@georgeroman) +- [INTERNAL #901](https://github.com/boa-dev/boa/pull/901): + Added "unimplemented" syntax errors (@Razican) +- [INTERNAL #911](https://github.com/boa-dev/boa/pull/911): + Change Symbol hash to `u64` (@HalidOdat) +- [INTERNAL #912](https://github.com/boa-dev/boa/pull/912): + Feature `Context::register_global_property()` (@HalidOdat) +- [INTERNAL #913](https://github.com/boa-dev/boa/pull/913): + Added check to ignore semicolon in parser (@AngelOnFira) +- [INTERNAL #915](https://github.com/boa-dev/boa/pull/915): + Improve lexer by make cursor iterate over bytes (@jevancc) +- [INTERNAL #952](https://github.com/boa-dev/boa/pull/952): + Upgraded rustyline and test262 (@Razican) +- [INTERNAL #960](https://github.com/boa-dev/boa/pull/960): + Fix unresolved links in documentation (@tofpie) +- [INTERNAL #979](https://github.com/boa-dev/boa/pull/979): + Read file input in bytes instead of string (@tofpie) +- [INTERNAL #1014](https://github.com/boa-dev/boa/pull/1014): + StatementList: Rename `statements` to `items` (@AnnikaCodes) +- [INTERNAL #860](https://github.com/boa-dev/boa/pull/860): + Investigation into ByteCode Interpreter (@jasonwilliams) +- [INTERNAL #1042](https://github.com/boa-dev/boa/pull/1042): + Add receiver parameter to object internal methods (@tofpie) +- [INTERNAL #1030](https://github.com/boa-dev/boa/pull/1030): + VM: Implement variable declaration (var, const, and let) (@AnnikaCodes) +- [INTERNAL #1010](https://github.com/boa-dev/boa/pull/1010): + Modify environment binding behaviour of function (@54k1) + +# [0.10.0 (2020-09-29) - New Lexer & Test 262 Harness](https://github.com/boa-dev/boa/compare/v0.9.0...v0.10.0) + +Feature Enhancements: + +- [FEATURE #524](https://github.com/boa-dev/boa/pull/525): + Implement remaining `Math` methods (@mr-rodgers) +- [FEATURE #562](https://github.com/boa-dev/boa/pull/562): + Implement remaining `Number` methods (@joshwd36) +- [FEATURE #536](https://github.com/boa-dev/boa/pull/536): + Implement `SyntaxError` (@HalidOdat) +- [FEATURE #543](https://github.com/boa-dev/boa/pull/543): + Implements `Object.create` builtin method (@croraf) +- [FEATURE #492](https://github.com/boa-dev/boa/pull/492): + Switch to [rustyline](https://github.com/kkawakam/rustyline) for the CLI (@IovoslavIovchev & @Razican) +- [FEATURE #595](https://github.com/boa-dev/boa/pull/595): + Added syntax highlighting for strings in REPL (@HalidOdat) +- [FEATURE #586](https://github.com/boa-dev/boa/pull/586): + Better error formatting and cli color (@HalidOdat) +- [FEATURE #590](https://github.com/boa-dev/boa/pull/590): + Added keyword and operator colors and matching bracket validator to REPL (@HalidOdat) +- [FEATURE #555](https://github.com/boa-dev/boa/pull/555): + Implement Array.prototype.reduce (@benjaminflin) +- [FEATURE #550](https://github.com/boa-dev/boa/pull/550): + Initial implementation of Map() (@joshwd36 & @HalidOdat) +- [FEATURE #579](https://github.com/boa-dev/boa/pull/579): + Implement Array.prototype.reduceRight (@benjaminflin) +- [FEATURE #585](https://github.com/boa-dev/boa/pull/587): + Implement Well-Known Symbols (@joshwd36) +- [FEATURE #589](https://github.com/boa-dev/boa/pull/589): + Implement the comma operator (@KashParty) +- [FEATURE #341](https://github.com/boa-dev/boa/pull/590): + Ability to create multiline blocks in boa shell (@HalidOdat) +- [FEATURE #252](https://github.com/boa-dev/boa/pull/596): + Implement `Date` (@jcdickinson) +- [FEATURE #711](https://github.com/boa-dev/boa/pull/711): + Add support for >>>= (@arpit-saxena) +- [FEATURE #549](https://github.com/boa-dev/boa/pull/549): + Implement label statements (@jasonwilliams) +- [FEATURE #373](https://github.com/boa-dev/boa/pull/373): + Introduce PropertyKey for field acces (@RageKnify) +- [FEATURE #627](https://github.com/boa-dev/boa/pull/627): + Feature native class objects (`NativeObject` and `Class` traits) (@HalidOdat) +- [FEATURE #694](https://github.com/boa-dev/boa/pull/694): + Feature `gc` module (@HalidOdat) +- [FEATURE #656](https://github.com/boa-dev/boa/pull/656): + Feature `Context` (@HalidOdat) +- [FEATURE #673](https://github.com/boa-dev/boa/pull/673): + Add `#[track_caller]` to `GcObject` methods that can panic (@HalidOdat) +- [FEATURE #661](https://github.com/boa-dev/boa/pull/661): + Add documentation to `GcObject` methods (@HalidOdat) +- [FEATURE #662](https://github.com/boa-dev/boa/pull/662): + Implement `std::error::Error` for `GcObject` borrow errors (@HalidOdat) +- [FEATURE #660](https://github.com/boa-dev/boa/pull/660): + Make `GcObject::contruct` not take 'this' (@HalidOdat) +- [FEATURE #654](https://github.com/boa-dev/boa/pull/654): + Move `require_object_coercible` to `Value` (@HalidOdat) +- [FEATURE #603](https://github.com/boa-dev/boa/pull/603): + Index `PropertyKey`, `Object` iterators and symbol support (@HalidOdat) +- [FEATURE #637](https://github.com/boa-dev/boa/pull/637): + Feature `boa::Result` (@HalidOdat) +- [FEATURE #625](https://github.com/boa-dev/boa/pull/625): + Moved value operations from `Interpreter` to `Value` (@HalidOdat) +- [FEATURE #638](https://github.com/boa-dev/boa/pull/638): + Changed to `Value::to_*int32` => `Value::to_*32` (@HalidOdat) + +Bug Fixes: + +- [BUG #405](https://github.com/boa-dev/boa/issues/405): + Fix json.stringify symbol handling (@n14little) +- [BUG #520](https://github.com/boa-dev/boa/pull/520): + Fix all `Value` operations and add unsigned shift right (@HalidOdat) +- [BUG #529](https://github.com/boa-dev/boa/pull/529): + Refactor exec/expression into exec/call and exec/new (@croraf) +- [BUG #510](https://github.com/boa-dev/boa/issues/510): + [[Call]] calling an undefined method does not throw (@joshwd36) +- [BUG #493](https://github.com/boa-dev/boa/pull/493): + Use correct exponential representation for rational values (@Tropid) +- [BUG #572](https://github.com/boa-dev/boa/pull/572): + Spec Compliant `Number.prototype.toString()`, better `Number` object formating and `-0` (@HalidOdat) +- [BUG #599](https://github.com/boa-dev/boa/pull/599): + Fixed `String.prototype.indexOf()` bug, when the search string is empty (@HalidOdat) +- [BUG #615](https://github.com/boa-dev/boa/issues/615): + Fix abstract relational comparison operators (@HalidOdat) +- [BUG #608](https://github.com/boa-dev/boa/issues/608): + `Debug::fmt` Causes Causes a Stack Overflow (@jcdickinson) +- [BUG #532](https://github.com/boa-dev/boa/issues/532) + [builtins - Object] Object.getPrototypeOf returning incorrectly (@54k1) +- [BUG #533](https://github.com/boa-dev/boa/issues/533) + [exec - function] function.prototype doesn't have own constructor property pointing to this function (@54k1) +- [BUG #641](https://github.com/boa-dev/boa/issues/641) + Test new_instance_should_point_to_prototype is not checked correctly (@54k1) +- [BUG #644](https://github.com/boa-dev/boa/pull/645) + `undefined` constants panic on execution (@jcdickinson) +- [BUG #631](https://github.com/boa-dev/boa/pull/645): + Unexpected result when applying typeof to undefined value (@jcdickinson) +- [BUG #667](https://github.com/boa-dev/boa/pull/667): + Fix panic when calling function that mutates itself (@dvtkrlbs) +- [BUG #668](https://github.com/boa-dev/boa/pull/668): + Fix clippy on Nightly (@dvtkrlbs) +- [BUG #582](https://github.com/boa-dev/boa/pull/582): + Make `String.prototype.repeat()` ECMAScript specification compliant (@HalidOdat) +- [BUG #541](https://github.com/boa-dev/boa/pull/541): + Made all `Math` methods spec compliant (@HalidOdat) +- [BUG #597](https://github.com/boa-dev/boa/pull/597): + Made `String.prototype.indexOf` spec compliant. (@HalidOdat) +- [BUG #598](https://github.com/boa-dev/boa/pull/598): + Made `String.prototype.lastIndexOf()` spec compliant (@HalidOdat) +- [BUG #583](https://github.com/boa-dev/boa/pull/583): + Fix string prototype `trim` methods (@HalidOdat) +- [BUG #728](https://github.com/boa-dev/boa/pull/728): + Fix bug when setting the length on String objects (@jasonwilliams) +- [BUG #710](https://github.com/boa-dev/boa/pull/710): + Fix panic when a self mutating function is constructing an object (@HalidOdat) +- [BUG #699](https://github.com/boa-dev/boa/pull/699): + Fix `Value::to_json` order of items in array (@sele9) +- [BUG #610](https://github.com/boa-dev/boa/pull/610): + Fix: `String.prototype.replace` substitutions (@RageKnify) +- [BUG #645](https://github.com/boa-dev/boa/pull/645): + Fix undefined constant expression evaluation (@jcdickinson) +- [BUG #643](https://github.com/boa-dev/boa/pull/643): + Change default return type from null to undefined (@54k1) +- [BUG #642](https://github.com/boa-dev/boa/pull/642): + Missing `constructor` field in ordinary functions (@54k1) +- [BUG #604](https://github.com/boa-dev/boa/pull/604): + Missing `__proto__` field in functions instances (@54k1) +- [BUG #561](https://github.com/boa-dev/boa/pull/561): + Throw a `TypeError` when a non-object is called (@joshwd36) +- [BUG #748](https://github.com/boa-dev/boa/pull/748): + Fix parse error throwing a `TypeError`, instead of `SyntaxError` (@iamsaquib8) +- [BUG #737](https://github.com/boa-dev/boa/pull/737): + Make `Object.toString()` spec compliant (@RageKnify) + +Internal Improvements: + +- [INTERNAL #567](https://github.com/boa-dev/boa/pull/567): + Add ECMAScript test suite (test262) (@Razican) +- [INTERNAL #559](https://github.com/boa-dev/boa/pull/559): + New Lexer (@Lan2u @HalidOdat @Razican) +- [INTERNAL #712](https://github.com/boa-dev/boa/pull/712): + Refactor: `Value::to_object` to return `GcObject` (@RageKnify) +- [INTERNAL #544](https://github.com/boa-dev/boa/pull/544): + Removed `console`s dependency of `InternalState` (@HalidOdat) +- [INTERNAL #556](https://github.com/boa-dev/boa/pull/556): + Added benchmark for goal symbol switching (@Razican) +- [INTERNAL #578](https://github.com/boa-dev/boa/pull/580): + Extract `prototype` from internal slots (@HalidOdat) +- [INTERNAL #553](https://github.com/boa-dev/boa/pull/553): + Refactor Property Descriptor flags (@HalidOdat) +- [INTERNAL #592](https://github.com/boa-dev/boa/pull/592): + `RegExp` specialization (@HalidOdat) +- [INTERNAL #626](https://github.com/boa-dev/boa/pull/626): + Refactor `Function` (@HalidOdat @Razican) +- [INTERNAL #564](https://github.com/boa-dev/boa/pull/581): + Add benchmarks for "uglified" JS (@neeldug) +- [INTERNAL #706](https://github.com/boa-dev/boa/pull/706): + Cache well known symbols (@HalidOdat) +- [INTERNAL #723](https://github.com/boa-dev/boa/pull/723): + Add fast path for string concatenation (@RageKnify) +- [INTERNAL #689](https://github.com/boa-dev/boa/pull/689): + Move `object` module to root (@HalidOdat) +- [INTERNAL #684](https://github.com/boa-dev/boa/pull/684): + Move `property` module to root (@HalidOdat) +- [INTERNAL #674](https://github.com/boa-dev/boa/pull/674): + Move `value` module to root (@HalidOdat) +- [INTERNAL #693](https://github.com/boa-dev/boa/pull/693): + Rename `Object::prototype()` and `Object::set_prototype()` (@RageKnify) +- [INTERNAL #665](https://github.com/boa-dev/boa/pull/665): + `approx_eq!` macro for `expm1` tests. (@neeldung) +- [INTERNAL #581](https://github.com/boa-dev/boa/pull/581): + Added CLEAN_JS and MINI_JS benches (@neeldung) +- [INTERNAL #640](https://github.com/boa-dev/boa/pull/640): + Benchmark refactor (@neeldung) +- [INTERNAL #635](https://github.com/boa-dev/boa/pull/635): + Add missing ops to exec module (@jarredholman) +- [INTERNAL #616](https://github.com/boa-dev/boa/pull/616): + Remove `Value::as_num_to_power()` (@HalidOdat) +- [INTERNAL #601](https://github.com/boa-dev/boa/pull/601): + Removed internal_slots from object (@HalidOdat) +- [INTERNAL #560](https://github.com/boa-dev/boa/pull/560): + Added benchmarks for full program execution (@Razican) +- [INTERNAL #547](https://github.com/boa-dev/boa/pull/547): + Merged `create` into `init` for builtins (@HalidOdat) +- [INTERNAL #538](https://github.com/boa-dev/boa/pull/538): + Cleanup and added test for `String.prototype.concat` (@HalidOdat) +- [INTERNAL #739](https://github.com/boa-dev/boa/pull/739): + Add release action (@jasonwilliams) +- [INTERNAL #744](https://github.com/boa-dev/boa/pull/744): + Add MacOS check and test to CI (@neeldug) + +# [# 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) + +Feature Enhancements: + +- [FEATURE #414](https://github.com/boa-dev/boa/issues/414): + Implement `Number` object constants (@Lan2u) (@HalidOdat) +- [FEATURE #345](https://github.com/boa-dev/boa/issues/345): + Implement the optional `replacer` parameter in `JSON.stringify( value[, replacer [, space] ] )` (@n14little) +- [FEATURE #480](https://github.com/boa-dev/boa/issues/480): + Implement global `Infinity` property (@AnirudhKonduru) +- [FEATURE #410](https://github.com/boa-dev/boa/pull/410): + Add support for the reviver function to JSON.parse (@abhijeetbhagat) +- [FEATURE #425](https://github.com/boa-dev/boa/pull/425): + Specification compliant `ToString` (`to_string`) (@HalidOdat) +- [FEATURE #442](https://github.com/boa-dev/boa/pull/442): + Added `TypeError` implementation (@HalidOdat) +- [FEATURE #450](https://github.com/boa-dev/boa/pull/450): + Specification compliant `ToBigInt` (`to_bigint`) (@HalidOdat) +- [FEATURE #455](https://github.com/boa-dev/boa/pull/455): + TemplateLiteral Basic lexer implementation (@croraf) +- [FEATURE #447](https://github.com/boa-dev/boa/issues/447): + parseInt, parseFloat implementation (@Lan2u) +- [FEATURE #468](https://github.com/boa-dev/boa/pull/468): + Add BigInt.asIntN() and BigInt.asUintN() functions (@Tropid) +- [FEATURE #428](https://github.com/boa-dev/boa/issues/428): + [Feature Request] - Create benchmark for Array manipulation (@abhijeetbhagat) +- [FEATURE #439](https://github.com/boa-dev/boa/issues/439): + Implement break handling in switch statements (@Lan2u) +- [FEATURE #301](https://github.com/boa-dev/boa/issues/301): + Implementing the switch statement in the new parser (@Lan2u) +- [FEATURE #120](https://github.com/boa-dev/boa/issues/120): + Implement `globalThis` (@zanayr) +- [FEATURE #513](https://github.com/boa-dev/boa/issues/513): + Implement `Object.is()` method (@tylermorten) +- [FEATURE #481](https://github.com/boa-dev/boa/issues/481): + Implement global `undefined` property (@croraf) + +Bug Fixes: + +- [BUG #412](https://github.com/boa-dev/boa/pull/412): + Fixed parsing if statement without else block preceded by a newline (@HalidOdat) +- [BUG #409](https://github.com/boa-dev/boa/pull/409): + Fix function object constructable/callable (@HalidOdat) +- [BUG #403](https://github.com/boa-dev/boa/issues/403) + `Value::to_json()` does not handle `undefined` correctly (@n14little) +- [BUG #443](https://github.com/boa-dev/boa/issues/443): + HasOwnProperty should call GetOwnProperty and not GetProperty (@n14little) +- [BUG #210](https://github.com/boa-dev/boa/issues/210): + builtinfun.length undefined (@Croraf) +- [BUG #466](https://github.com/boa-dev/boa/issues/466): + Change `ToPrimitive()` (`to_primitive()`) hint to be an enum, instead of string (@HalidOdat) +- [BUG #421](https://github.com/boa-dev/boa/issues/421): + `NaN` is lexed as a number, not as an identifier (@croraf) +- [BUG #454](https://github.com/boa-dev/boa/issues/454): + Function declaration returns the function, it should return `undefined` (@croraf) +- [BUG #482](https://github.com/boa-dev/boa/issues/482): + Field access should propagate the exception (`Err(_)`) (@neeldug) +- [BUG #463](https://github.com/boa-dev/boa/issues/463): + Use of undefined variable should throw an error (@croraf) +- [BUG #502](https://github.com/boa-dev/boa/pull/502): + Fixed global objects initialization order (@HalidOdat) +- [BUG #509](https://github.com/boa-dev/boa/issues/509): + JSON.stringify(undefined) panics (@n14little) +- [BUG #514](https://github.com/boa-dev/boa/issues/514): + Clean up `Math` Methods (@n14little) +- [BUG #511](https://github.com/boa-dev/boa/issues/511): + [Call] Usage of "this" in methods is not supported (@jasonwilliams) + +Internal Improvements + +- [INTERNAL #435](https://github.com/boa-dev/boa/issues/435): + Optimize type comparisons (@Lan2u) +- [INTERNAL #296](https://github.com/boa-dev/boa/issues/296): + using measureme for profiling the interpreter (@jasonwilliams) +- [INTERNAL #419](https://github.com/boa-dev/boa/pull/419): + Object specialization (fast paths for many objects) (@HalidOdat) +- [INTERNAL #392](https://github.com/boa-dev/boa/pull/392): + Execution and Node modulization (@Razican) +- [INTERNAL #465](https://github.com/boa-dev/boa/issues/465): + Refactoring Value (decouple `Gc` from `Value`) (@HalidOdat) +- [INTERNAL #416](https://github.com/boa-dev/boa/pull/416) & [INTERNAL #423](https://github.com/boa-dev/boa/commit/c8218dd91ef3181e048e7a2659a4fbf8d53c7174): + Update links to boa-dev (@pedropaulosuzuki) +- [INTERNAL #378](https://github.com/boa-dev/boa/issues/378): + Code Coverage! (@Lan2u) +- [INTERNAL #431](https://github.com/boa-dev/boa/pull/431): + Updates to PR Benchmarks (@Razican) +- [INTERNAL #427 #429 #430](https://github.com/boa-dev/boa/commit/64dbf13afd15f12f958daa87a3d236dc9af1a9aa): + Added new benchmarks (@Razican) + +# [# 0.8.0 (2020-05-23) - BigInt, Modularized Parser, Faster Hashing](https://github.com/boa-dev/boa/compare/v0.7.0...v0.8.0) + +`v0.8.0` brings more language implementations, such as do..while, function objects and also more recent EcmaScript additions, like BigInt. +We have now moved the Web Assembly build into the `wasm` package, plus added a code of conduct for those contributing. + +The parser has been even more modularized in this release making it easier to add new parsing rules. + +Boa 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). + +Feature Enhancements: + +- [FEATURE #121](https://github.com/boa-dev/boa/issues/121): + `BigInt` Implemented (@HalidOdat) +- [FEATURE #293](https://github.com/boa-dev/boa/pull/293): + Improved documentation of all modules (@HalidOdat) +- [FEATURE #302](https://github.com/boa-dev/boa/issues/302): + Implement do..while loop (@ptasz3k) +- [FEATURE #318](https://github.com/boa-dev/boa/pull/318): + Added continous integration for windows (@HalidOdat) +- [FEATURE #290](https://github.com/boa-dev/boa/pull/290): + Added more build profiles (@Razican) +- [FEATURE #323](https://github.com/boa-dev/boa/pull/323): + Aded more benchmarks (@Razican) +- [FEATURE #326](https://github.com/boa-dev/boa/pull/326): + Rename Boa CLI (@sphinxc0re) +- [FEATURE #312](https://github.com/boa-dev/boa/pull/312): + Added jemallocator for linux targets (@Razican) +- [FEATURE #339](https://github.com/boa-dev/boa/pull/339): + Improved Method parsing (@muskuloes) +- [FEATURE #352](https://github.com/boa-dev/boa/pull/352): + create boa-wasm package (@muskuloes) +- [FEATURE #304](https://github.com/boa-dev/boa/pull/304): + Modularized parser +- [FEATURE #141](https://github.com/boa-dev/boa/issues/141): + Implement function objects (@jasonwilliams) +- [FEATURE #365](https://github.com/boa-dev/boa/issues/365): + Implement for loop execution (@Razican) +- [FEATURE #356](https://github.com/boa-dev/boa/issues/356): + Use Fx Hash to speed up hash maps in the compiler (@Razican) +- [FEATURE #321](https://github.com/boa-dev/boa/issues/321): + Implement unary operator execution (@akryvomaz) +- [FEATURE #379](https://github.com/boa-dev/boa/issues/379): + Automatic auditing of Boa (@n14little) +- [FEATURE #264](https://github.com/boa-dev/boa/issues/264): + Implement `this` (@jasonwilliams) +- [FEATURE #395](https://github.com/boa-dev/boa/pull/395): + impl abstract-equality-comparison (@hello2dj) +- [FEATURE #359](https://github.com/boa-dev/boa/issues/359): + impl typeof (@RestitutorOrbis) +- [FEATURE #390](https://github.com/boa-dev/boa/pull/390): + Modularize try statement parsing (@abhijeetbhagat) + +Bug fixes: + +- [BUG #308](https://github.com/boa-dev/boa/issues/308): + Assignment operator not working in tests (a = a +1) (@ptasz3k) +- [BUG #322](https://github.com/boa-dev/boa/issues/322): + Benchmarks are failing in master (@Razican) +- [BUG #325](https://github.com/boa-dev/boa/pull/325): + Put JSON functions on the object, not the prototype (@coolreader18) +- [BUG #331](https://github.com/boa-dev/boa/issues/331): + We only get `Const::Num`, never `Const::Int` (@HalidOdat) +- [BUG #209](https://github.com/boa-dev/boa/issues/209): + Calling `new Array` with 1 argument doesn't work properly (@HalidOdat) +- [BUG #266](https://github.com/boa-dev/boa/issues/266): + Panic assigning named function to variable (@Razican) +- [BUG #397](https://github.com/boa-dev/boa/pull/397): + fix `NaN` is lexed as identifier, not as a number (@attliaLin) +- [BUG #362](https://github.com/boa-dev/boa/pull/362): + Remove Monaco Editor Webpack Plugin and Manually Vendor Editor Workers (@subhankar-panda) +- [BUG #406](https://github.com/boa-dev/boa/pull/406): + Dependency Upgrade (@Razican) +- [BUG #407](https://github.com/boa-dev/boa/pull/407): + `String()` wasn't defaulting to empty string on call (@jasonwilliams) +- [BUG #404](https://github.com/boa-dev/boa/pull/404): + Fix for 0 length new String(@tylermorten) + +Code Of Conduct: + +- [COC #384](https://github.com/boa-dev/boa/pull/384): + Code of conduct added (@Razican) + +Security: + +- [SEC #391](https://github.com/boa-dev/boa/pull/391): + run security audit daily at midnight. (@n14little) + +# [# 0.7.0 (2020-04-13) - New Parser is 67% faster](https://github.com/boa-dev/boa/compare/v0.6.0...v0.7.0) + +`v0.7.0` brings a REPL, Improved parser messages and a new parser! +This is now the default behaviour of Boa, so running Boa without a file argument will bring you into a javascript shell. +Tests have also been moved to their own files, we had a lot of tests in some modules so it was time to separate. + +## New Parser + +Most 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). + +The 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.\ +The 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. + +This only worked so far, eventually debugging parsing problems were difficult, also more bugs were being raised against the parser which couldn't be fixed. + +We 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 + +The 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. + +Once 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.\ +This works because the tokens live just as long as the parser operations do, so we don't need to copy the tokens.\ +What this brings is a huge performance boost, the parser is 67% faster than before! + +![Parser Improvement](./docs/img/parser-graph.png) + +Feature enhancements: + +- [FEATURE #281](https://github.com/boa-dev/boa/pull/281): + Rebuild the parser (@jasonwilliams, @Razican, @HalidOdat) +- [FEATURE #278](https://github.com/boa-dev/boa/pull/278): + Added the ability to dump the token stream or ast in bin. (@HalidOdat) +- [FEATURE #253](https://github.com/boa-dev/boa/pull/253): + Implement Array.isArray (@cisen) +- [FEATURE](https://github.com/boa-dev/boa/commit/edab5ca6cc10d13265f82fa4bc05d6b432a362fc) + Switch to normal output instead of debugged output (stdout/stdout) (@jasonwilliams) +- [FEATURE #258](https://github.com/boa-dev/boa/pull/258): + Moved test modules to their own files (@Razican) +- [FEATURE #267](https://github.com/boa-dev/boa/pull/267): + Add print & REPL functionality to CLI (@JohnDoneth) +- [FEATURE #268](https://github.com/boa-dev/boa/pull/268): + Addition of forEach() (@jasonwilliams) (@xSke) +- [FEATURE #262](https://github.com/boa-dev/boa/pull/262): + Implement Array.prototype.filter (@Nickforall) +- [FEATURE #261](https://github.com/boa-dev/boa/pull/261): + Improved parser error messages (@Razican) +- [FEATURE #277](https://github.com/boa-dev/boa/pull/277): + Add a logo to the project (@HalidOdat) +- [FEATURE #260](https://github.com/boa-dev/boa/pull/260): + Add methods with f64 std equivelant to Math object (@Nickforall) + +Bug fixes: + +- [BUG #249](https://github.com/boa-dev/boa/pull/249): + fix(parser): handle trailing comma in object literals (@gomesalexandre) +- [BUG #244](https://github.com/boa-dev/boa/pull/244): + Fixed more Lexer Panics (@adumbidiot) +- [BUG #256](https://github.com/boa-dev/boa/pull/256): + Fixed comments lexing (@Razican) +- [BUG #251](https://github.com/boa-dev/boa/issues/251): + Fixed empty returns (@Razican) +- [BUG #272](https://github.com/boa-dev/boa/pull/272): + Fix parsing of floats that start with a zero (@Nickforall) +- [BUG #240](https://github.com/boa-dev/boa/issues/240): + Fix parser panic +- [BUG #273](https://github.com/boa-dev/boa/issues/273): + new Class().method() has incorrect precedence + +Documentation Updates: + +- [DOC #297](https://github.com/boa-dev/boa/pull/297): + Better user contributed documentation + +# [# 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) + +The lexer has had several fixes in this release, including how it parses numbers, scientific notation should be improved. +On 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. + +## Moving to a workspace architecture + +Boa offers both a CLI and a library, initially these were all in the same binary. The downside is +those who want to embed boa as-is end up with all of the command-line dependencies. +So the time has come to separate out the two, this is normal procedure, this should be analogous to ripgrep +and the regex crate. +Cargo has great support for workspaces, so this shouldn't be an issue. + +## Benchmarks + +We now have [benchmarks which run against master](https://boa-dev.github.io/boa/dev/bench)! +Thanks to Github Actions these will run automatically a commit is merged. + +Feature enhancements: + +- [FEATURE #218](https://github.com/boa-dev/boa/pull/218): + Implement Array.prototype.toString (@cisen) +- [FEATURE #216](https://github.com/boa-dev/boa/commit/85e9a3526105a600358bd53811e2b022987c6fc8): + Keep accepting new array elements after spread. +- [FEATURE #220](https://github.com/boa-dev/boa/pull/220): + Documentation updates. (@croraf) +- [FEATURE #226](https://github.com/boa-dev/boa/pull/226): + add parser benchmark for expressions. (@jasonwilliams) +- [FEATURE #217](https://github.com/boa-dev/boa/pull/217): + String.prototype.replace() implemented +- [FEATURE #247](https://github.com/boa-dev/boa/pull/247): + Moved to a workspace architecture (@Razican) + +Bug fixes: + +- [BUG #222](https://github.com/boa-dev/boa/pull/222): + Fixed clippy errors (@IovoslavIovchev) +- [BUG #228](https://github.com/boa-dev/boa/pull/228): + [lexer: single-line-comment] Fix bug when single line comment is last line of file (@croraf) +- [BUG #229](https://github.com/boa-dev/boa/pull/229): + Replace error throwing with panic in "Lexer::next()" (@croraf) +- [BUG #232/BUG #238](https://github.com/boa-dev/boa/pull/232): + Clippy checking has been scaled right back to just Perf and Style (@jasonwilliams) +- [BUG #227](https://github.com/boa-dev/boa/pull/227): + Array.prototype.toString should be called by ES value (@cisen) +- [BUG #242](https://github.com/boa-dev/boa/pull/242): + Fixed some panics in the lexer (@adumbidiot) +- [BUG #235](https://github.com/boa-dev/boa/pull/235): + Fixed arithmetic operations with no space (@gomesalexandre) +- [BUG #245](https://github.com/boa-dev/boa/pull/245): + Fixed parsing of floats with scientific notation (@adumbidiot) + +# [# 0.5.1 (2019-12-02) - Rest / Spread (almost)](https://github.com/boa-dev/boa/compare/v0.5.0...v0.5.1) + +Feature enhancements: + +- [FEATURE #151](https://github.com/boa-dev/boa/issues/151): + Implement the Rest/Spread operator (functions and arrays). +- [FEATURE #193](https://github.com/boa-dev/boa/issues/193): + Implement macro for setting builtin functions +- [FEATURE #211](https://github.com/boa-dev/boa/pull/211): + Better Display support for all Objects (pretty printing) + +# [# 0.5.0 (2019-11-06) - Hacktoberfest Release](https://github.com/boa-dev/boa/compare/v0.4.0...v0.5.1) + +Feature enhancements: + +- [FEATURE #119](https://github.com/boa-dev/boa/issues/119): + Introduce realm struct to hold realm context and global object. +- [FEATURE #89](https://github.com/boa-dev/boa/issues/89): + Implement exponentiation operator. Thanks @arbroween +- [FEATURE #47](https://github.com/boa-dev/boa/issues/47): + Add tests for comments in source code. Thanks @Emanon42 +- [FEATURE #137](https://github.com/boa-dev/boa/issues/137): + Use Monaco theme for the demo page +- [FEATURE #114](https://github.com/boa-dev/boa/issues/114): + String.match(regExp) is implemented (@muskuloes) +- [FEATURE #115](https://github.com/boa-dev/boa/issues/115): + String.matchAll(regExp) is implemented (@bojan88) +- [FEATURE #163](https://github.com/boa-dev/boa/issues/163): + Implement Array.prototype.every() (@letmutx) +- [FEATURE #165](https://github.com/boa-dev/boa/issues/165): + Implement Array.prototype.find() (@letmutx) +- [FEATURE #166](https://github.com/boa-dev/boa/issues/166): + Implement Array.prototype.findIndex() (@felipe-fg) +- [FEATURE #39](https://github.com/boa-dev/boa/issues/39): + Implement block scoped variable declarations (@barskern) +- [FEATURE #161](https://github.com/boa-dev/boa/pull/161): + Enable obj[key] = value syntax. +- [FEATURE #179](https://github.com/boa-dev/boa/issues/179): + Implement the Tilde operator (@letmutx) +- [FEATURE #189](https://github.com/boa-dev/boa/pull/189): + Implement Array.prototype.includes (incl tests) (@simonbrahan) +- [FEATURE #180](https://github.com/boa-dev/boa/pull/180): + Implement Array.prototype.slice (@muskuloes @letmutx) +- [FEATURE #152](https://github.com/boa-dev/boa/issues/152): + Short Function syntax (no arguments) +- [FEATURE #164](https://github.com/boa-dev/boa/issues/164): + Implement Array.prototype.fill() (@bojan88) +- Array tests: Tests implemented for shift, unshift and reverse, pop and push (@muskuloes) +- Demo page has been improved, new font plus change on input. Thanks @WofWca +- [FEATURE #182](https://github.com/boa-dev/boa/pull/182): + Implement some Number prototype methods (incl tests) (@pop) +- [FEATURE #34](https://github.com/boa-dev/boa/issues/34): + Number object and Constructore are implemented (including methods) (@pop) +- [FEATURE #194](https://github.com/boa-dev/boa/pull/194): + Array.prototype.map (@IovoslavIovchev) +- [FEATURE #90](https://github.com/boa-dev/boa/issues/90): + Symbol Implementation (@jasonwilliams) + +Bug fixes: + +- [BUG #113](https://github.com/boa-dev/boa/issues/113): + Unassigned variables have default of undefined (@pop) +- [BUG #61](https://github.com/boa-dev/boa/issues/61): + Clippy warnings/errors fixed (@korpen) +- [BUG #147](https://github.com/boa-dev/boa/pull/147): + Updated object global +- [BUG #154](https://github.com/boa-dev/boa/issues/154): + Correctly handle all whitespaces within the lexer +- Tidy up Globals being added to Global Object. Thanks @DomParfitt + +# 0.4.0 (2019-09-25) + +v0.4.0 brings quite a big release. The biggest feature to land is the support of regular expressions. +Functions now have the arguments object supported and we have a [`debugging`](docs/debugging.md) section in the docs. + +Feature enhancements: + +- [FEATURE #6](https://github.com/boa-dev/boa/issues/6): + Support for regex literals. (Big thanks @999eagle) +- [FEATURE #13](https://github.com/boa-dev/boa/issues/13): + toLowerCase, toUpperCase, substring, substr and valueOf implemented (thanks @arbroween) +- Support for `arguments` object within functions +- `StringData` instead of `PrimitieData` to match spec +- Native function signatures changed, operations added to match spec +- Primitives can now be boxed/unboxed when methods are ran on them +- Spelling edits (thanks @someguynamedmatt) +- Ability to set global values before interpreter starts (thanks @999eagle) +- Assign operators implemented (thanks @oll3) +- + +Bug fixes: + +- [BUG #57](https://github.com/boa-dev/boa/issues/57): + Fixed issue with stackoverflow by implementing early returns. +- Allow to re-assign value to an existing binding. (Thanks @oll3) + +# 0.3.0 (2019-07-26) + +- UnexpectedKeyword(Else) bug fixed https://github.com/boa-dev/boa/issues/38 +- Contributing guide added +- Ability to specify file - Thanks @callumquick +- Travis fixes +- Parser Tests - Thanks @Razican +- Migrate to dyn traits - Thanks @Atul9 +- Added implementations for Array.prototype: concat(), push(), pop() and join() - Thanks @callumquick +- Some clippy Issues fixed - Thanks @Razican +- Objects have been refactored to use structs which are more closely aligned with the specification +- Benchmarks have been added +- String and Array specific console.log formats - Thanks @callumquick +- isPropertyKey implementation added - Thanks @KrisChambers +- Unit Tests for Array and Strings - Thanks @GalAster +- typo fix - Thanks @palerdot +- dist cleanup, thanks @zgotsch + +# 0.2.1 (2019-06-30) + +Some String prototype methods are implemented. +Thanks to @lennartbuit we have +trim/trimStart/trimEnd added to the string prototype + +Feature enhancements: + +- [String.prototype.concat ( ...args )](https://tc39.es/ecma262/#sec-string.prototype.slice) +- [String.prototype.endsWith ( searchString [ , endPosition ] )](https://tc39.es/ecma262/#sec-string.prototype.endswith) +- [String.prototype.includes ( searchString [ , position ] )](https://tc39.es/ecma262/#sec-string.prototype.includes) +- [String.prototype.indexOf ( searchString [ , position ] )](https://tc39.es/ecma262/#sec-string.prototype.indexof) +- [String.prototype.lastIndexOf ( searchString [ , position ] )](https://tc39.es/ecma262/#sec-string.prototype.lastindexof) +- [String.prototype.repeat ( count )](https://tc39.es/ecma262/#sec-string.prototype.repeat) +- [String.prototype.slice ( start, end )](https://tc39.es/ecma262/#sec-string.prototype.slice) +- [String.prototype.startsWith ( searchString [ , position ] )](https://tc39.es/ecma262/#sec-string.prototype.startswith) + +Bug fixes: + +- Plenty + +# 0.2.0 (2019-06-10) + +Working state reached + +- Tests on the lexer, conforms with puncturators and keywords from TC39 specification +- wasm-bindgen added with working demo in Web Assembly +- snapshot of boa in a working state for the first time diff --git a/javascript-engine/external/boa/CODE_OF_CONDUCT.md b/javascript-engine/external/boa/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..a3654c1 --- /dev/null +++ b/javascript-engine/external/boa/CODE_OF_CONDUCT.md @@ -0,0 +1,133 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or advances of + any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, + without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[discord](https://discord.gg/tUFFk9Y) by contacting anyone in the _@boa_dev_ +group (check the yellow usernames). +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][mozilla coc]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][faq]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[mozilla coc]: https://github.com/mozilla/diversity +[faq]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/javascript-engine/external/boa/CONTRIBUTING.md b/javascript-engine/external/boa/CONTRIBUTING.md new file mode 100644 index 0000000..407431c --- /dev/null +++ b/javascript-engine/external/boa/CONTRIBUTING.md @@ -0,0 +1,115 @@ +# Contributing to Boa + +Boa welcomes contribution from everyone. Here are the guidelines if you are +thinking of helping out: + +## Contributions + +Contributions to Boa or its dependencies should be made in the form of GitHub +pull requests. Each pull request will be reviewed by a core contributor +(someone with permission to land patches) and either landed in the main tree or +given feedback for changes that would be required. All contributions should +follow this format. + +Should you wish to work on an issue, please claim it first by commenting on +the GitHub issue that you want to work on it. This is to prevent duplicated +efforts from contributors on the same issue. + +Head over to [issues][issues] and check for "good first issue" labels to find +good tasks to start with. If you come across words or jargon that do not make +sense, please ask! + +If you don't already have Rust installed [_rustup_][rustup] is the recommended +tool to use. It will install Rust and allow you to switch between _nightly_, +_stable_ and _beta_. You can also install additional components. In Linux, you +can run: + +```shell +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` + +Then simply clone this project and `cargo build`. + +### Running the compiler + +You can execute a Boa console by running `cargo run`, and you can compile a list +of JavaScript files by running `cargo run -- file1.js file2.js` and so on. + +### Debugging + +Knowing how to debug the interpreter should help you resolve problems quite quickly. +See [Debugging](./docs/debugging.md). + +### Web Assembly + +If you want to develop on the web assembly side you can run `yarn serve` and then go +to . + +### boa-unicode + +Boa uses the library `boa-unicode` to query Unicode character properties and classes in lexer and parser. See [boa_unicode/README.md](./boa_unicode/README.md) for development and more information. + +### Setup + +#### VSCode Plugins + +Either the [Rust (RLS)][rls_vscode] or the [Rust Analyzer][rust-analyzer_vscode] +extensions are preferred. RLS is easier to set up but some of the development is +moving towards Rust Analyzer. Both of these plugins will help you with your Rust +Development + +#### Tasks + +There are some pre-defined tasks in [tasks.json](.vscode/tasks.json) + +- Build - shift+cmd/ctrl+b should build and run cargo. You should be able to make changes and run this task. +- Test - (there is no shortcut, you'll need to make one) - Runs `Cargo Test`. + I personally set a shortcut of shift+cmd+option+T (or shift+ctrl+alt+T) + +If you don't want to install everything on your machine, you can use the Dockerfile. +Start VSCode in container mode (you may need the docker container plugin) and use the Dockerfile. + +## Testing + +Boa provides its own test suite, and can also run the official ECMAScript test suite. To run the Boa test +suite, you can just run the normal `cargo test`, and to run the full ECMAScript test suite, you can run it +with this command: + +```shell +cargo run --release --bin boa_tester -- run -v 2> error.log +``` + +Note that this requires the `test262` submodule to be checked out, so you will need to run the following first: + +```shell +git submodule init && git submodule update +``` + +This will run the test suite in verbose mode (you can remove the `-v` part to run it in non-verbose mode), +and output nice colorings in the terminal. It will also output any panic information into the `error.log` file. + +You can get some more verbose information that tells you the exact name of each test that is being run, useful +for debugging purposes by setting up the verbose flag twice, for example `-vv`. If you want to know the output of +each test that is executed, you can use the triple verbose (`-vvv`) flag. + +If you want to only run one sub-suite or even one test (to just check if you fixed/broke something specific), +you can do it with the `-s` parameter, and then passing the path to the sub-suite or test that you want to run. Note +that the `-s` parameter value should be a path relative to the `test262` directory. For example, to run the number +type tests, use `-s test/language/types/number`. + +Finally, if you're using the verbose flag and running a sub suite with a small number of tests, then the output will +be more readable if you disable parallelism with the `-d` flag. All together it might look something like: + +```shell +cargo run --release --bin boa_tester -- run -vv -d -s test/language/types/number 2> error.log +``` + +## Communication + +We have a Discord server, feel free to ask questions here: + + +[issues]: https://github.com/boa-dev/boa/issues +[rustup]: https://rustup.rs/ +[rls_vscode]: https://marketplace.visualstudio.com/items?itemName=rust-lang.rust +[rust-analyzer_vscode]: https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer diff --git a/javascript-engine/external/boa/Cargo.toml b/javascript-engine/external/boa/Cargo.toml new file mode 100644 index 0000000..8813a82 --- /dev/null +++ b/javascript-engine/external/boa/Cargo.toml @@ -0,0 +1,64 @@ +[workspace] +members = [ + "boa_cli", + "boa_engine", + "boa_ast", + "boa_parser", + "boa_gc", + "boa_interner", + "boa_profiler", + "boa_tester", + "boa_unicode", + "boa_wasm", + "boa_examples", + "boa_macros", + "boa_icu_provider", +] + +[workspace.package] +edition = "2021" +version = "0.16.0" +rust-version = "1.66" +authors = ["boa-dev"] +repository = "https://github.com/boa-dev/boa" +license = "Unlicense/MIT" +description = "Boa is a Javascript lexer, parser and Just-in-Time compiler written in Rust. Currently, it has support for some of the language." + +[workspace.dependencies] +boa_engine = { version = "0.16.0", path = "boa_engine" } +boa_interner = { version = "0.16.0", path = "boa_interner" } +boa_gc = { version = "0.16.0", path = "boa_gc" } +boa_profiler = { version = "0.16.0", path = "boa_profiler" } +boa_unicode = { version = "0.16.0", path = "boa_unicode" } +boa_macros = { version = "0.16.0", path = "boa_macros" } +boa_ast = { version = "0.16.0", path = "boa_ast" } +boa_parser = { version = "0.16.0", path = "boa_parser" } +boa_icu_provider = { version = "0.16.0", path = "boa_icu_provider" } + +[workspace.metadata.workspaces] +allow_branch = "main" + +# The ci profile, designed to reduce size of target directory +[profile.ci] +inherits = "dev" +debug = false +incremental = false + +# The release profile, used for `cargo build --release`. +[profile.release] +# Enables "fat" LTO, for faster release builds +lto = "fat" +# Makes sure that all code is compiled together, for LTO +codegen-units = 1 + +# The test profile, used for `cargo test`. +[profile.test] +# Enables thin local LTO and some optimizations. +opt-level = 1 + +# The benchmark profile, used for `cargo bench`. +[profile.bench] +# Enables "fat" LTO, for faster benchmark builds +lto = "fat" +# Makes sure that all code is compiled together, for LTO +codegen-units = 1 diff --git a/javascript-engine/external/boa/LICENSE-MIT b/javascript-engine/external/boa/LICENSE-MIT new file mode 100644 index 0000000..d87a9e8 --- /dev/null +++ b/javascript-engine/external/boa/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Jason Williams + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/javascript-engine/external/boa/LICENSE-UNLICENSE b/javascript-engine/external/boa/LICENSE-UNLICENSE new file mode 100644 index 0000000..cf1ab25 --- /dev/null +++ b/javascript-engine/external/boa/LICENSE-UNLICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/javascript-engine/external/boa/README.md b/javascript-engine/external/boa/README.md new file mode 100644 index 0000000..8909738 --- /dev/null +++ b/javascript-engine/external/boa/README.md @@ -0,0 +1,115 @@ +# Boa + +

+ Boa Logo +

+ +This is an experimental Javascript lexer, parser and interpreter written in Rust. +Currently, it has support for some of the language. + +[![Build Status][build_badge]][build_link] +[![codecov](https://codecov.io/gh/boa-dev/boa/branch/main/graph/badge.svg)](https://codecov.io/gh/boa-dev/boa) +[![Crates.io](https://img.shields.io/crates/v/boa_engine.svg)](https://crates.io/crates/boa_engine) +[![Docs.rs](https://docs.rs/boa_engine/badge.svg)](https://docs.rs/boa_engine) +[![Discord](https://img.shields.io/discord/595323158140158003?logo=discord)](https://discord.gg/tUFFk9Y) + +[build_badge]: https://github.com/boa-dev/boa/actions/workflows/rust.yml/badge.svg?event=push&branch=main +[build_link]: https://github.com/boa-dev/boa/actions/workflows/rust.yml?query=event%3Apush+branch%3Amain + +## Live Demo (WASM) + + + +You can get more verbose errors when running from the command line. + +## Development documentation + +You can check the internal development docs at . + +## Conformance + +To know how much of the _ECMAScript_ specification does Boa cover, you can check out results running the _ECMASCript Test262_ test suite [here](https://boa-dev.github.io/boa/test262/). + +## Contributing + +Please, check the [CONTRIBUTING.md](CONTRIBUTING.md) file to know how to +contribute in the project. You will need Rust installed and an editor. We have +some configurations ready for VSCode. + +### Debugging + +Check [debugging.md](./docs/debugging.md) for more info on debugging. + +### Web Assembly + +This interpreter can be exposed to JavaScript! +You can build the example locally with: + +```shell +npm run build +``` + +In the console you can use `window.evaluate` to pass JavaScript in. +To develop on the web assembly side you can run: + +```shell +npm run serve +``` + +then go to `http://localhost:8080`. + +## Usage + +- Clone this repo. +- Run with `cargo run -- test.js` where `test.js` is an existing JS file with any JS valid code. +- If any JS doesn't work then it's a bug. Please raise an [issue](https://github.com/boa-dev/boa/issues/)! + +### Example + +![Example](docs/img/latestDemo.gif) + +## Command-line Options + +```shell +USAGE: + boa [OPTIONS] [FILE]... + +FLAGS: + -h, --help Prints help information + -V, --version Prints version information + +OPTIONS: + -a, --dump-ast Dump the abstract syntax tree (ast) to stdout with the given format [possible values: Debug, Json, + JsonPretty] + +ARGS: + ... The JavaScript file(s) to be evaluated +``` + +## Roadmap + +See [Milestones](https://github.com/boa-dev/boa/milestones). + +## Benchmarks + +See [Benchmarks](https://boa-dev.github.io/boa/dev/bench/). + +## Profiling + +See [Profiling](./docs/profiling.md). + +## Changelog + +See [CHANGELOG.md](./CHANGELOG.md). + +## Communication + +Feel free to contact us on [Discord](https://discord.gg/tUFFk9Y). + +## License + +This project is licensed under the [Unlicense](./LICENSE-UNLICENSE) or [MIT](./LICENSE-MIT) licenses, at your option. diff --git a/javascript-engine/external/boa/assets/01_rust_loves_js.png b/javascript-engine/external/boa/assets/01_rust_loves_js.png new file mode 100644 index 0000000..145034d Binary files /dev/null and b/javascript-engine/external/boa/assets/01_rust_loves_js.png differ diff --git a/javascript-engine/external/boa/assets/logo.svg b/javascript-engine/external/boa/assets/logo.svg new file mode 100644 index 0000000..d91e1db --- /dev/null +++ b/javascript-engine/external/boa/assets/logo.svg @@ -0,0 +1,184 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/javascript-engine/external/boa/boa_ast/Cargo.toml b/javascript-engine/external/boa/boa_ast/Cargo.toml new file mode 100644 index 0000000..da866d8 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "boa_ast" +description = "Abstract Syntax Tree definition for the Boa JavaScript engine." +keywords = ["javascript", "js", "syntax", "ast"] +categories = ["parser-implementations", "compilers"] +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[features] +serde = ["boa_interner/serde", "dep:serde"] + +fuzz = ["arbitrary", "boa_interner/fuzz", "num-bigint/arbitrary"] + +[dependencies] +boa_interner.workspace = true +boa_macros.workspace = true +rustc-hash = "1.1.0" +serde = { version = "1.0.152", features = ["derive"], optional = true } +bitflags = "1.3.2" +num-bigint = "0.4.3" +arbitrary = { version = "1", optional = true, features = ["derive"] } diff --git a/javascript-engine/external/boa/boa_ast/src/declaration/mod.rs b/javascript-engine/external/boa/boa_ast/src/declaration/mod.rs new file mode 100644 index 0000000..2aceb78 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/declaration/mod.rs @@ -0,0 +1,97 @@ +//! The [`Declaration`] Parse Node, as defined by the [spec]. +//! +//! ECMAScript declarations include: +//! - [Lexical][lex] declarations (`let`, `const`). +//! - [Function][fun] declarations (`function`, `async function`). +//! - [Class][class] declarations. +//! +//! See [*Difference between statements and declarations*][diff] for an explanation on why `Declaration`s +//! and `Statement`s are distinct nodes. +//! +//! [spec]: https://tc39.es/ecma262/#prod-Declaration +//! [lex]: https://tc39.es/ecma262/#prod-LexicalDeclaration +//! [fun]: https://tc39.es/ecma262/#prod-HoistableDeclaration +//! [class]: https://tc39.es/ecma262/#prod-ClassDeclaration +//! [diff]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements#difference_between_statements_and_declarations + +use super::function::{AsyncFunction, AsyncGenerator, Class, Function, Generator}; +use boa_interner::{Interner, ToIndentedString, ToInternedString}; +use core::ops::ControlFlow; + +mod variable; + +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +pub use variable::*; + +/// The `Declaration` Parse Node. +/// +/// See the [module level documentation][self] for more information. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum Declaration { + /// See [`Function`] + Function(Function), + + /// See [`Generator`] + Generator(Generator), + + /// See [`AsyncFunction`] + AsyncFunction(AsyncFunction), + + /// See [`AsyncGenerator`] + AsyncGenerator(AsyncGenerator), + + /// See [`Class`] + Class(Class), + + /// See [`LexicalDeclaration`] + Lexical(LexicalDeclaration), +} + +impl ToIndentedString for Declaration { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + match self { + Self::Function(f) => f.to_indented_string(interner, indentation), + Self::Generator(g) => g.to_indented_string(interner, indentation), + Self::AsyncFunction(af) => af.to_indented_string(interner, indentation), + Self::AsyncGenerator(ag) => ag.to_indented_string(interner, indentation), + Self::Class(c) => c.to_indented_string(interner, indentation), + Self::Lexical(l) => { + let mut s = l.to_interned_string(interner); + s.push(';'); + s + } + } + } +} + +impl VisitWith for Declaration { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::Function(f) => visitor.visit_function(f), + Self::Generator(g) => visitor.visit_generator(g), + Self::AsyncFunction(af) => visitor.visit_async_function(af), + Self::AsyncGenerator(ag) => visitor.visit_async_generator(ag), + Self::Class(c) => visitor.visit_class(c), + Self::Lexical(ld) => visitor.visit_lexical_declaration(ld), + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::Function(f) => visitor.visit_function_mut(f), + Self::Generator(g) => visitor.visit_generator_mut(g), + Self::AsyncFunction(af) => visitor.visit_async_function_mut(af), + Self::AsyncGenerator(ag) => visitor.visit_async_generator_mut(ag), + Self::Class(c) => visitor.visit_class_mut(c), + Self::Lexical(ld) => visitor.visit_lexical_declaration_mut(ld), + } + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/declaration/variable.rs b/javascript-engine/external/boa/boa_ast/src/declaration/variable.rs new file mode 100644 index 0000000..c206ef4 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/declaration/variable.rs @@ -0,0 +1,385 @@ +//! Variable related declarations. + +use core::ops::ControlFlow; +use std::convert::TryFrom; + +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ + expression::{Expression, Identifier}, + join_nodes, + pattern::Pattern, + Statement, +}; +use boa_interner::{Interner, ToInternedString}; + +use super::Declaration; + +/// A [`var`][var] statement, also called [`VariableStatement`][varstmt] in the spec. +/// +/// The scope of a variable declared with `var` is its current execution context, which is either +/// the enclosing function or, for variables declared outside any function, global. If you +/// re-declare a ECMAScript variable, it will not lose its value. +/// +/// Although a bit confusing, `VarDeclaration`s are not considered [`Declaration`]s by the spec. +/// This is partly because it has very different semantics from `let` and `const` declarations, but +/// also because a `var` statement can be labelled just like any other [`Statement`]: +/// +/// ```javascript +/// label: var a = 5; +/// a; +/// ``` +/// +/// returns `5` as the value of the statement list, while: +/// +/// ```javascript +/// label: let a = 5; +/// a; +/// ``` +/// throws a `SyntaxError`. +/// +/// `var` declarations, wherever they occur, are processed before any code is executed. This is +/// called [hoisting]. +/// +/// [var]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var +/// [varstmt]: https://tc39.es/ecma262/#prod-VariableStatement +/// [hoisting]: https://developer.mozilla.org/en-US/docs/Glossary/Hoisting +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct VarDeclaration(pub VariableList); + +impl From for Statement { + fn from(var: VarDeclaration) -> Self { + Self::Var(var) + } +} + +impl ToInternedString for VarDeclaration { + fn to_interned_string(&self, interner: &Interner) -> String { + format!("var {}", self.0.to_interned_string(interner)) + } +} + +impl VisitWith for VarDeclaration { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + visitor.visit_variable_list(&self.0) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + visitor.visit_variable_list_mut(&mut self.0) + } +} + +/// A **[lexical declaration]** defines variables that are scoped to the lexical environment of +/// the variable declaration. +/// +/// [lexical declaration]: https://tc39.es/ecma262/#sec-let-and-const-declarations +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum LexicalDeclaration { + /// A [const] variable creates a constant whose scope can be either global or local + /// to the block in which it is declared. + /// + /// An initializer for a constant is required. You must specify its value in the same statement + /// in which it's declared. (This makes sense, given that it can't be changed later) + /// + /// [const]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const + Const(VariableList), + + /// A [let] variable is limited to a scope of a block statement, or expression on + /// which it is used, unlike the `var` keyword, which defines a variable globally, or locally to + /// an entire function regardless of block scope. + /// + /// Just like const, `let` does not create properties of the window object when declared + /// globally (in the top-most scope). + /// + /// If a let declaration does not have an initializer, the variable is assigned the value `undefined`. + /// + /// [let]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let + Let(VariableList), +} + +impl LexicalDeclaration { + /// Gets the inner variable list of the `LexicalDeclaration` + #[must_use] + pub const fn variable_list(&self) -> &VariableList { + match self { + Self::Const(list) | Self::Let(list) => list, + } + } +} + +impl From for Declaration { + fn from(lex: LexicalDeclaration) -> Self { + Self::Lexical(lex) + } +} + +impl ToInternedString for LexicalDeclaration { + fn to_interned_string(&self, interner: &Interner) -> String { + format!( + "{} {}", + match &self { + Self::Let(_) => "let", + Self::Const(_) => "const", + }, + self.variable_list().to_interned_string(interner) + ) + } +} + +impl VisitWith for LexicalDeclaration { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::Const(vars) | Self::Let(vars) => visitor.visit_variable_list(vars), + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::Const(vars) | Self::Let(vars) => visitor.visit_variable_list_mut(vars), + } + } +} + +/// List of variables in a variable declaration. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct VariableList { + list: Box<[Variable]>, +} + +impl VariableList { + /// Creates a variable list if the provided list of [`Variable`] is not empty. + #[must_use] + pub fn new(list: Box<[Variable]>) -> Option { + if list.is_empty() { + return None; + } + + Some(Self { list }) + } +} + +impl AsRef<[Variable]> for VariableList { + fn as_ref(&self) -> &[Variable] { + &self.list + } +} + +impl ToInternedString for VariableList { + fn to_interned_string(&self, interner: &Interner) -> String { + join_nodes(interner, self.list.as_ref()) + } +} + +impl VisitWith for VariableList { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + for variable in self.list.iter() { + try_break!(visitor.visit_variable(variable)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + for variable in self.list.iter_mut() { + try_break!(visitor.visit_variable_mut(variable)); + } + ControlFlow::Continue(()) + } +} + +/// The error returned by the [`VariableList::try_from`] function. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct TryFromVariableListError(()); + +impl std::fmt::Display for TryFromVariableListError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + "provided list of variables cannot be empty".fmt(f) + } +} + +impl TryFrom> for VariableList { + type Error = TryFromVariableListError; + + fn try_from(value: Box<[Variable]>) -> Result { + Self::new(value).ok_or(TryFromVariableListError(())) + } +} + +impl TryFrom> for VariableList { + type Error = TryFromVariableListError; + + fn try_from(value: Vec) -> Result { + Self::try_from(value.into_boxed_slice()) + } +} + +/// Variable represents a variable declaration of some kind. +/// +/// For `let` and `const` declarations this type represents a [`LexicalBinding`][spec1] +/// +/// For `var` declarations this type represents a [`VariableDeclaration`][spec2] +/// +/// More information: +/// - [ECMAScript reference: 14.3 Declarations and the Variable Statement][spec3] +/// +/// [spec1]: https://tc39.es/ecma262/#prod-LexicalBinding +/// [spec2]: https://tc39.es/ecma262/#prod-VariableDeclaration +/// [spec3]: https://tc39.es/ecma262/#sec-declarations-and-the-variable-statement +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Variable { + binding: Binding, + init: Option, +} + +impl ToInternedString for Variable { + fn to_interned_string(&self, interner: &Interner) -> String { + let mut buf = self.binding.to_interned_string(interner); + + if let Some(ref init) = self.init { + buf.push_str(&format!(" = {}", init.to_interned_string(interner))); + } + buf + } +} + +impl Variable { + /// Creates a new variable declaration from a `BindingIdentifier`. + #[inline] + #[must_use] + pub const fn from_identifier(ident: Identifier, init: Option) -> Self { + Self { + binding: Binding::Identifier(ident), + init, + } + } + + /// Creates a new variable declaration from a `Pattern`. + #[inline] + #[must_use] + pub const fn from_pattern(pattern: Pattern, init: Option) -> Self { + Self { + binding: Binding::Pattern(pattern), + init, + } + } + /// Gets the variable declaration binding. + #[must_use] + pub const fn binding(&self) -> &Binding { + &self.binding + } + + /// Gets the initialization expression for the variable declaration, if any. + #[inline] + #[must_use] + pub const fn init(&self) -> Option<&Expression> { + self.init.as_ref() + } +} + +impl VisitWith for Variable { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_binding(&self.binding)); + if let Some(init) = &self.init { + try_break!(visitor.visit_expression(init)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_binding_mut(&mut self.binding)); + if let Some(init) = &mut self.init { + try_break!(visitor.visit_expression_mut(init)); + } + ControlFlow::Continue(()) + } +} + +/// Binding represents either an individual binding or a binding pattern. +/// +/// More information: +/// - [ECMAScript reference: 14.3 Declarations and the Variable Statement][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-declarations-and-the-variable-statement +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum Binding { + /// A single identifier binding. + Identifier(Identifier), + /// A pattern binding. + Pattern(Pattern), +} + +impl From for Binding { + fn from(id: Identifier) -> Self { + Self::Identifier(id) + } +} + +impl From for Binding { + fn from(pat: Pattern) -> Self { + Self::Pattern(pat) + } +} + +impl ToInternedString for Binding { + fn to_interned_string(&self, interner: &Interner) -> String { + match self { + Self::Identifier(id) => id.to_interned_string(interner), + Self::Pattern(ref pattern) => pattern.to_interned_string(interner), + } + } +} + +impl VisitWith for Binding { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::Identifier(id) => visitor.visit_identifier(id), + Self::Pattern(pattern) => visitor.visit_pattern(pattern), + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::Identifier(id) => visitor.visit_identifier_mut(id), + Self::Pattern(pattern) => visitor.visit_pattern_mut(pattern), + } + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/access.rs b/javascript-engine/external/boa/boa_ast/src/expression/access.rs new file mode 100644 index 0000000..fd189ba --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/access.rs @@ -0,0 +1,348 @@ +//! Property access expressions, as defined by the [spec]. +//! +//! [Property access expressions][access] provide two ways to access properties of an object: *dot notation* +//! and *bracket notation*. +//! - *Dot notation* is mostly used when the name of the property is static, and a valid Javascript +//! identifier e.g. `obj.prop`, `arr.$val`. +//! - *Bracket notation* is used when the name of the property is either variable, not a valid +//! identifier or a symbol e.g. `arr[var]`, `arr[5]`, `arr[Symbol.iterator]`. +//! +//! A property access expression can be represented by a [`SimplePropertyAccess`] (`x.y`), a +//! [`PrivatePropertyAccess`] (`x.#y`) or a [`SuperPropertyAccess`] (`super["y"]`), each of them with +//! slightly different semantics overall. +//! +//! [spec]: https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-property-accessors +//! [access]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors + +use crate::expression::Expression; +use crate::function::PrivateName; +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use boa_interner::{Interner, Sym, ToInternedString}; +use core::ops::ControlFlow; + +/// A property access field. +/// +/// See the [module level documentation][self] for more information. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum PropertyAccessField { + /// A constant property field, such as `x.prop`. + Const(Sym), + /// An expression property field, such as `x["val"]`. + Expr(Box), +} + +impl From for PropertyAccessField { + #[inline] + fn from(id: Sym) -> Self { + Self::Const(id) + } +} + +impl From for PropertyAccessField { + #[inline] + fn from(expr: Expression) -> Self { + Self::Expr(Box::new(expr)) + } +} + +impl VisitWith for PropertyAccessField { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::Const(sym) => visitor.visit_sym(sym), + Self::Expr(expr) => visitor.visit_expression(expr), + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::Const(sym) => visitor.visit_sym_mut(sym), + Self::Expr(expr) => visitor.visit_expression_mut(&mut *expr), + } + } +} + +/// A property access expression. +/// +/// See the [module level documentation][self] for more information. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum PropertyAccess { + /// A simple property access (`x.prop`). + Simple(SimplePropertyAccess), + /// A property access of a private property (`x.#priv`). + Private(PrivatePropertyAccess), + /// A property access of a `super` reference. (`super["prop"]`). + Super(SuperPropertyAccess), +} + +impl ToInternedString for PropertyAccess { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + match self { + Self::Simple(s) => s.to_interned_string(interner), + Self::Private(p) => p.to_interned_string(interner), + Self::Super(s) => s.to_interned_string(interner), + } + } +} + +impl From for Expression { + #[inline] + fn from(access: PropertyAccess) -> Self { + Self::PropertyAccess(access) + } +} + +impl VisitWith for PropertyAccess { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::Simple(spa) => visitor.visit_simple_property_access(spa), + Self::Private(ppa) => visitor.visit_private_property_access(ppa), + Self::Super(supa) => visitor.visit_super_property_access(supa), + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::Simple(spa) => visitor.visit_simple_property_access_mut(spa), + Self::Private(ppa) => visitor.visit_private_property_access_mut(ppa), + Self::Super(supa) => visitor.visit_super_property_access_mut(supa), + } + } +} + +/// A simple property access, where the target object is an [`Expression`]. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct SimplePropertyAccess { + target: Box, + field: PropertyAccessField, +} + +impl SimplePropertyAccess { + /// Gets the target object of the property access. + #[inline] + #[must_use] + pub const fn target(&self) -> &Expression { + &self.target + } + + /// Gets the accessed field of the target object. + #[inline] + #[must_use] + pub const fn field(&self) -> &PropertyAccessField { + &self.field + } + + /// Creates a `PropertyAccess` AST Expression. + pub fn new(target: Expression, field: F) -> Self + where + F: Into, + { + Self { + target: target.into(), + field: field.into(), + } + } +} + +impl ToInternedString for SimplePropertyAccess { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + let target = self.target.to_interned_string(interner); + match self.field { + PropertyAccessField::Const(sym) => format!("{target}.{}", interner.resolve_expect(sym)), + PropertyAccessField::Expr(ref expr) => { + format!("{target}[{}]", expr.to_interned_string(interner)) + } + } + } +} + +impl From for PropertyAccess { + #[inline] + fn from(access: SimplePropertyAccess) -> Self { + Self::Simple(access) + } +} + +impl VisitWith for SimplePropertyAccess { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_expression(&self.target)); + visitor.visit_property_access_field(&self.field) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_expression_mut(&mut self.target)); + visitor.visit_property_access_field_mut(&mut self.field) + } +} + +/// An access expression to a class object's [private fields][mdn]. +/// +/// Private property accesses differ slightly from plain property accesses, since the accessed +/// property must be prefixed by `#`, and the bracket notation is not allowed. For example, +/// `this.#a` is a valid private property access. +/// +/// This expression corresponds to the [`MemberExpression.PrivateIdentifier`][spec] production. +/// +/// [spec]: https://tc39.es/ecma262/#prod-MemberExpression +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct PrivatePropertyAccess { + target: Box, + field: PrivateName, +} + +impl PrivatePropertyAccess { + /// Creates a `GetPrivateField` AST Expression. + #[inline] + #[must_use] + pub fn new(value: Expression, field: PrivateName) -> Self { + Self { + target: value.into(), + field, + } + } + + /// Gets the original object from where to get the field from. + #[inline] + #[must_use] + pub const fn target(&self) -> &Expression { + &self.target + } + + /// Gets the name of the field to retrieve. + #[inline] + #[must_use] + pub const fn field(&self) -> PrivateName { + self.field + } +} + +impl ToInternedString for PrivatePropertyAccess { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + format!( + "{}.#{}", + self.target.to_interned_string(interner), + interner.resolve_expect(self.field.description()) + ) + } +} + +impl From for PropertyAccess { + #[inline] + fn from(access: PrivatePropertyAccess) -> Self { + Self::Private(access) + } +} + +impl VisitWith for PrivatePropertyAccess { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_expression(&self.target)); + visitor.visit_private_name(&self.field) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_expression_mut(&mut self.target)); + visitor.visit_private_name_mut(&mut self.field) + } +} + +/// A property access of an object's parent, as defined by the [spec]. +/// +/// A `SuperPropertyAccess` is much like a regular [`PropertyAccess`], but where its `target` object +/// is not a regular object, but a reference to the parent object of the current object ([`super`][mdn]). +/// +/// [spec]: https://tc39.es/ecma262/#prod-SuperProperty +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct SuperPropertyAccess { + field: PropertyAccessField, +} + +impl SuperPropertyAccess { + /// Creates a new property access field node. + #[must_use] + pub const fn new(field: PropertyAccessField) -> Self { + Self { field } + } + + /// Gets the name of the field to retrieve. + #[inline] + #[must_use] + pub const fn field(&self) -> &PropertyAccessField { + &self.field + } +} + +impl ToInternedString for SuperPropertyAccess { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + match &self.field { + PropertyAccessField::Const(field) => { + format!("super.{}", interner.resolve_expect(*field)) + } + PropertyAccessField::Expr(field) => { + format!("super[{}]", field.to_interned_string(interner)) + } + } + } +} + +impl From for PropertyAccess { + #[inline] + fn from(access: SuperPropertyAccess) -> Self { + Self::Super(access) + } +} + +impl VisitWith for SuperPropertyAccess { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + visitor.visit_property_access_field(&self.field) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + visitor.visit_property_access_field_mut(&mut self.field) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/await.rs b/javascript-engine/external/boa/boa_ast/src/expression/await.rs new file mode 100644 index 0000000..602e931 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/await.rs @@ -0,0 +1,71 @@ +//! Await expression Expression. + +use core::ops::ControlFlow; + +use super::Expression; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use boa_interner::{Interner, ToIndentedString, ToInternedString}; + +/// An await expression is used within an async function to pause execution and wait for a +/// promise to resolve. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-AwaitExpression +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Await { + target: Box, +} + +impl Await { + /// Return the target expression that should be awaited. + #[inline] + #[must_use] + pub const fn target(&self) -> &Expression { + &self.target + } +} + +impl From for Await +where + T: Into>, +{ + fn from(e: T) -> Self { + Self { target: e.into() } + } +} + +impl ToInternedString for Await { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + format!("await {}", self.target.to_indented_string(interner, 0)) + } +} + +impl From for Expression { + #[inline] + fn from(awaitexpr: Await) -> Self { + Self::Await(awaitexpr) + } +} + +impl VisitWith for Await { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + visitor.visit_expression(&self.target) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + visitor.visit_expression_mut(&mut self.target) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/call.rs b/javascript-engine/external/boa/boa_ast/src/expression/call.rs new file mode 100644 index 0000000..5b97824 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/call.rs @@ -0,0 +1,164 @@ +use crate::join_nodes; +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use boa_interner::{Interner, ToInternedString}; +use core::ops::ControlFlow; + +use super::Expression; + +/// Calling the function actually performs the specified actions with the indicated parameters. +/// +/// Defining a function does not execute it. Defining it simply names the function and +/// specifies what to do when the function is called. Functions must be in scope when they are +/// called, but the function declaration can be hoisted. The scope of a function is the +/// function in which it is declared (or the entire program, if it is declared at the top +/// level). +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-CallExpression +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#Calling_functions +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Call { + function: Box, + args: Box<[Expression]>, +} + +impl Call { + /// Creates a new `Call` AST Expression. + #[inline] + #[must_use] + pub fn new(function: Expression, args: Box<[Expression]>) -> Self { + Self { + function: function.into(), + args, + } + } + + /// Gets the target function of this call expression. + #[inline] + #[must_use] + pub const fn function(&self) -> &Expression { + &self.function + } + + /// Retrieves the arguments passed to the function. + #[inline] + #[must_use] + pub const fn args(&self) -> &[Expression] { + &self.args + } +} + +impl ToInternedString for Call { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + format!( + "{}({})", + self.function.to_interned_string(interner), + join_nodes(interner, &self.args) + ) + } +} + +impl From for Expression { + #[inline] + fn from(call: Call) -> Self { + Self::Call(call) + } +} + +impl VisitWith for Call { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_expression(&self.function)); + for expr in self.args.iter() { + try_break!(visitor.visit_expression(expr)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_expression_mut(&mut self.function)); + for expr in self.args.iter_mut() { + try_break!(visitor.visit_expression_mut(expr)); + } + ControlFlow::Continue(()) + } +} + +/// The `super` keyword is used to access and call functions on an object's parent. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-SuperCall +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct SuperCall { + args: Box<[Expression]>, +} + +impl SuperCall { + /// Creates a new `SuperCall` AST node. + pub fn new(args: A) -> Self + where + A: Into>, + { + Self { args: args.into() } + } + + /// Retrieves the arguments of the super call. + #[must_use] + pub const fn arguments(&self) -> &[Expression] { + &self.args + } +} + +impl ToInternedString for SuperCall { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + format!("super({})", join_nodes(interner, &self.args)) + } +} + +impl From for Expression { + #[inline] + fn from(call: SuperCall) -> Self { + Self::SuperCall(call) + } +} + +impl VisitWith for SuperCall { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + for expr in self.args.iter() { + try_break!(visitor.visit_expression(expr)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + for expr in self.args.iter_mut() { + try_break!(visitor.visit_expression_mut(expr)); + } + ControlFlow::Continue(()) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/identifier.rs b/javascript-engine/external/boa/boa_ast/src/expression/identifier.rs new file mode 100644 index 0000000..cdcf275 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/identifier.rs @@ -0,0 +1,122 @@ +//! Local identifier Expression. + +use crate::{ + visitor::{VisitWith, Visitor, VisitorMut}, + ToStringEscaped, +}; +use boa_interner::{Interner, Sym, ToInternedString}; +use core::ops::ControlFlow; + +use super::Expression; + +/// List of reserved keywords exclusive to strict mode. +pub const RESERVED_IDENTIFIERS_STRICT: [Sym; 9] = [ + Sym::IMPLEMENTS, + Sym::INTERFACE, + Sym::LET, + Sym::PACKAGE, + Sym::PRIVATE, + Sym::PROTECTED, + Sym::PUBLIC, + Sym::STATIC, + Sym::YIELD, +]; + +/// An `identifier` is a sequence of characters in the code that identifies a variable, +/// function, or property. +/// +/// In ECMAScript, identifiers are case-sensitive and can contain Unicode letters, $, _, and +/// digits (0-9), but may not start with a digit. +/// +/// An identifier differs from a string in that a string is data, while an identifier is part +/// of the code. In JavaScript, there is no way to convert identifiers to strings, but +/// sometimes it is possible to parse strings into identifiers. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-Identifier +/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Identifier +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(transparent) +)] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(transparent)] +pub struct Identifier { + ident: Sym, +} + +impl PartialEq for Identifier { + #[inline] + fn eq(&self, other: &Sym) -> bool { + self.ident == *other + } +} + +impl PartialEq for Sym { + #[inline] + fn eq(&self, other: &Identifier) -> bool { + *self == other.ident + } +} + +impl Identifier { + /// Creates a new identifier AST Expression. + #[inline] + #[must_use] + pub const fn new(ident: Sym) -> Self { + Self { ident } + } + + /// Retrieves the identifier's string symbol in the interner. + #[inline] + #[must_use] + pub const fn sym(self) -> Sym { + self.ident + } +} + +impl ToInternedString for Identifier { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + interner.resolve_expect(self.ident).join( + String::from, + ToStringEscaped::to_string_escaped, + true, + ) + } +} + +impl From for Identifier { + #[inline] + fn from(sym: Sym) -> Self { + Self { ident: sym } + } +} + +impl From for Expression { + #[inline] + fn from(local: Identifier) -> Self { + Self::Identifier(local) + } +} + +impl VisitWith for Identifier { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + visitor.visit_sym(&self.ident) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + visitor.visit_sym_mut(&mut self.ident) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/literal/array.rs b/javascript-engine/external/boa/boa_ast/src/expression/literal/array.rs new file mode 100644 index 0000000..81decbc --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/literal/array.rs @@ -0,0 +1,223 @@ +//! Array declaration Expression. + +use crate::expression::operator::assign::AssignTarget; +use crate::expression::Expression; +use crate::pattern::{ArrayPattern, ArrayPatternElement, Pattern}; +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use boa_interner::{Interner, Sym, ToInternedString}; +use core::ops::ControlFlow; + +/// An array is an ordered collection of data (either primitive or object depending upon the +/// language). +/// +/// Arrays are used to store multiple values in a single variable. +/// This is compared to a variable that can store only one value. +/// +/// Each item in an array has a number attached to it, called a numeric index, that allows you +/// to access it. In JavaScript, arrays start at index zero and can be manipulated with various +/// methods. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-ArrayLiteral +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct ArrayLiteral { + arr: Box<[Option]>, + has_trailing_comma_spread: bool, +} + +impl ArrayLiteral { + /// Creates a new array literal. + pub fn new(array: A, has_trailing_comma_spread: bool) -> Self + where + A: Into]>>, + { + Self { + arr: array.into(), + has_trailing_comma_spread, + } + } + + /// Indicates if a spread operator in the array literal has a trailing comma. + /// This is a syntax error in some cases. + #[must_use] + pub const fn has_trailing_comma_spread(&self) -> bool { + self.has_trailing_comma_spread + } + + /// Converts this `ArrayLiteral` into an [`ArrayPattern`]. + #[must_use] + pub fn to_pattern(&self, strict: bool) -> Option { + if self.has_trailing_comma_spread() { + return None; + } + + let mut bindings = Vec::new(); + for (i, expr) in self.arr.iter().enumerate() { + let expr = if let Some(expr) = expr { + expr + } else { + bindings.push(ArrayPatternElement::Elision); + continue; + }; + match expr { + Expression::Identifier(ident) => { + if strict && *ident == Sym::ARGUMENTS { + return None; + } + + bindings.push(ArrayPatternElement::SingleName { + ident: *ident, + default_init: None, + }); + } + Expression::Spread(spread) => { + match spread.target() { + Expression::Identifier(ident) => { + bindings.push(ArrayPatternElement::SingleNameRest { ident: *ident }); + } + Expression::PropertyAccess(access) => { + bindings.push(ArrayPatternElement::PropertyAccessRest { + access: access.clone(), + }); + } + Expression::ArrayLiteral(array) => { + let pattern = array.to_pattern(strict)?.into(); + bindings.push(ArrayPatternElement::PatternRest { pattern }); + } + Expression::ObjectLiteral(object) => { + let pattern = object.to_pattern(strict)?.into(); + bindings.push(ArrayPatternElement::PatternRest { pattern }); + } + _ => return None, + } + if i + 1 != self.arr.len() { + return None; + } + } + Expression::Assign(assign) => match assign.lhs() { + AssignTarget::Identifier(ident) => { + bindings.push(ArrayPatternElement::SingleName { + ident: *ident, + default_init: Some(assign.rhs().clone()), + }); + } + AssignTarget::Access(access) => { + bindings.push(ArrayPatternElement::PropertyAccess { + access: access.clone(), + }); + } + AssignTarget::Pattern(pattern) => match pattern { + Pattern::Object(pattern) => { + bindings.push(ArrayPatternElement::Pattern { + pattern: Pattern::Object(pattern.clone()), + default_init: Some(assign.rhs().clone()), + }); + } + Pattern::Array(pattern) => { + bindings.push(ArrayPatternElement::Pattern { + pattern: Pattern::Array(pattern.clone()), + default_init: Some(assign.rhs().clone()), + }); + } + }, + }, + Expression::ArrayLiteral(array) => { + let pattern = array.to_pattern(strict)?.into(); + bindings.push(ArrayPatternElement::Pattern { + pattern, + default_init: None, + }); + } + Expression::ObjectLiteral(object) => { + let pattern = object.to_pattern(strict)?.into(); + bindings.push(ArrayPatternElement::Pattern { + pattern, + default_init: None, + }); + } + Expression::PropertyAccess(access) => { + bindings.push(ArrayPatternElement::PropertyAccess { + access: access.clone(), + }); + } + _ => return None, + } + } + Some(ArrayPattern::new(bindings.into())) + } +} + +impl AsRef<[Option]> for ArrayLiteral { + #[inline] + fn as_ref(&self) -> &[Option] { + &self.arr + } +} + +impl From for ArrayLiteral +where + T: Into]>>, +{ + fn from(decl: T) -> Self { + Self { + arr: decl.into(), + has_trailing_comma_spread: false, + } + } +} + +impl ToInternedString for ArrayLiteral { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + let mut buf = String::from("["); + let mut first = true; + for e in &*self.arr { + if first { + first = false; + } else { + buf.push_str(", "); + } + if let Some(e) = e { + buf.push_str(&e.to_interned_string(interner)); + } + } + buf.push(']'); + buf + } +} + +impl From for Expression { + #[inline] + fn from(arr: ArrayLiteral) -> Self { + Self::ArrayLiteral(arr) + } +} + +impl VisitWith for ArrayLiteral { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + for expr in self.arr.iter().flatten() { + try_break!(visitor.visit_expression(expr)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + for expr in self.arr.iter_mut().flatten() { + try_break!(visitor.visit_expression_mut(expr)); + } + ControlFlow::Continue(()) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/literal/mod.rs b/javascript-engine/external/boa/boa_ast/src/expression/literal/mod.rs new file mode 100644 index 0000000..4f174c5 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/literal/mod.rs @@ -0,0 +1,202 @@ +//! This module contains all literal expressions, which represents the primitive values in ECMAScript. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! - [MDN documentation][mdn] +//! +//! [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals + +mod array; +mod object; +mod template; + +pub use array::ArrayLiteral; +use core::ops::ControlFlow; +pub use object::ObjectLiteral; +pub use template::{TemplateElement, TemplateLiteral}; + +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use boa_interner::{Interner, Sym, ToInternedString}; +use num_bigint::BigInt; + +use super::Expression; + +/// Literals represent values in ECMAScript. +/// +/// These are fixed values **not variables** that you literally provide in your script. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum Literal { + /// A string literal is zero or more characters enclosed in double (`"`) or single (`'`) quotation marks. + /// + /// A string must be delimited by quotation marks of the same type (that is, either both single quotation marks, or both double quotation marks). + /// You can call any of the String object's methods on a string literal value. + /// ECMAScript automatically converts the string literal to a temporary String object, + /// calls the method, then discards the temporary String object. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-string-value + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#String_literals + String(Sym), + + /// A floating-point number literal. + /// + /// The exponent part is an "`e`" or "`E`" followed by an integer, which can be signed (preceded by "`+`" or "`-`"). + /// A floating-point literal must have at least one digit, and either a decimal point or "`e`" (or "`E`"). + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-number-value + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Floating-point_literals + Num(f64), + + /// Integer types can be expressed in decimal (base 10), hexadecimal (base 16), octal (base 8) and binary (base 2). + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-number-value + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Numeric_literals + Int(i32), + + /// BigInt provides a way to represent whole numbers larger than the largest number ECMAScript + /// can reliably represent with the `Number` primitive. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-bigint-value + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Numeric_literals + BigInt(Box), + + /// The Boolean type has two literal values: `true` and `false`. + /// + /// The Boolean object is a wrapper around the primitive Boolean data type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-boolean-value + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Boolean_literals + Bool(bool), + + /// In JavaScript, `null` is marked as one of the primitive values, cause it's behaviour is seemingly primitive. + /// + /// In computer science, a null value represents a reference that points, + /// generally intentionally, to a nonexistent or invalid object or address. + /// The meaning of a null reference varies among language implementations. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-null-value + /// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/null + Null, +} + +impl From for Literal { + #[inline] + fn from(string: Sym) -> Self { + Self::String(string) + } +} + +impl From for Literal { + #[inline] + fn from(num: f64) -> Self { + Self::Num(num) + } +} + +impl From for Literal { + #[inline] + fn from(i: i32) -> Self { + Self::Int(i) + } +} + +impl From for Literal { + #[inline] + fn from(i: BigInt) -> Self { + Self::BigInt(Box::new(i)) + } +} + +impl From> for Literal { + #[inline] + fn from(i: Box) -> Self { + Self::BigInt(i) + } +} + +impl From for Literal { + #[inline] + fn from(b: bool) -> Self { + Self::Bool(b) + } +} + +impl From for Expression { + #[inline] + fn from(lit: Literal) -> Self { + Self::Literal(lit) + } +} + +impl ToInternedString for Literal { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + match *self { + Self::String(st) => { + format!("\"{}\"", interner.resolve_expect(st)) + } + Self::Num(num) => num.to_string(), + Self::Int(num) => num.to_string(), + Self::BigInt(ref num) => num.to_string(), + Self::Bool(v) => v.to_string(), + Self::Null => "null".to_owned(), + } + } +} + +impl VisitWith for Literal { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + if let Self::String(sym) = self { + visitor.visit_sym(sym) + } else { + ControlFlow::Continue(()) + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + if let Self::String(sym) = self { + visitor.visit_sym_mut(sym) + } else { + ControlFlow::Continue(()) + } + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/literal/object.rs b/javascript-engine/external/boa/boa_ast/src/expression/literal/object.rs new file mode 100644 index 0000000..f9356ec --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/literal/object.rs @@ -0,0 +1,326 @@ +//! Object Expression. + +use crate::{ + block_to_string, + expression::{operator::assign::AssignTarget, Expression, RESERVED_IDENTIFIERS_STRICT}, + function::Function, + join_nodes, + pattern::{ObjectPattern, ObjectPatternElement}, + property::{MethodDefinition, PropertyDefinition, PropertyName}, + try_break, + visitor::{VisitWith, Visitor, VisitorMut}, +}; +use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString}; +use core::ops::ControlFlow; + +/// Objects in ECMAScript may be defined as an unordered collection of related data, of +/// primitive or reference types, in the form of “key: value” pairs. +/// +/// Objects can be initialized using `new Object()`, `Object.create()`, or using the literal +/// notation. +/// +/// An object initializer is an expression that describes the initialization of an +/// [`Object`][object]. Objects consist of properties, which are used to describe an object. +/// Values of object properties can either contain [`primitive`][primitive] data types or other +/// objects. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-ObjectLiteral +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer +/// [object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object +/// [primitive]: https://developer.mozilla.org/en-US/docs/Glossary/primitive +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(transparent))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectLiteral { + properties: Box<[PropertyDefinition]>, +} + +impl ObjectLiteral { + /// Gets the object literal properties + #[inline] + #[must_use] + pub const fn properties(&self) -> &[PropertyDefinition] { + &self.properties + } + + /// Converts the object literal into an [`ObjectPattern`]. + #[must_use] + pub fn to_pattern(&self, strict: bool) -> Option { + let mut bindings = Vec::new(); + let mut excluded_keys = Vec::new(); + for (i, property) in self.properties.iter().enumerate() { + match property { + PropertyDefinition::IdentifierReference(ident) if strict && *ident == Sym::EVAL => { + return None + } + PropertyDefinition::IdentifierReference(ident) => { + if strict && RESERVED_IDENTIFIERS_STRICT.contains(&ident.sym()) { + return None; + } + + excluded_keys.push(*ident); + bindings.push(ObjectPatternElement::SingleName { + ident: *ident, + name: PropertyName::Literal(ident.sym()), + default_init: None, + }); + } + PropertyDefinition::Property(name, expr) => match (name, expr) { + (PropertyName::Literal(name), Expression::Identifier(ident)) + if *name == *ident => + { + if strict && *name == Sym::EVAL { + return None; + } + if strict && RESERVED_IDENTIFIERS_STRICT.contains(name) { + return None; + } + + excluded_keys.push(*ident); + bindings.push(ObjectPatternElement::SingleName { + ident: *ident, + name: PropertyName::Literal(*name), + default_init: None, + }); + } + (PropertyName::Literal(name), Expression::Identifier(ident)) => { + bindings.push(ObjectPatternElement::SingleName { + ident: *ident, + name: PropertyName::Literal(*name), + default_init: None, + }); + } + (PropertyName::Literal(name), Expression::ObjectLiteral(object)) => { + let pattern = object.to_pattern(strict)?.into(); + bindings.push(ObjectPatternElement::Pattern { + name: PropertyName::Literal(*name), + pattern, + default_init: None, + }); + } + (PropertyName::Literal(name), Expression::ArrayLiteral(array)) => { + let pattern = array.to_pattern(strict)?.into(); + bindings.push(ObjectPatternElement::Pattern { + name: PropertyName::Literal(*name), + pattern, + default_init: None, + }); + } + (_, Expression::Assign(assign)) => match assign.lhs() { + AssignTarget::Identifier(ident) => { + if let Some(name) = name.literal() { + if name == *ident { + if strict && name == Sym::EVAL { + return None; + } + if strict && RESERVED_IDENTIFIERS_STRICT.contains(&name) { + return None; + } + excluded_keys.push(*ident); + } + bindings.push(ObjectPatternElement::SingleName { + ident: *ident, + name: PropertyName::Literal(name), + default_init: Some(assign.rhs().clone()), + }); + } else { + return None; + } + } + AssignTarget::Pattern(pattern) => { + bindings.push(ObjectPatternElement::Pattern { + name: name.clone(), + pattern: pattern.clone(), + default_init: Some(assign.rhs().clone()), + }); + } + AssignTarget::Access(access) => { + bindings.push(ObjectPatternElement::AssignmentPropertyAccess { + name: name.clone(), + access: access.clone(), + default_init: Some(assign.rhs().clone()), + }); + } + }, + (_, Expression::PropertyAccess(access)) => { + bindings.push(ObjectPatternElement::AssignmentPropertyAccess { + name: name.clone(), + access: access.clone(), + default_init: None, + }); + } + (PropertyName::Computed(name), Expression::Identifier(ident)) => { + bindings.push(ObjectPatternElement::SingleName { + ident: *ident, + name: PropertyName::Computed(name.clone()), + default_init: None, + }); + } + _ => return None, + }, + PropertyDefinition::SpreadObject(spread) => { + match spread { + Expression::Identifier(ident) => { + bindings.push(ObjectPatternElement::RestProperty { + ident: *ident, + excluded_keys: excluded_keys.clone(), + }); + } + Expression::PropertyAccess(access) => { + bindings.push(ObjectPatternElement::AssignmentRestPropertyAccess { + access: access.clone(), + excluded_keys: excluded_keys.clone(), + }); + } + _ => return None, + } + if i + 1 != self.properties.len() { + return None; + } + } + PropertyDefinition::MethodDefinition(_, _) => return None, + PropertyDefinition::CoverInitializedName(ident, expr) => { + if strict && [Sym::EVAL, Sym::ARGUMENTS].contains(&ident.sym()) { + return None; + } + + bindings.push(ObjectPatternElement::SingleName { + ident: *ident, + name: PropertyName::Literal(ident.sym()), + default_init: Some(expr.clone()), + }); + } + } + } + + Some(ObjectPattern::new(bindings.into())) + } +} + +impl ToIndentedString for ObjectLiteral { + fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String { + let mut buf = "{\n".to_owned(); + let indentation = " ".repeat(indent_n + 1); + for property in self.properties().iter() { + buf.push_str(&match property { + PropertyDefinition::IdentifierReference(ident) => { + format!("{indentation}{},\n", interner.resolve_expect(ident.sym())) + } + PropertyDefinition::Property(key, value) => { + let value = if let Expression::Function(f) = value { + Function::new(None, f.parameters().clone(), f.body().clone()).into() + } else { + value.clone() + }; + + format!( + "{indentation}{}: {},\n", + key.to_interned_string(interner), + value.to_no_indent_string(interner, indent_n + 1) + ) + } + PropertyDefinition::SpreadObject(key) => { + format!("{indentation}...{},\n", key.to_interned_string(interner)) + } + PropertyDefinition::MethodDefinition(key, method) => { + format!( + "{indentation}{}{}({}) {},\n", + match &method { + MethodDefinition::Get(_) => "get ", + MethodDefinition::Set(_) => "set ", + _ => "", + }, + key.to_interned_string(interner), + match &method { + MethodDefinition::Get(expression) + | MethodDefinition::Set(expression) + | MethodDefinition::Ordinary(expression) => { + join_nodes(interner, expression.parameters().as_ref()) + } + MethodDefinition::Generator(expression) => { + join_nodes(interner, expression.parameters().as_ref()) + } + MethodDefinition::AsyncGenerator(expression) => { + join_nodes(interner, expression.parameters().as_ref()) + } + MethodDefinition::Async(expression) => { + join_nodes(interner, expression.parameters().as_ref()) + } + }, + match &method { + MethodDefinition::Get(expression) + | MethodDefinition::Set(expression) + | MethodDefinition::Ordinary(expression) => { + block_to_string(expression.body(), interner, indent_n + 1) + } + MethodDefinition::Generator(expression) => { + block_to_string(expression.body(), interner, indent_n + 1) + } + MethodDefinition::AsyncGenerator(expression) => { + block_to_string(expression.body(), interner, indent_n + 1) + } + MethodDefinition::Async(expression) => { + block_to_string(expression.body(), interner, indent_n + 1) + } + }, + ) + } + PropertyDefinition::CoverInitializedName(ident, expr) => { + format!( + "{indentation}{} = {},\n", + interner.resolve_expect(ident.sym()), + expr.to_no_indent_string(interner, indent_n + 1) + ) + } + }); + } + buf.push_str(&format!("{}}}", " ".repeat(indent_n))); + + buf + } +} + +impl From for ObjectLiteral +where + T: Into>, +{ + fn from(props: T) -> Self { + Self { + properties: props.into(), + } + } +} + +impl From for Expression { + #[inline] + fn from(obj: ObjectLiteral) -> Self { + Self::ObjectLiteral(obj) + } +} + +impl VisitWith for ObjectLiteral { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + for pd in self.properties.iter() { + try_break!(visitor.visit_property_definition(pd)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + for pd in self.properties.iter_mut() { + try_break!(visitor.visit_property_definition_mut(pd)); + } + ControlFlow::Continue(()) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/literal/template.rs b/javascript-engine/external/boa/boa_ast/src/expression/literal/template.rs new file mode 100644 index 0000000..e899c45 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/literal/template.rs @@ -0,0 +1,133 @@ +//! Template literal Expression. + +use core::ops::ControlFlow; +use std::borrow::Cow; + +use boa_interner::{Interner, Sym, ToInternedString}; + +use crate::{ + expression::Expression, + try_break, + visitor::{VisitWith, Visitor, VisitorMut}, + ToStringEscaped, +}; + +/// Template literals are string literals allowing embedded expressions. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals +/// [spec]: https://tc39.es/ecma262/#sec-template-literals +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct TemplateLiteral { + elements: Box<[TemplateElement]>, +} + +impl From for Expression { + #[inline] + fn from(tem: TemplateLiteral) -> Self { + Self::TemplateLiteral(tem) + } +} + +/// An element found within a [`TemplateLiteral`]. +/// +/// The [spec] doesn't define an element akin to `TemplateElement`. However, the AST defines this +/// node as the equivalent of the components found in a template literal. +/// +/// [spec]: https://tc39.es/ecma262/#sec-template-literals +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum TemplateElement { + /// A simple string. + String(Sym), + /// An expression that is evaluated and replaced by its string representation. + Expr(Expression), +} + +impl TemplateLiteral { + /// Creates a new `TemplateLiteral` from a list of [`TemplateElement`]s. + #[inline] + #[must_use] + pub fn new(elements: Box<[TemplateElement]>) -> Self { + Self { elements } + } + + /// Gets the element list of this `TemplateLiteral`. + #[must_use] + pub const fn elements(&self) -> &[TemplateElement] { + &self.elements + } +} + +impl ToInternedString for TemplateLiteral { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + let mut buf = "`".to_owned(); + + for elt in self.elements.iter() { + match elt { + TemplateElement::String(s) => buf.push_str(&interner.resolve_expect(*s).join( + Cow::Borrowed, + |utf16| Cow::Owned(utf16.to_string_escaped()), + true, + )), + TemplateElement::Expr(n) => { + buf.push_str(&format!("${{{}}}", n.to_interned_string(interner))); + } + } + } + buf.push('`'); + + buf + } +} + +impl VisitWith for TemplateLiteral { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + for element in self.elements.iter() { + try_break!(visitor.visit_template_element(element)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + for element in self.elements.iter_mut() { + try_break!(visitor.visit_template_element_mut(element)); + } + ControlFlow::Continue(()) + } +} + +impl VisitWith for TemplateElement { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::String(sym) => visitor.visit_sym(sym), + Self::Expr(expr) => visitor.visit_expression(expr), + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::String(sym) => visitor.visit_sym_mut(sym), + Self::Expr(expr) => visitor.visit_expression_mut(expr), + } + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/mod.rs b/javascript-engine/external/boa/boa_ast/src/expression/mod.rs new file mode 100644 index 0000000..282006e --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/mod.rs @@ -0,0 +1,332 @@ +//! The [`Expression`] Parse Node, as defined by the [spec]. +//! +//! ECMAScript expressions include: +//! - [Primary][primary] expressions (`this`, function expressions, literals). +//! - [Left hand side][lhs] expressions (accessors, `new` operator, `super`). +//! - [operator] expressions. +//! +//! [spec]: https://tc39.es/ecma262/#prod-Expression +//! [primary]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#primary_expressions +//! [lhs]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#left-hand-side_expressions + +use boa_interner::{Interner, ToIndentedString, ToInternedString}; +use core::ops::ControlFlow; + +use self::{ + access::PropertyAccess, + literal::{ArrayLiteral, Literal, ObjectLiteral, TemplateLiteral}, + operator::{Assign, Binary, Conditional, Unary}, +}; + +use super::{ + function::{ArrowFunction, AsyncFunction, AsyncGenerator, Class, Function, Generator}, + function::{AsyncArrowFunction, FormalParameterList}, + Statement, +}; + +mod r#await; +mod call; +mod identifier; +mod new; +mod optional; +mod spread; +mod tagged_template; +mod r#yield; + +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +pub use call::{Call, SuperCall}; +pub use identifier::{Identifier, RESERVED_IDENTIFIERS_STRICT}; +pub use new::New; +pub use optional::{Optional, OptionalOperation, OptionalOperationKind}; +pub use r#await::Await; +pub use r#yield::Yield; +pub use spread::Spread; +pub use tagged_template::TaggedTemplate; + +pub mod access; +pub mod literal; +pub mod operator; + +/// The `Expression` Parse Node. +/// +/// See the [module level documentation][self] for more information. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, PartialEq)] +pub enum Expression { + /// The ECMAScript `this` keyword refers to the object it belongs to. + /// + /// A property of an execution context (global, function or eval) that, + /// in non–strict mode, is always a reference to an object and in strict + /// mode can be any value. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-this-keyword + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this + This, + + /// See [`Identifier`]. + Identifier(Identifier), + + /// See [`Literal`]. + Literal(Literal), + + /// See [`ArrayLiteral`]. + ArrayLiteral(ArrayLiteral), + + /// See [`ObjectLiteral`]. + ObjectLiteral(ObjectLiteral), + + /// See [`Spread`], + Spread(Spread), + + /// See [`Function`]. + Function(Function), + + /// See [`ArrowFunction`]. + ArrowFunction(ArrowFunction), + + /// See [`AsyncArrowFunction`]. + AsyncArrowFunction(AsyncArrowFunction), + + /// See [`Generator`]. + Generator(Generator), + + /// See [`AsyncFunction`]. + AsyncFunction(AsyncFunction), + + /// See [`AsyncGenerator`]. + AsyncGenerator(AsyncGenerator), + + /// See [`Class`]. + Class(Box), + + // TODO: Extract regexp literal Expression + // RegExpLiteral, + /// See [`TemplateLiteral`]. + TemplateLiteral(TemplateLiteral), + + /// See [`PropertyAccess`]. + PropertyAccess(PropertyAccess), + + /// See [`New`]. + New(New), + + /// See [`Call`]. + Call(Call), + + /// See [`SuperCall`]. + SuperCall(SuperCall), + + /// See [`Optional`]. + Optional(Optional), + + // TODO: Import calls + /// See [`TaggedTemplate`]. + TaggedTemplate(TaggedTemplate), + + /// The `new.target` pseudo-property expression. + NewTarget, + + // TODO: import.meta + /// See [`Assign`]. + Assign(Assign), + + /// See [`Unary`]. + Unary(Unary), + + /// See [`Binary`]. + Binary(Binary), + + /// See [`Conditional`]. + Conditional(Conditional), + + /// See [`Await`]. + Await(Await), + + /// See [`Yield`]. + Yield(Yield), + + /// A FormalParameterList. + /// + /// This is only used in the parser itself. + /// It is not a valid expression node. + #[doc(hidden)] + FormalParameterList(FormalParameterList), +} + +impl Expression { + /// Implements the display formatting with indentation. + /// + /// This will not prefix the value with any indentation. If you want to prefix this with proper + /// indents, use [`to_indented_string()`](Self::to_indented_string). + pub(crate) fn to_no_indent_string(&self, interner: &Interner, indentation: usize) -> String { + match self { + Self::This => "this".to_owned(), + Self::Identifier(id) => id.to_interned_string(interner), + Self::Literal(lit) => lit.to_interned_string(interner), + Self::ArrayLiteral(arr) => arr.to_interned_string(interner), + Self::ObjectLiteral(o) => o.to_indented_string(interner, indentation), + Self::Spread(sp) => sp.to_interned_string(interner), + Self::Function(f) => f.to_indented_string(interner, indentation), + Self::AsyncArrowFunction(f) => f.to_indented_string(interner, indentation), + Self::ArrowFunction(arrf) => arrf.to_indented_string(interner, indentation), + Self::Class(cl) => cl.to_indented_string(interner, indentation), + Self::Generator(gen) => gen.to_indented_string(interner, indentation), + Self::AsyncFunction(asf) => asf.to_indented_string(interner, indentation), + Self::AsyncGenerator(asgen) => asgen.to_indented_string(interner, indentation), + Self::TemplateLiteral(tem) => tem.to_interned_string(interner), + Self::PropertyAccess(prop) => prop.to_interned_string(interner), + Self::New(new) => new.to_interned_string(interner), + Self::Call(call) => call.to_interned_string(interner), + Self::SuperCall(supc) => supc.to_interned_string(interner), + Self::Optional(opt) => opt.to_interned_string(interner), + Self::NewTarget => "new.target".to_owned(), + Self::TaggedTemplate(tag) => tag.to_interned_string(interner), + Self::Assign(assign) => assign.to_interned_string(interner), + Self::Unary(unary) => unary.to_interned_string(interner), + Self::Binary(bin) => bin.to_interned_string(interner), + Self::Conditional(cond) => cond.to_interned_string(interner), + Self::Await(aw) => aw.to_interned_string(interner), + Self::Yield(yi) => yi.to_interned_string(interner), + Self::FormalParameterList(_) => unreachable!(), + } + } + + /// Returns if the expression is a function definition according to the spec. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-isfunctiondefinition + #[must_use] + #[inline] + pub const fn is_function_definition(&self) -> bool { + matches!( + self, + Self::ArrowFunction(_) + | Self::AsyncArrowFunction(_) + | Self::Function(_) + | Self::Generator(_) + | Self::AsyncGenerator(_) + | Self::AsyncFunction(_) + | Self::Class(_) + ) + } + + /// Returns if the expression is a function definition without a name. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-isanonymousfunctiondefinition + #[must_use] + #[inline] + pub const fn is_anonymous_function_definition(&self) -> bool { + match self { + Self::ArrowFunction(f) => f.name().is_none(), + Self::AsyncArrowFunction(f) => f.name().is_none(), + Self::Function(f) => f.name().is_none(), + Self::Generator(f) => f.name().is_none(), + Self::AsyncGenerator(f) => f.name().is_none(), + Self::AsyncFunction(f) => f.name().is_none(), + Self::Class(f) => f.name().is_none(), + _ => false, + } + } +} + +impl From for Statement { + #[inline] + fn from(expr: Expression) -> Self { + Self::Expression(expr) + } +} + +impl ToIndentedString for Expression { + #[inline] + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + self.to_no_indent_string(interner, indentation) + } +} + +impl VisitWith for Expression { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::Identifier(id) => visitor.visit_identifier(id), + Self::Literal(lit) => visitor.visit_literal(lit), + Self::ArrayLiteral(arlit) => visitor.visit_array_literal(arlit), + Self::ObjectLiteral(olit) => visitor.visit_object_literal(olit), + Self::Spread(sp) => visitor.visit_spread(sp), + Self::Function(f) => visitor.visit_function(f), + Self::ArrowFunction(af) => visitor.visit_arrow_function(af), + Self::AsyncArrowFunction(af) => visitor.visit_async_arrow_function(af), + Self::Generator(g) => visitor.visit_generator(g), + Self::AsyncFunction(af) => visitor.visit_async_function(af), + Self::AsyncGenerator(ag) => visitor.visit_async_generator(ag), + Self::Class(c) => visitor.visit_class(c), + Self::TemplateLiteral(tlit) => visitor.visit_template_literal(tlit), + Self::PropertyAccess(pa) => visitor.visit_property_access(pa), + Self::New(n) => visitor.visit_new(n), + Self::Call(c) => visitor.visit_call(c), + Self::SuperCall(sc) => visitor.visit_super_call(sc), + Self::Optional(opt) => visitor.visit_optional(opt), + Self::TaggedTemplate(tt) => visitor.visit_tagged_template(tt), + Self::Assign(a) => visitor.visit_assign(a), + Self::Unary(u) => visitor.visit_unary(u), + Self::Binary(b) => visitor.visit_binary(b), + Self::Conditional(c) => visitor.visit_conditional(c), + Self::Await(a) => visitor.visit_await(a), + Self::Yield(y) => visitor.visit_yield(y), + Self::FormalParameterList(fpl) => visitor.visit_formal_parameter_list(fpl), + Self::This | Self::NewTarget => { + // do nothing; can be handled as special case by visitor + ControlFlow::Continue(()) + } + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::Identifier(id) => visitor.visit_identifier_mut(id), + Self::Literal(lit) => visitor.visit_literal_mut(lit), + Self::ArrayLiteral(arlit) => visitor.visit_array_literal_mut(arlit), + Self::ObjectLiteral(olit) => visitor.visit_object_literal_mut(olit), + Self::Spread(sp) => visitor.visit_spread_mut(sp), + Self::Function(f) => visitor.visit_function_mut(f), + Self::ArrowFunction(af) => visitor.visit_arrow_function_mut(af), + Self::AsyncArrowFunction(af) => visitor.visit_async_arrow_function_mut(af), + Self::Generator(g) => visitor.visit_generator_mut(g), + Self::AsyncFunction(af) => visitor.visit_async_function_mut(af), + Self::AsyncGenerator(ag) => visitor.visit_async_generator_mut(ag), + Self::Class(c) => visitor.visit_class_mut(c), + Self::TemplateLiteral(tlit) => visitor.visit_template_literal_mut(tlit), + Self::PropertyAccess(pa) => visitor.visit_property_access_mut(pa), + Self::New(n) => visitor.visit_new_mut(n), + Self::Call(c) => visitor.visit_call_mut(c), + Self::SuperCall(sc) => visitor.visit_super_call_mut(sc), + Self::Optional(opt) => visitor.visit_optional_mut(opt), + Self::TaggedTemplate(tt) => visitor.visit_tagged_template_mut(tt), + Self::Assign(a) => visitor.visit_assign_mut(a), + Self::Unary(u) => visitor.visit_unary_mut(u), + Self::Binary(b) => visitor.visit_binary_mut(b), + Self::Conditional(c) => visitor.visit_conditional_mut(c), + Self::Await(a) => visitor.visit_await_mut(a), + Self::Yield(y) => visitor.visit_yield_mut(y), + Self::FormalParameterList(fpl) => visitor.visit_formal_parameter_list_mut(fpl), + Self::This | Self::NewTarget => { + // do nothing; can be handled as special case by visitor + ControlFlow::Continue(()) + } + } + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/new.rs b/javascript-engine/external/boa/boa_ast/src/expression/new.rs new file mode 100644 index 0000000..4b77e83 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/new.rs @@ -0,0 +1,87 @@ +use crate::expression::Call; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use boa_interner::{Interner, ToInternedString}; +use core::ops::ControlFlow; + +use super::Expression; + +/// The `new` operator lets developers create an instance of a user-defined object type or of +/// one of the built-in object types that has a constructor function. +/// +/// The new keyword does the following things: +/// - Creates a blank, plain JavaScript object; +/// - Links (sets the constructor of) this object to another object; +/// - Passes the newly created object from Step 1 as the this context; +/// - Returns this if the function doesn't return its own object. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-NewExpression +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct New { + call: Call, +} + +impl New { + /// Gets the constructor of the new expression. + #[inline] + #[must_use] + pub const fn constructor(&self) -> &Expression { + self.call.function() + } + + /// Retrieves the arguments passed to the constructor. + #[inline] + #[must_use] + pub const fn arguments(&self) -> &[Expression] { + self.call.args() + } + + /// Returns the inner call expression. + #[must_use] + pub const fn call(&self) -> &Call { + &self.call + } +} + +impl From for New { + #[inline] + fn from(call: Call) -> Self { + Self { call } + } +} + +impl ToInternedString for New { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + format!("new {}", self.call.to_interned_string(interner)) + } +} + +impl From for Expression { + #[inline] + fn from(new: New) -> Self { + Self::New(new) + } +} + +impl VisitWith for New { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + visitor.visit_call(&self.call) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + visitor.visit_call_mut(&mut self.call) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/operator/assign/mod.rs b/javascript-engine/external/boa/boa_ast/src/expression/operator/assign/mod.rs new file mode 100644 index 0000000..095c586 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/operator/assign/mod.rs @@ -0,0 +1,191 @@ +//! Assignment expression nodes, as defined by the [spec]. +//! +//! An [assignment operator][mdn] assigns a value to its left operand based on the value of its right +//! operand. Almost any [`LeftHandSideExpression`][lhs] Parse Node can be the target of a simple +//! assignment expression (`=`). However, the compound assignment operations such as `%=` or `??=` +//! only allow ["simple"][simple] left hand side expressions as an assignment target. +//! +//! [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators +//! [lhs]: https://tc39.es/ecma262/#prod-LeftHandSideExpression +//! [simple]: https://tc39.es/ecma262/#sec-static-semantics-assignmenttargettype + +mod op; + +use core::ops::ControlFlow; +pub use op::*; + +use boa_interner::{Interner, ToInternedString}; + +use crate::{ + expression::{access::PropertyAccess, identifier::Identifier, Expression}, + pattern::Pattern, + try_break, + visitor::{VisitWith, Visitor, VisitorMut}, +}; + +/// An assignment operator expression. +/// +/// See the [module level documentation][self] for more information. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Assign { + op: AssignOp, + lhs: Box, + rhs: Box, +} + +impl Assign { + /// Creates an `Assign` AST Expression. + #[inline] + #[must_use] + pub fn new(op: AssignOp, lhs: AssignTarget, rhs: Expression) -> Self { + Self { + op, + lhs: Box::new(lhs), + rhs: Box::new(rhs), + } + } + + /// Gets the operator of the assignment operation. + #[inline] + #[must_use] + pub const fn op(&self) -> AssignOp { + self.op + } + + /// Gets the left hand side of the assignment operation. + #[inline] + #[must_use] + pub const fn lhs(&self) -> &AssignTarget { + &self.lhs + } + + /// Gets the right hand side of the assignment operation. + #[inline] + #[must_use] + pub const fn rhs(&self) -> &Expression { + &self.rhs + } +} + +impl ToInternedString for Assign { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + format!( + "{} {} {}", + self.lhs.to_interned_string(interner), + self.op, + self.rhs.to_interned_string(interner) + ) + } +} + +impl From for Expression { + #[inline] + fn from(op: Assign) -> Self { + Self::Assign(op) + } +} + +impl VisitWith for Assign { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_assign_target(&self.lhs)); + visitor.visit_expression(&self.rhs) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_assign_target_mut(&mut self.lhs)); + visitor.visit_expression_mut(&mut self.rhs) + } +} + +/// The valid left-hand-side expressions of an assignment operator. Also called +/// [`LeftHandSideExpression`][spec] in the spec. +/// +/// [spec]: hhttps://tc39.es/ecma262/#prod-LeftHandSideExpression +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum AssignTarget { + /// A simple identifier, such as `a`. + Identifier(Identifier), + /// A property access, such as `a.prop`. + Access(PropertyAccess), + /// A pattern assignment, such as `{a, b, ...c}`. + Pattern(Pattern), +} + +impl AssignTarget { + /// Converts the left-hand-side Expression of an assignment expression into an [`AssignTarget`]. + /// Returns `None` if the given Expression is an invalid left-hand-side for a assignment expression. + #[must_use] + pub fn from_expression( + expression: &Expression, + strict: bool, + destructure: bool, + ) -> Option { + match expression { + Expression::Identifier(id) => Some(Self::Identifier(*id)), + Expression::PropertyAccess(access) => Some(Self::Access(access.clone())), + Expression::ObjectLiteral(object) if destructure => { + let pattern = object.to_pattern(strict)?; + Some(Self::Pattern(pattern.into())) + } + Expression::ArrayLiteral(array) if destructure => { + let pattern = array.to_pattern(strict)?; + Some(Self::Pattern(pattern.into())) + } + _ => None, + } + } +} + +impl ToInternedString for AssignTarget { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + match self { + Self::Identifier(id) => id.to_interned_string(interner), + Self::Access(access) => access.to_interned_string(interner), + Self::Pattern(pattern) => pattern.to_interned_string(interner), + } + } +} + +impl From for AssignTarget { + #[inline] + fn from(target: Identifier) -> Self { + Self::Identifier(target) + } +} + +impl VisitWith for AssignTarget { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::Identifier(id) => visitor.visit_identifier(id), + Self::Access(pa) => visitor.visit_property_access(pa), + Self::Pattern(pat) => visitor.visit_pattern(pat), + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::Identifier(id) => visitor.visit_identifier_mut(id), + Self::Access(pa) => visitor.visit_property_access_mut(pa), + Self::Pattern(pat) => visitor.visit_pattern_mut(pat), + } + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/operator/assign/op.rs b/javascript-engine/external/boa/boa_ast/src/expression/operator/assign/op.rs new file mode 100644 index 0000000..e574b1e --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/operator/assign/op.rs @@ -0,0 +1,244 @@ +/// An assignment operator assigns a value to its left operand based on the value of its right operand. +/// +/// The simple assignment operator is equal (`=`), which assigns the value of its right operand to its +/// left operand. That is, `x = y` assigns the value of `y to x`. +/// +/// There are also compound assignment operators that are shorthand for the operations +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Assignment +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum AssignOp { + /// The assignment operator assigns the value of the right operand to the left operand. + /// + /// Syntax: `x = y` + + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment + Assign, + /// The addition assignment operator adds the value of the right operand to a variable and assigns the result to the variable. + /// + /// Syntax: `x += y` + /// + /// The types of the two operands determine the behavior of the addition assignment operator. Addition or concatenation is possible. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Addition_assignment + Add, + + /// The subtraction assignment operator subtracts the value of the right operand from a variable and assigns the result to the variable. + /// + /// Syntax: `x -= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Subtraction_assignment + Sub, + + /// The multiplication assignment operator multiplies a variable by the value of the right operand and assigns the result to the variable. + /// + /// Syntax: `x *= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Multiplication_assignment + Mul, + + /// The division assignment operator divides a variable by the value of the right operand and assigns the result to the variable. + /// + /// Syntax: `x /= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Division_assignment + Div, + + /// The remainder assignment operator divides a variable by the value of the right operand and assigns the remainder to the variable. + /// + /// Syntax: `x %= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Remainder_assignment + Mod, + + /// The exponentiation assignment operator raises the value of a variable to the power of the right operand. + /// + /// Syntax: `x ** y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Exponentiation_assignment + Exp, + + /// The bitwise AND assignment operator uses the binary representation of both operands, does a bitwise AND operation on + /// them and assigns the result to the variable. + /// + /// Syntax: `x &= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Bitwise_AND_assignment + And, + + /// The bitwise OR assignment operator uses the binary representation of both operands, does a bitwise OR operation on + /// them and assigns the result to the variable. + /// + /// Syntax: `x |= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Bitwise_OR_assignment + Or, + + /// The bitwise XOR assignment operator uses the binary representation of both operands, does a bitwise XOR operation on + /// them and assigns the result to the variable. + /// + /// Syntax: `x ^= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Bitwise_XOR_assignment + Xor, + + /// The left shift assignment operator moves the specified amount of bits to the left and assigns the result to the variable. + /// + /// Syntax: `x <<= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Left_shift_assignment + Shl, + + /// The right shift assignment operator moves the specified amount of bits to the right and assigns the result to the variable. + /// + /// Syntax: `x >>= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Right_shift_assignment + Shr, + + /// The unsigned right shift assignment operator moves the specified amount of bits to the right and assigns the result to the variable. + /// + /// Syntax: `x >>>= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unsigned_right_shift_assignment + Ushr, + + /// The logical and assignment operator only assigns if the target variable is truthy. + /// + /// Syntax: `x &&= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND_assignment + BoolAnd, + + /// The logical or assignment operator only assigns if the target variable is falsy. + /// + /// Syntax: `x ||= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR_assignment + BoolOr, + + /// The logical nullish assignment operator only assigns if the target variable is nullish (null or undefined). + /// + /// Syntax: `x ??= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_nullish_assignment + Coalesce, +} + +impl AssignOp { + /// Retrieves the operation as a static string. + const fn as_str(self) -> &'static str { + match self { + Self::Assign => "=", + Self::Add => "+=", + Self::Sub => "-=", + Self::Mul => "*=", + Self::Exp => "**=", + Self::Div => "/=", + Self::Mod => "%=", + Self::And => "&=", + Self::Or => "|=", + Self::Xor => "^=", + Self::Shl => "<<=", + Self::Shr => ">>=", + Self::Ushr => ">>>=", + Self::BoolAnd => "&&=", + Self::BoolOr => "||=", + Self::Coalesce => "??=", + } + } +} + +impl std::fmt::Display for AssignOp { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.as_str()) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/operator/binary/mod.rs b/javascript-engine/external/boa/boa_ast/src/expression/operator/binary/mod.rs new file mode 100644 index 0000000..a9e91cd --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/operator/binary/mod.rs @@ -0,0 +1,111 @@ +//! Binary expression nodes. +//! +//! A Binary expression comprises any operation between two expressions (excluding assignments), +//! such as: +//! - [Logic operations][logic] (`||`, `&&`). +//! - [Relational math][relat] (`==`, `<`). +//! - [Bit manipulation][bit] (`^`, `|`). +//! - [Arithmetic][arith] (`+`, `%`). +//! - The [comma operator][comma] (`,`) +//! +//! [logic]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#binary_logical_operators +//! [relat]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#relational_operators +//! [bit]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#binary_bitwise_operators +//! [arith]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#arithmetic_operators +//! [comma]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator + +mod op; + +use core::ops::ControlFlow; +pub use op::*; + +use boa_interner::{Interner, ToInternedString}; + +use crate::{ + expression::Expression, + try_break, + visitor::{VisitWith, Visitor, VisitorMut}, +}; + +/// Binary operations require two operands, one before the operator and one after the operator. +/// +/// See the [module level documentation][self] for more information. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Binary { + op: BinaryOp, + lhs: Box, + rhs: Box, +} + +impl Binary { + /// Creates a `BinOp` AST Expression. + #[inline] + #[must_use] + pub fn new(op: BinaryOp, lhs: Expression, rhs: Expression) -> Self { + Self { + op, + lhs: Box::new(lhs), + rhs: Box::new(rhs), + } + } + + /// Gets the binary operation of the Expression. + #[inline] + #[must_use] + pub const fn op(&self) -> BinaryOp { + self.op + } + + /// Gets the left hand side of the binary operation. + #[inline] + #[must_use] + pub const fn lhs(&self) -> &Expression { + &self.lhs + } + + /// Gets the right hand side of the binary operation. + #[inline] + #[must_use] + pub const fn rhs(&self) -> &Expression { + &self.rhs + } +} + +impl ToInternedString for Binary { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + format!( + "{} {} {}", + self.lhs.to_interned_string(interner), + self.op, + self.rhs.to_interned_string(interner) + ) + } +} + +impl From for Expression { + #[inline] + fn from(op: Binary) -> Self { + Self::Binary(op) + } +} + +impl VisitWith for Binary { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_expression(&self.lhs)); + visitor.visit_expression(&self.rhs) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_expression_mut(&mut self.lhs)); + visitor.visit_expression_mut(&mut self.rhs) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/operator/binary/op.rs b/javascript-engine/external/boa/boa_ast/src/expression/operator/binary/op.rs new file mode 100644 index 0000000..9bba78e --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/operator/binary/op.rs @@ -0,0 +1,578 @@ +//! This module implements various structure for logic handling. + +use std::fmt::{Display, Formatter, Result}; + +/// This represents a binary operation between two values. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum BinaryOp { + /// Numeric operation. + /// + /// see: [`NumOp`](enum.NumOp.html) + Arithmetic(ArithmeticOp), + + /// Bitwise operation. + /// + /// see: [`BitOp`](enum.BitOp.html). + Bitwise(BitwiseOp), + + /// Comparative operation. + /// + /// see: [`CompOp`](enum.CompOp.html). + Relational(RelationalOp), + + /// Logical operation. + /// + /// see: [`LogOp`](enum.LogOp.html). + Logical(LogicalOp), + + /// Comma operation. + Comma, +} + +impl From for BinaryOp { + #[inline] + fn from(op: ArithmeticOp) -> Self { + Self::Arithmetic(op) + } +} + +impl From for BinaryOp { + #[inline] + fn from(op: BitwiseOp) -> Self { + Self::Bitwise(op) + } +} + +impl From for BinaryOp { + #[inline] + fn from(op: RelationalOp) -> Self { + Self::Relational(op) + } +} + +impl From for BinaryOp { + #[inline] + fn from(op: LogicalOp) -> Self { + Self::Logical(op) + } +} + +impl BinaryOp { + /// Retrieves the operation as a static string. + const fn as_str(self) -> &'static str { + match self { + Self::Arithmetic(ref op) => op.as_str(), + Self::Bitwise(ref op) => op.as_str(), + Self::Relational(ref op) => op.as_str(), + Self::Logical(ref op) => op.as_str(), + Self::Comma => ",", + } + } +} + +impl Display for BinaryOp { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.as_str()) + } +} + +/// Arithmetic operators take numerical values (either literals or variables) +/// as their operands and return a single numerical value. +/// +/// More information: +/// - [MDN documentation][mdn] +/// +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Arithmetic +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ArithmeticOp { + /// The addition operator produces the sum of numeric operands or string concatenation. + /// + /// Syntax: `x + y` + /// + /// More information: + /// - [ECMAScript reference][spec]. + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-addition-operator-plus + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Addition + Add, + + /// The subtraction operator subtracts the two operands, producing their difference. + /// + /// Syntax: `x - y` + /// + /// More information: + /// - [ECMAScript reference][spec]. + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-subtraction-operator-minus + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Subtraction + Sub, + + /// The division operator produces the quotient of its operands where the left operand + /// is the dividend and the right operand is the divisor. + /// + /// Syntax: `x / y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Division + Div, + + /// The multiplication operator produces the product of the operands. + /// + /// Syntax: `x * y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Multiplication + Mul, + + /// The exponentiation operator returns the result of raising the first operand to + /// the power of the second operand. + /// + /// Syntax: `x ** y` + /// + /// The exponentiation operator is right-associative. a ** b ** c is equal to a ** (b ** c). + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-exp-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Exponentiation + Exp, + + /// The remainder operator returns the remainder left over when one operand is divided by a second operand. + /// + /// Syntax: `x % y` + /// + /// The remainder operator always takes the sign of the dividend. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Remainder + Mod, +} + +impl ArithmeticOp { + /// Retrieves the operation as a static string. + const fn as_str(self) -> &'static str { + match self { + Self::Add => "+", + Self::Sub => "-", + Self::Div => "/", + Self::Mul => "*", + Self::Exp => "**", + Self::Mod => "%", + } + } +} + +impl Display for ArithmeticOp { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.as_str()) + } +} + +/// A bitwise operator is an operator used to perform bitwise operations +/// on bit patterns or binary numerals that involve the manipulation of individual bits. +/// +/// More information: +/// - [MDN documentation][mdn] +/// +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Bitwise +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum BitwiseOp { + /// Performs the AND operation on each pair of bits. a AND b yields 1 only if both a and b are 1. + /// + /// Syntax: `x & y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-BitwiseANDExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_AND + And, + + /// Performs the OR operation on each pair of bits. a OR b yields 1 if either a or b is 1. + /// + /// Syntax: `x | y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-BitwiseORExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_OR + Or, + + /// Performs the XOR operation on each pair of bits. a XOR b yields 1 if a and b are different. + /// + /// Syntax: `x ^ y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-BitwiseXORExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_XOR + Xor, + + /// This operator shifts the first operand the specified number of bits to the left. + /// + /// Syntax: `x << y` + /// + /// Excess bits shifted off to the left are discarded. Zero bits are shifted in from the right. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-left-shift-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Left_shift + Shl, + + /// This operator shifts the first operand the specified number of bits to the right. + /// + /// Syntax: `x >> y` + /// + /// Excess bits shifted off to the right are discarded. Copies of the leftmost bit + /// are shifted in from the left. Since the new leftmost bit has the same value as + /// the previous leftmost bit, the sign bit (the leftmost bit) does not change. + /// Hence the name "sign-propagating". + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-signed-right-shift-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Right_shift + Shr, + + /// This operator shifts the first operand the specified number of bits to the right. + /// + /// Syntax: `x >>> y` + /// + /// Excess bits shifted off to the right are discarded. Zero bits are shifted in + /// from the left. The sign bit becomes 0, so the result is always non-negative. + /// Unlike the other bitwise operators, zero-fill right shift returns an unsigned 32-bit integer. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-unsigned-right-shift-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Unsigned_right_shift + UShr, +} + +impl BitwiseOp { + /// Retrieves the operation as a static string. + const fn as_str(self) -> &'static str { + match self { + Self::And => "&", + Self::Or => "|", + Self::Xor => "^", + Self::Shl => "<<", + Self::Shr => ">>", + Self::UShr => ">>>", + } + } +} + +impl Display for BitwiseOp { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.as_str()) + } +} + +/// A relational operator compares its operands and returns a logical value based on whether the relation is true. +/// +/// The operands can be numerical, string, logical, or object values. Strings are compared based on standard +/// lexicographical ordering, using Unicode values. In most cases, if the two operands are not of the same type, +/// JavaScript attempts to convert them to an appropriate type for the comparison. This behavior generally results in +/// comparing the operands numerically. The sole exceptions to type conversion within comparisons involve the `===` and `!==` +/// operators, which perform strict equality and inequality comparisons. These operators do not attempt to convert the operands +/// to compatible types before checking equality. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: tc39.es/ecma262/#sec-testing-and-comparison-operations +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Comparison +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum RelationalOp { + /// The equality operator converts the operands if they are not of the same type, then applies + /// strict comparison. + /// + /// Syntax: `y == y` + /// + /// If both operands are objects, then JavaScript compares internal references which are equal + /// when operands refer to the same object in memory. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-abstract-equality-comparison + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Equality + Equal, + + /// The inequality operator returns `true` if the operands are not equal. + /// + /// Syntax: `x != y` + /// + /// If the two operands are not of the same type, JavaScript attempts to convert the operands + /// to an appropriate type for the comparison. If both operands are objects, then JavaScript + /// compares internal references which are not equal when operands refer to different objects + /// in memory. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-EqualityExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Inequality + NotEqual, + + /// The identity operator returns `true` if the operands are strictly equal **with no type + /// conversion**. + /// + /// Syntax: `x === y` + /// + /// Returns `true` if the operands are equal and of the same type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-strict-equality-comparison + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Identity + StrictEqual, + + /// The non-identity operator returns `true` if the operands **are not equal and/or not of the + /// same type**. + /// + /// Syntax: `x !== y` + /// + /// Returns `true` if the operands are of the same type but not equal, or are of different type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-EqualityExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Nonidentity> + StrictNotEqual, + + /// The greater than operator returns `true` if the left operand is greater than the right + /// operand. + /// + /// Syntax: `x > y` + /// + /// Returns `true` if the left operand is greater than the right operand. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Greater_than_operator + GreaterThan, + + /// The greater than or equal operator returns `true` if the left operand is greater than or + /// equal to the right operand. + /// + /// Syntax: `x >= y` + /// + /// Returns `true` if the left operand is greater than the right operand. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Greater_than_operator + GreaterThanOrEqual, + + /// The less than operator returns `true` if the left operand is less than the right operand. + /// + /// Syntax: `x < y` + /// + /// Returns `true` if the left operand is less than the right operand. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Less_than_operator + LessThan, + + /// The less than or equal operator returns `true` if the left operand is less than or equal to + /// the right operand. + /// + /// Syntax: `x <= y` + /// + /// Returns `true` if the left operand is less than or equal to the right operand. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Less_than_or_equal_operator + LessThanOrEqual, + + /// The `in` operator returns `true` if the specified property is in the specified object or + /// its prototype chain. + /// + /// Syntax: `prop in object` + /// + /// Returns `true` the specified property is in the specified object or its prototype chain. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in + In, + + /// The `instanceof` operator returns `true` if the specified object is an instance of the + /// right hand side object. + /// + /// Syntax: `obj instanceof Object` + /// + /// Returns `true` the `prototype` property of the right hand side constructor appears anywhere + /// in the prototype chain of the object. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof + InstanceOf, +} + +impl RelationalOp { + /// Retrieves the operation as a static string. + const fn as_str(self) -> &'static str { + match self { + Self::Equal => "==", + Self::NotEqual => "!=", + Self::StrictEqual => "===", + Self::StrictNotEqual => "!==", + Self::GreaterThan => ">", + Self::GreaterThanOrEqual => ">=", + Self::LessThan => "<", + Self::LessThanOrEqual => "<=", + Self::In => "in", + Self::InstanceOf => "instanceof", + } + } +} + +impl Display for RelationalOp { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.as_str()) + } +} + +/// Logical operators are typically used with Boolean (logical) values; when they are, they return a Boolean value. +/// +/// However, the `&&` and `||` operators actually return the value of one of the specified operands, +/// so if these operators are used with non-Boolean values, they may return a non-Boolean value. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#sec-binary-logical-operators +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Logical +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum LogicalOp { + /// The logical AND operator returns the value of the first operand if it can be coerced into `false`; + /// otherwise, it returns the second operand. + /// + /// Syntax: `x && y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-LogicalANDExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_AND + And, + + /// The logical OR operator returns the value the first operand if it can be coerced into `true`; + /// otherwise, it returns the second operand. + /// + /// Syntax: `x || y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-LogicalORExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_OR + Or, + + /// The nullish coalescing operator is a logical operator that returns the second operand + /// when its first operand is null or undefined, and otherwise returns its first operand. + /// + /// Syntax: `x ?? y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-CoalesceExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator + Coalesce, +} + +impl LogicalOp { + /// Retrieves the operation as a static string. + const fn as_str(self) -> &'static str { + match self { + Self::And => "&&", + Self::Or => "||", + Self::Coalesce => "??", + } + } +} + +impl Display for LogicalOp { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.as_str()) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/operator/conditional.rs b/javascript-engine/external/boa/boa_ast/src/expression/operator/conditional.rs new file mode 100644 index 0000000..8ecca55 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/operator/conditional.rs @@ -0,0 +1,103 @@ +use crate::{ + expression::Expression, + try_break, + visitor::{VisitWith, Visitor, VisitorMut}, +}; +use boa_interner::{Interner, ToInternedString}; +use core::ops::ControlFlow; + +/// The `conditional` (ternary) operation is the only ECMAScript operation that takes three +/// operands. +/// +/// This operation takes three operands: a condition followed by a question mark (`?`), +/// then an expression to execute `if` the condition is truthy followed by a colon (`:`), +/// and finally the expression to execute if the condition is `false`. +/// This operator is frequently used as a shortcut for the `if` statement. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-ConditionalExpression +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Conditional { + condition: Box, + if_true: Box, + if_false: Box, +} + +impl Conditional { + /// Gets the condition of the `Conditional` expression. + #[inline] + #[must_use] + pub const fn condition(&self) -> &Expression { + &self.condition + } + + /// Gets the expression returned if `condition` is truthy. + #[inline] + #[must_use] + pub const fn if_true(&self) -> &Expression { + &self.if_true + } + + /// Gets the expression returned if `condition` is falsy. + #[inline] + #[must_use] + pub const fn if_false(&self) -> &Expression { + &self.if_false + } + + /// Creates a `Conditional` AST Expression. + #[inline] + #[must_use] + pub fn new(condition: Expression, if_true: Expression, if_false: Expression) -> Self { + Self { + condition: Box::new(condition), + if_true: Box::new(if_true), + if_false: Box::new(if_false), + } + } +} + +impl ToInternedString for Conditional { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + format!( + "{} ? {} : {}", + self.condition().to_interned_string(interner), + self.if_true().to_interned_string(interner), + self.if_false().to_interned_string(interner) + ) + } +} + +impl From for Expression { + #[inline] + fn from(cond_op: Conditional) -> Self { + Self::Conditional(cond_op) + } +} + +impl VisitWith for Conditional { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_expression(&self.condition)); + try_break!(visitor.visit_expression(&self.if_true)); + visitor.visit_expression(&self.if_false) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_expression_mut(&mut self.condition)); + try_break!(visitor.visit_expression_mut(&mut self.if_true)); + visitor.visit_expression_mut(&mut self.if_false) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/operator/mod.rs b/javascript-engine/external/boa/boa_ast/src/expression/operator/mod.rs new file mode 100644 index 0000000..2591935 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/operator/mod.rs @@ -0,0 +1,20 @@ +//! Operator expression nodes. +//! +//! An [operator][op] expression is an expression that takes an operator (such as `+`, `typeof`, `+=`) +//! and one or more expressions and returns an expression as a result. +//! An operator expression can be any of the following: +//! +//! - A [`Unary`] expression. +//! - An [`Assign`] expression. +//! - A [`Binary`] expression. +//! - A [`Conditional`] expression. +//! +//! [op]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators + +mod conditional; + +pub mod assign; +pub mod binary; +pub mod unary; + +pub use self::{assign::Assign, binary::Binary, conditional::Conditional, unary::Unary}; diff --git a/javascript-engine/external/boa/boa_ast/src/expression/operator/unary/mod.rs b/javascript-engine/external/boa/boa_ast/src/expression/operator/unary/mod.rs new file mode 100644 index 0000000..b286561 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/operator/unary/mod.rs @@ -0,0 +1,102 @@ +//! Unary expression nodes. +//! +//! A Binary expression comprises any operation applied to a single expression. Some examples include: +//! +//! - [Increment and decrement operations][inc] (`++`, `--`). +//! - The [`delete`][del] operator. +//! - The [bitwise NOT][not] operator (`~`). +//! +//! The full list of valid unary operators is defined in [`UnaryOp`]. +//! +//! [inc]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#increment_and_decrement +//! [del]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete +//! [not]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_NOT +mod op; + +use core::ops::ControlFlow; +pub use op::*; + +use boa_interner::{Interner, ToInternedString}; + +use crate::expression::Expression; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; + +/// A unary expression is an operation with only one operand. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary_operators +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Unary { + op: UnaryOp, + target: Box, +} + +impl Unary { + /// Creates a new `UnaryOp` AST Expression. + #[inline] + #[must_use] + pub fn new(op: UnaryOp, target: Expression) -> Self { + Self { + op, + target: Box::new(target), + } + } + + /// Gets the unary operation of the Expression. + #[inline] + #[must_use] + pub const fn op(&self) -> UnaryOp { + self.op + } + + /// Gets the target of this unary operator. + #[inline] + #[must_use] + pub fn target(&self) -> &Expression { + self.target.as_ref() + } +} + +impl ToInternedString for Unary { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + let space = match self.op { + UnaryOp::TypeOf | UnaryOp::Delete | UnaryOp::Void => " ", + _ => "", + }; + format!( + "{}{space}{}", + self.op, + self.target.to_interned_string(interner) + ) + } +} + +impl From for Expression { + #[inline] + fn from(op: Unary) -> Self { + Self::Unary(op) + } +} + +impl VisitWith for Unary { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + visitor.visit_expression(&self.target) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + visitor.visit_expression_mut(&mut self.target) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/operator/unary/op.rs b/javascript-engine/external/boa/boa_ast/src/expression/operator/unary/op.rs new file mode 100644 index 0000000..f4e2461 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/operator/unary/op.rs @@ -0,0 +1,216 @@ +/// A unary operator is one that takes a single operand/argument and performs an operation. +/// +/// A unary operation is an operation with only one operand. This operand comes either +/// before or after the operator. Unary operators are more efficient than standard JavaScript +/// function calls. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum UnaryOp { + /// The increment operator increments (adds one to) its operand and returns a value. + /// + /// Syntax: `++x` + /// + /// This operator increments and returns the value after incrementing. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-postfix-increment-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Increment + IncrementPost, + + /// The increment operator increments (adds one to) its operand and returns a value. + /// + /// Syntax: `x++` + /// + /// This operator increments and returns the value before incrementing. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-prefix-increment-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Increment + IncrementPre, + + /// The decrement operator decrements (subtracts one from) its operand and returns a value. + /// + /// Syntax: `--x` + /// + /// This operator decrements and returns the value before decrementing. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-postfix-decrement-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Decrement + DecrementPost, + + /// The decrement operator decrements (subtracts one from) its operand and returns a value. + /// + /// Syntax: `x--` + /// + /// This operator decrements the operand and returns the value after decrementing. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-prefix-decrement-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Decrement + DecrementPre, + + /// The unary negation operator precedes its operand and negates it. + /// + /// Syntax: `-x` + /// + /// Converts non-numbers data types to numbers like unary plus, + /// however, it performs an additional operation, negation. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-unary-minus-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Unary_negation + Minus, + + /// The unary plus operator attempts to convert the operand into a number, if it isn't already. + /// + /// Syntax: `+x` + /// + /// Although unary negation (`-`) also can convert non-numbers, unary plus is the fastest and preferred + /// way of converting something into a number, because it does not perform any other operations on the number. + /// It can convert `string` representations of integers and floats, as well as the non-string values `true`, `false`, and `null`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-unary-plus-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Unary_plus + Plus, + + /// Returns `false` if its single operand can be converted to `true`; otherwise, returns `true`. + /// + /// Syntax: `!x` + /// + /// Boolean values simply get inverted: `!true === false` and `!false === true`. + /// Non-boolean values get converted to boolean values first, then are negated. + /// This means that it is possible to use a couple of NOT operators in series to explicitly + /// force the conversion of any value to the corresponding boolean primitive. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-logical-not-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_NOT + Not, + + /// Performs the NOT operator on each bit. + /// + /// Syntax: `~x` + /// + /// NOT `a` yields the inverted value (or one's complement) of `a`. + /// Bitwise NOTing any number x yields -(x + 1). For example, ~-5 yields 4. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-bitwise-not-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_NOT + Tilde, + + /// The `typeof` operator returns a string indicating the type of the unevaluated operand. + /// + /// Syntax: `typeof x` or `typeof(x)` + /// + /// The `typeof` is a JavaScript keyword that will return the type of a variable when you call it. + /// You can use this to validate function parameters or check if variables are defined. + /// There are other uses as well. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-typeof-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof + TypeOf, + + /// The JavaScript `delete` operator removes a property from an object. + /// + /// Syntax: `delete x` + /// + /// Unlike what common belief suggests, the delete operator has nothing to do with + /// directly freeing memory. Memory management is done indirectly via breaking references. + /// If no more references to the same property are held, it is eventually released automatically. + /// + /// The `delete` operator returns `true` for all cases except when the property is an + /// [own](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) + /// [non-configurable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cant_delete) + /// property, in which case, `false` is returned in non-strict mode. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-delete-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete + Delete, + + /// The `void` operator evaluates the given `expression` and then returns `undefined`. + /// + /// Syntax: `void x` + /// + /// This operator allows evaluating expressions that produce a value into places where an + /// expression that evaluates to `undefined` is desired. + /// The `void` operator is often used merely to obtain the `undefined` primitive value, usually using `void(0)` + /// (which is equivalent to `void 0`). In these cases, the global variable undefined can be used. + /// + /// When using an [immediately-invoked function expression](https://developer.mozilla.org/en-US/docs/Glossary/IIFE), + /// `void` can be used to force the function keyword to be treated as an expression instead of a declaration. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-void-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void + Void, +} + +impl UnaryOp { + /// Retrieves the operation as a static string. + const fn as_str(self) -> &'static str { + match self { + Self::IncrementPost | Self::IncrementPre => "++", + Self::DecrementPost | Self::DecrementPre => "--", + Self::Plus => "+", + Self::Minus => "-", + Self::Not => "!", + Self::Tilde => "~", + Self::Delete => "delete", + Self::TypeOf => "typeof", + Self::Void => "void", + } + } +} + +impl std::fmt::Display for UnaryOp { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.as_str()) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/optional.rs b/javascript-engine/external/boa/boa_ast/src/expression/optional.rs new file mode 100644 index 0000000..36f4e44 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/optional.rs @@ -0,0 +1,249 @@ +use super::{access::PropertyAccessField, Expression}; +use crate::{ + function::PrivateName, + join_nodes, try_break, + visitor::{VisitWith, Visitor, VisitorMut}, +}; +use boa_interner::{Interner, ToInternedString}; +use core::ops::ControlFlow; + +/// List of valid operations in an [`Optional`] chain. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum OptionalOperationKind { + /// A property access (`a?.prop`). + SimplePropertyAccess { + /// The field accessed. + field: PropertyAccessField, + }, + /// A private property access (`a?.#prop`). + PrivatePropertyAccess { + /// The private property accessed. + field: PrivateName, + }, + /// A function call (`a?.(arg)`). + Call { + /// The args passed to the function call. + args: Box<[Expression]>, + }, +} + +impl VisitWith for OptionalOperationKind { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::SimplePropertyAccess { field } => visitor.visit_property_access_field(field), + Self::PrivatePropertyAccess { field } => visitor.visit_private_name(field), + Self::Call { args } => { + for arg in args.iter() { + try_break!(visitor.visit_expression(arg)); + } + ControlFlow::Continue(()) + } + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::SimplePropertyAccess { field } => visitor.visit_property_access_field_mut(field), + Self::PrivatePropertyAccess { field } => visitor.visit_private_name_mut(field), + Self::Call { args } => { + for arg in args.iter_mut() { + try_break!(visitor.visit_expression_mut(arg)); + } + ControlFlow::Continue(()) + } + } + } +} + +/// Operation within an [`Optional`] chain. +/// +/// An operation within an `Optional` chain can be either shorted or non-shorted. A shorted operation +/// (`?.item`) will force the expression to return `undefined` if the target is `undefined` or `null`. +/// In contrast, a non-shorted operation (`.prop`) will try to access the property, even if the target +/// is `undefined` or `null`. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct OptionalOperation { + kind: OptionalOperationKind, + shorted: bool, +} + +impl OptionalOperation { + /// Creates a new `OptionalOperation`. + #[inline] + #[must_use] + pub const fn new(kind: OptionalOperationKind, shorted: bool) -> Self { + Self { kind, shorted } + } + /// Gets the kind of operation. + #[inline] + #[must_use] + pub const fn kind(&self) -> &OptionalOperationKind { + &self.kind + } + + /// Returns `true` if the operation short-circuits the [`Optional`] chain when the target is + /// `undefined` or `null`. + #[inline] + #[must_use] + pub const fn shorted(&self) -> bool { + self.shorted + } +} + +impl ToInternedString for OptionalOperation { + fn to_interned_string(&self, interner: &Interner) -> String { + let mut buf = if self.shorted { + String::from("?.") + } else { + if let OptionalOperationKind::SimplePropertyAccess { + field: PropertyAccessField::Const(name), + } = &self.kind + { + return format!(".{}", interner.resolve_expect(*name)); + } + + if let OptionalOperationKind::PrivatePropertyAccess { field } = &self.kind { + return format!(".#{}", interner.resolve_expect(field.description())); + } + + String::new() + }; + buf.push_str(&match &self.kind { + OptionalOperationKind::SimplePropertyAccess { field } => match field { + PropertyAccessField::Const(name) => interner.resolve_expect(*name).to_string(), + PropertyAccessField::Expr(expr) => { + format!("[{}]", expr.to_interned_string(interner)) + } + }, + OptionalOperationKind::PrivatePropertyAccess { field } => { + format!("#{}", interner.resolve_expect(field.description())) + } + OptionalOperationKind::Call { args } => format!("({})", join_nodes(interner, args)), + }); + buf + } +} + +impl VisitWith for OptionalOperation { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + visitor.visit_optional_operation_kind(&self.kind) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + visitor.visit_optional_operation_kind_mut(&mut self.kind) + } +} + +/// An optional chain expression, as defined by the [spec]. +/// +/// [Optional chaining][mdn] allows for short-circuiting property accesses and function calls, which +/// will return `undefined` instead of returning an error if the access target or the call is +/// either `undefined` or `null`. +/// +/// An example of optional chaining: +/// +/// ```Javascript +/// const adventurer = { +/// name: 'Alice', +/// cat: { +/// name: 'Dinah' +/// } +/// }; +/// +/// console.log(adventurer.cat?.name); // Dinah +/// console.log(adventurer.dog?.name); // undefined +/// ``` +/// +/// [spec]: https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#prod-OptionalExpression +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Optional { + target: Box, + chain: Box<[OptionalOperation]>, +} + +impl VisitWith for Optional { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_expression(&self.target)); + for op in self.chain.iter() { + try_break!(visitor.visit_optional_operation(op)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_expression_mut(&mut self.target)); + for op in self.chain.iter_mut() { + try_break!(visitor.visit_optional_operation_mut(op)); + } + ControlFlow::Continue(()) + } +} + +impl Optional { + /// Creates a new `Optional` expression. + #[inline] + #[must_use] + pub fn new(target: Expression, chain: Box<[OptionalOperation]>) -> Self { + Self { + target: Box::new(target), + chain, + } + } + + /// Gets the target of this `Optional` expression. + #[inline] + #[must_use] + pub fn target(&self) -> &Expression { + self.target.as_ref() + } + + /// Gets the chain of accesses and calls that will be applied to the target at runtime. + #[inline] + #[must_use] + pub fn chain(&self) -> &[OptionalOperation] { + self.chain.as_ref() + } +} + +impl From for Expression { + fn from(opt: Optional) -> Self { + Self::Optional(opt) + } +} + +impl ToInternedString for Optional { + fn to_interned_string(&self, interner: &Interner) -> String { + let mut buf = self.target.to_interned_string(interner); + + for item in &*self.chain { + buf.push_str(&item.to_interned_string(interner)); + } + + buf + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/spread.rs b/javascript-engine/external/boa/boa_ast/src/expression/spread.rs new file mode 100644 index 0000000..95d4ed4 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/spread.rs @@ -0,0 +1,78 @@ +use boa_interner::{Interner, ToInternedString}; +use core::ops::ControlFlow; + +use crate::visitor::{VisitWith, Visitor, VisitorMut}; + +use super::Expression; + +/// The `spread` operator allows an iterable such as an array expression or string to be +/// expanded. +/// +/// Syntax: `...x` +/// +/// It expands array expressions or strings in places where zero or more arguments (for +/// function calls) or elements (for array literals) +/// are expected, or an object expression to be expanded in places where zero or more key-value +/// pairs (for object literals) are expected. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-SpreadElement +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(transparent))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Spread { + target: Box, +} + +impl Spread { + /// Gets the target expression to be expanded by the spread operator. + #[inline] + #[must_use] + pub const fn target(&self) -> &Expression { + &self.target + } + + /// Creates a `Spread` AST Expression. + #[inline] + #[must_use] + pub fn new(target: Expression) -> Self { + Self { + target: Box::new(target), + } + } +} + +impl ToInternedString for Spread { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + format!("...{}", self.target().to_interned_string(interner)) + } +} + +impl From for Expression { + #[inline] + fn from(spread: Spread) -> Self { + Self::Spread(spread) + } +} + +impl VisitWith for Spread { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + visitor.visit_expression(&self.target) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + visitor.visit_expression_mut(&mut self.target) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/tagged_template.rs b/javascript-engine/external/boa/boa_ast/src/expression/tagged_template.rs new file mode 100644 index 0000000..68d79f6 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/tagged_template.rs @@ -0,0 +1,132 @@ +use boa_interner::{Interner, Sym, ToInternedString}; +use core::ops::ControlFlow; + +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; + +use super::Expression; + +/// A [`TaggedTemplate`][moz] expression, as defined by the [spec]. +/// +/// `TaggedTemplate`s are a type of template literals that are parsed by a custom function to generate +/// arbitrary objects from the inner strings and expressions. +/// +/// [moz]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates +/// [spec]: https://tc39.es/ecma262/#sec-tagged-templates +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct TaggedTemplate { + tag: Box, + raws: Box<[Sym]>, + cookeds: Box<[Option]>, + exprs: Box<[Expression]>, +} + +impl TaggedTemplate { + /// Creates a new tagged template with a tag, the list of raw strings, the cooked strings and + /// the expressions. + #[inline] + #[must_use] + pub fn new( + tag: Expression, + raws: Box<[Sym]>, + cookeds: Box<[Option]>, + exprs: Box<[Expression]>, + ) -> Self { + Self { + tag: tag.into(), + raws, + cookeds, + exprs, + } + } + + /// Gets the tag function of the template. + #[inline] + #[must_use] + pub const fn tag(&self) -> &Expression { + &self.tag + } + + /// Gets the inner raw strings of the template. + #[inline] + #[must_use] + pub const fn raws(&self) -> &[Sym] { + &self.raws + } + + /// Gets the cooked strings of the template. + #[inline] + #[must_use] + pub const fn cookeds(&self) -> &[Option] { + &self.cookeds + } + + /// Gets the interpolated expressions of the template. + #[inline] + #[must_use] + pub const fn exprs(&self) -> &[Expression] { + &self.exprs + } +} + +impl ToInternedString for TaggedTemplate { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + let mut buf = format!("{}`", self.tag.to_interned_string(interner)); + for (&raw, expr) in self.raws.iter().zip(self.exprs.iter()) { + buf.push_str(&format!( + "{}${{{}}}", + interner.resolve_expect(raw), + expr.to_interned_string(interner) + )); + } + buf.push('`'); + + buf + } +} + +impl From for Expression { + #[inline] + fn from(template: TaggedTemplate) -> Self { + Self::TaggedTemplate(template) + } +} + +impl VisitWith for TaggedTemplate { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_expression(&self.tag)); + for raw in self.raws.iter() { + try_break!(visitor.visit_sym(raw)); + } + for cooked in self.cookeds.iter().flatten() { + try_break!(visitor.visit_sym(cooked)); + } + for expr in self.exprs.iter() { + try_break!(visitor.visit_expression(expr)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_expression_mut(&mut self.tag)); + for raw in self.raws.iter_mut() { + try_break!(visitor.visit_sym_mut(raw)); + } + for cooked in self.cookeds.iter_mut().flatten() { + try_break!(visitor.visit_sym_mut(cooked)); + } + for expr in self.exprs.iter_mut() { + try_break!(visitor.visit_expression_mut(expr)); + } + ControlFlow::Continue(()) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/expression/yield.rs b/javascript-engine/external/boa/boa_ast/src/expression/yield.rs new file mode 100644 index 0000000..91c2cd0 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/expression/yield.rs @@ -0,0 +1,90 @@ +use boa_interner::{Interner, ToInternedString}; +use core::ops::ControlFlow; + +use crate::visitor::{VisitWith, Visitor, VisitorMut}; + +use super::Expression; + +/// The `yield` keyword is used to pause and resume a generator function +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-YieldExpression +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Yield { + target: Option>, + delegate: bool, +} + +impl Yield { + /// Gets the target expression of this `Yield` statement. + #[inline] + pub fn target(&self) -> Option<&Expression> { + self.target.as_ref().map(Box::as_ref) + } + + /// Returns `true` if this `Yield` statement delegates to another generator or iterable object. + #[inline] + #[must_use] + pub const fn delegate(&self) -> bool { + self.delegate + } + + /// Creates a `Yield` AST Expression. + #[inline] + #[must_use] + pub fn new(expr: Option, delegate: bool) -> Self { + Self { + target: expr.map(Box::new), + delegate, + } + } +} + +impl From for Expression { + #[inline] + fn from(r#yield: Yield) -> Self { + Self::Yield(r#yield) + } +} + +impl ToInternedString for Yield { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + let y = if self.delegate { "yield*" } else { "yield" }; + if let Some(ex) = self.target() { + format!("{y} {}", ex.to_interned_string(interner)) + } else { + y.to_owned() + } + } +} + +impl VisitWith for Yield { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + if let Some(expr) = &self.target { + visitor.visit_expression(expr) + } else { + ControlFlow::Continue(()) + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + if let Some(expr) = &mut self.target { + visitor.visit_expression_mut(expr) + } else { + ControlFlow::Continue(()) + } + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/function/arrow_function.rs b/javascript-engine/external/boa/boa_ast/src/function/arrow_function.rs new file mode 100644 index 0000000..0c5a5b5 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/function/arrow_function.rs @@ -0,0 +1,118 @@ +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ + expression::{Expression, Identifier}, + join_nodes, StatementList, +}; +use boa_interner::{Interner, ToIndentedString}; +use core::ops::ControlFlow; + +use super::FormalParameterList; + +/// An arrow function expression, as defined by the [spec]. +/// +/// An [arrow function][mdn] expression is a syntactically compact alternative to a regular function +/// expression. Arrow function expressions are ill suited as methods, and they cannot be used as +/// constructors. Arrow functions cannot be used as constructors and will throw an error when +/// used with new. +/// +/// [spec]: https://tc39.es/ecma262/#prod-ArrowFunction +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct ArrowFunction { + name: Option, + parameters: FormalParameterList, + body: StatementList, +} + +impl ArrowFunction { + /// Creates a new `ArrowFunctionDecl` AST Expression. + #[inline] + #[must_use] + pub const fn new( + name: Option, + params: FormalParameterList, + body: StatementList, + ) -> Self { + Self { + name, + parameters: params, + body, + } + } + + /// Gets the name of the function declaration. + #[inline] + #[must_use] + pub const fn name(&self) -> Option { + self.name + } + + /// Sets the name of the function declaration. + #[inline] + pub fn set_name(&mut self, name: Option) { + self.name = name; + } + + /// Gets the list of parameters of the arrow function. + #[inline] + #[must_use] + pub const fn parameters(&self) -> &FormalParameterList { + &self.parameters + } + + /// Gets the body of the arrow function. + #[inline] + #[must_use] + pub const fn body(&self) -> &StatementList { + &self.body + } +} + +impl ToIndentedString for ArrowFunction { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let mut buf = format!("({}", join_nodes(interner, self.parameters.as_ref())); + if self.body().statements().is_empty() { + buf.push_str(") => {}"); + } else { + buf.push_str(&format!( + ") => {{\n{}{}}}", + self.body.to_indented_string(interner, indentation + 1), + " ".repeat(indentation) + )); + } + buf + } +} + +impl From for Expression { + fn from(decl: ArrowFunction) -> Self { + Self::ArrowFunction(decl) + } +} + +impl VisitWith for ArrowFunction { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + if let Some(ident) = &self.name { + try_break!(visitor.visit_identifier(ident)); + } + try_break!(visitor.visit_formal_parameter_list(&self.parameters)); + visitor.visit_statement_list(&self.body) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + if let Some(ident) = &mut self.name { + try_break!(visitor.visit_identifier_mut(ident)); + } + try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters)); + visitor.visit_statement_list_mut(&mut self.body) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/function/async_arrow_function.rs b/javascript-engine/external/boa/boa_ast/src/function/async_arrow_function.rs new file mode 100644 index 0000000..e47baf6 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/function/async_arrow_function.rs @@ -0,0 +1,118 @@ +use std::ops::ControlFlow; + +use super::FormalParameterList; +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ + expression::{Expression, Identifier}, + join_nodes, StatementList, +}; +use boa_interner::{Interner, ToIndentedString}; + +/// An async arrow function expression, as defined by the [spec]. +/// +/// An [async arrow function][mdn] expression is a syntactically compact alternative to a regular function +/// expression. Arrow function expressions are ill suited as methods, and they cannot be used as +/// constructors. Arrow functions cannot be used as constructors and will throw an error when +/// used with new. +/// +/// [spec]: https://tc39.es/ecma262/#prod-AsyncArrowFunction +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct AsyncArrowFunction { + name: Option, + parameters: FormalParameterList, + body: StatementList, +} + +impl AsyncArrowFunction { + /// Creates a new `AsyncArrowFunction` AST Expression. + #[inline] + #[must_use] + pub const fn new( + name: Option, + parameters: FormalParameterList, + body: StatementList, + ) -> Self { + Self { + name, + parameters, + body, + } + } + + /// Gets the name of the function declaration. + #[inline] + #[must_use] + pub const fn name(&self) -> Option { + self.name + } + + /// Sets the name of the function declaration. + #[inline] + pub fn set_name(&mut self, name: Option) { + self.name = name; + } + + /// Gets the list of parameters of the arrow function. + #[inline] + #[must_use] + pub const fn parameters(&self) -> &FormalParameterList { + &self.parameters + } + + /// Gets the body of the arrow function. + #[inline] + #[must_use] + pub const fn body(&self) -> &StatementList { + &self.body + } +} + +impl ToIndentedString for AsyncArrowFunction { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let mut buf = format!("async ({}", join_nodes(interner, self.parameters.as_ref())); + if self.body().statements().is_empty() { + buf.push_str(") => {}"); + } else { + buf.push_str(&format!( + ") => {{\n{}{}}}", + self.body.to_indented_string(interner, indentation + 1), + " ".repeat(indentation) + )); + } + buf + } +} + +impl From for Expression { + fn from(decl: AsyncArrowFunction) -> Self { + Self::AsyncArrowFunction(decl) + } +} + +impl VisitWith for AsyncArrowFunction { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + if let Some(ident) = &self.name { + try_break!(visitor.visit_identifier(ident)); + } + try_break!(visitor.visit_formal_parameter_list(&self.parameters)); + visitor.visit_statement_list(&self.body) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + if let Some(ident) = &mut self.name { + try_break!(visitor.visit_identifier_mut(ident)); + } + try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters)); + visitor.visit_statement_list_mut(&mut self.body) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/function/async_function.rs b/javascript-engine/external/boa/boa_ast/src/function/async_function.rs new file mode 100644 index 0000000..74100ee --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/function/async_function.rs @@ -0,0 +1,138 @@ +//! Async Function Expression. + +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ + expression::{Expression, Identifier}, + join_nodes, Declaration, StatementList, +}; +use boa_interner::{Interner, ToIndentedString}; +use core::ops::ControlFlow; + +use super::FormalParameterList; + +/// An async function definition, as defined by the [spec]. +/// +/// An [async function][mdn] is a function where await expressions are allowed within it. +/// The async and await keywords enable asynchronous programming on Javascript without the use +/// of promise chains. +/// +/// [spec]: https://tc39.es/ecma262/#sec-async-function-definitions +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct AsyncFunction { + name: Option, + parameters: FormalParameterList, + body: StatementList, + has_binding_identifier: bool, +} + +impl AsyncFunction { + /// Creates a new function expression + #[inline] + #[must_use] + pub const fn new( + name: Option, + parameters: FormalParameterList, + body: StatementList, + has_binding_identifier: bool, + ) -> Self { + Self { + name, + parameters, + body, + has_binding_identifier, + } + } + + /// Gets the name of the function declaration. + #[inline] + #[must_use] + pub const fn name(&self) -> Option { + self.name + } + + /// Gets the list of parameters of the function declaration. + #[inline] + #[must_use] + pub const fn parameters(&self) -> &FormalParameterList { + &self.parameters + } + + /// Gets the body of the function declaration. + #[inline] + #[must_use] + pub const fn body(&self) -> &StatementList { + &self.body + } + + /// Returns whether the function expression has a binding identifier. + #[inline] + #[must_use] + pub const fn has_binding_identifier(&self) -> bool { + self.has_binding_identifier + } +} + +impl ToIndentedString for AsyncFunction { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let mut buf = "async function".to_owned(); + if let Some(name) = self.name { + buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); + } + buf.push_str(&format!( + "({}", + join_nodes(interner, self.parameters.as_ref()) + )); + if self.body().statements().is_empty() { + buf.push_str(") {}"); + } else { + buf.push_str(&format!( + ") {{\n{}{}}}", + self.body.to_indented_string(interner, indentation + 1), + " ".repeat(indentation) + )); + } + buf + } +} + +impl From for Expression { + #[inline] + fn from(expr: AsyncFunction) -> Self { + Self::AsyncFunction(expr) + } +} + +impl From for Declaration { + #[inline] + fn from(f: AsyncFunction) -> Self { + Self::AsyncFunction(f) + } +} + +impl VisitWith for AsyncFunction { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + if let Some(ident) = &self.name { + try_break!(visitor.visit_identifier(ident)); + } + try_break!(visitor.visit_formal_parameter_list(&self.parameters)); + visitor.visit_statement_list(&self.body) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + if let Some(ident) = &mut self.name { + try_break!(visitor.visit_identifier_mut(ident)); + } + try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters)); + visitor.visit_statement_list_mut(&mut self.body) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/function/async_generator.rs b/javascript-engine/external/boa/boa_ast/src/function/async_generator.rs new file mode 100644 index 0000000..f983092 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/function/async_generator.rs @@ -0,0 +1,130 @@ +//! Async Generator Expression +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ + block_to_string, + expression::{Expression, Identifier}, + join_nodes, Declaration, StatementList, +}; +use boa_interner::{Interner, ToIndentedString}; +use core::ops::ControlFlow; + +use super::FormalParameterList; + +/// An async generator definition, as defined by the [spec]. +/// +/// An [async generator][mdn] combines async functions with generators, making it possible to use +/// `await` and `yield` expressions within the definition of the function. +/// +/// [spec]: https://tc39.es/ecma262/#sec-async-generator-function-definitions +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function* +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct AsyncGenerator { + name: Option, + parameters: FormalParameterList, + body: StatementList, + has_binding_identifier: bool, +} + +impl AsyncGenerator { + /// Creates a new async generator expression + #[inline] + #[must_use] + pub const fn new( + name: Option, + parameters: FormalParameterList, + body: StatementList, + has_binding_identifier: bool, + ) -> Self { + Self { + name, + parameters, + body, + has_binding_identifier, + } + } + + /// Gets the name of the async generator expression + #[inline] + #[must_use] + pub const fn name(&self) -> Option { + self.name + } + + /// Gets the list of parameters of the async generator expression + #[inline] + #[must_use] + pub const fn parameters(&self) -> &FormalParameterList { + &self.parameters + } + + /// Gets the body of the async generator expression + #[inline] + #[must_use] + pub const fn body(&self) -> &StatementList { + &self.body + } + + /// Returns whether the function expression has a binding identifier. + #[inline] + #[must_use] + pub const fn has_binding_identifier(&self) -> bool { + self.has_binding_identifier + } +} + +impl ToIndentedString for AsyncGenerator { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let mut buf = "async function*".to_owned(); + if let Some(name) = self.name { + buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); + } + buf.push_str(&format!( + "({}) {}", + join_nodes(interner, self.parameters.as_ref()), + block_to_string(&self.body, interner, indentation) + )); + + buf + } +} + +impl From for Expression { + #[inline] + fn from(expr: AsyncGenerator) -> Self { + Self::AsyncGenerator(expr) + } +} + +impl From for Declaration { + #[inline] + fn from(f: AsyncGenerator) -> Self { + Self::AsyncGenerator(f) + } +} + +impl VisitWith for AsyncGenerator { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + if let Some(ident) = &self.name { + try_break!(visitor.visit_identifier(ident)); + } + try_break!(visitor.visit_formal_parameter_list(&self.parameters)); + visitor.visit_statement_list(&self.body) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + if let Some(ident) = &mut self.name { + try_break!(visitor.visit_identifier_mut(ident)); + } + try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters)); + visitor.visit_statement_list_mut(&mut self.body) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/function/class.rs b/javascript-engine/external/boa/boa_ast/src/function/class.rs new file mode 100644 index 0000000..f12afa8 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/function/class.rs @@ -0,0 +1,579 @@ +use super::Function; +use crate::{ + block_to_string, + expression::{Expression, Identifier}, + join_nodes, + property::{MethodDefinition, PropertyName}, + try_break, + visitor::{VisitWith, Visitor, VisitorMut}, + Declaration, StatementList, ToStringEscaped, +}; +use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString}; +use core::ops::ControlFlow; +use std::borrow::Cow; +use std::hash::Hash; + +/// A class declaration, as defined by the [spec]. +/// +/// A [class][mdn] declaration defines a class with the specified methods, fields, and optional constructor. +/// Classes can be used to create objects, which can also be created through literals (using `{}`). +/// +/// [spec]: https://tc39.es/ecma262/#sec-class-definitions +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Class { + name: Option, + super_ref: Option, + pub(crate) constructor: Option, + pub(crate) elements: Box<[ClassElement]>, + has_binding_identifier: bool, +} + +impl Class { + /// Creates a new class declaration. + #[inline] + #[must_use] + pub fn new( + name: Option, + super_ref: Option, + constructor: Option, + elements: Box<[ClassElement]>, + has_binding_identifier: bool, + ) -> Self { + Self { + name, + super_ref, + constructor, + elements, + has_binding_identifier, + } + } + + /// Returns the name of the class. + #[inline] + #[must_use] + pub const fn name(&self) -> Option { + self.name + } + + /// Returns the super class ref of the class. + #[inline] + #[must_use] + pub const fn super_ref(&self) -> Option<&Expression> { + self.super_ref.as_ref() + } + + /// Returns the constructor of the class. + #[inline] + #[must_use] + pub const fn constructor(&self) -> Option<&Function> { + self.constructor.as_ref() + } + + /// Gets the list of all fields defined on the class. + #[inline] + #[must_use] + pub const fn elements(&self) -> &[ClassElement] { + &self.elements + } + + /// Returns whether the class has a binding identifier. + #[inline] + #[must_use] + pub const fn has_binding_identifier(&self) -> bool { + self.has_binding_identifier + } +} + +impl ToIndentedString for Class { + fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String { + let class_name = self.name.map_or(Cow::Borrowed(""), |s| { + interner.resolve_expect(s.sym()).join( + Cow::Borrowed, + |utf16| Cow::Owned(utf16.to_string_escaped()), + true, + ) + }); + if self.elements.is_empty() && self.constructor().is_none() { + return format!( + "class {class_name}{} {{}}", + self.super_ref + .as_ref() + .map_or_else(String::new, |sup| format!( + " extends {}", + sup.to_interned_string(interner) + )) + ); + } + let indentation = " ".repeat(indent_n + 1); + let mut buf = format!( + "class {class_name}{} {{\n", + self.super_ref + .as_ref() + .map_or_else(String::new, |sup| format!( + "extends {}", + sup.to_interned_string(interner) + )) + ); + if let Some(expr) = &self.constructor { + buf.push_str(&format!( + "{indentation}constructor({}) {}\n", + join_nodes(interner, expr.parameters().as_ref()), + block_to_string(expr.body(), interner, indent_n + 1) + )); + } + for element in self.elements.iter() { + buf.push_str(&match element { + ClassElement::MethodDefinition(name, method) => { + format!( + "{indentation}{}{}({}) {}\n", + match &method { + MethodDefinition::Get(_) => "get ", + MethodDefinition::Set(_) => "set ", + _ => "", + }, + name.to_interned_string(interner), + match &method { + MethodDefinition::Get(expr) + | MethodDefinition::Set(expr) + | MethodDefinition::Ordinary(expr) => { + join_nodes(interner, expr.parameters().as_ref()) + } + MethodDefinition::Generator(expr) => { + join_nodes(interner, expr.parameters().as_ref()) + } + MethodDefinition::AsyncGenerator(expr) => { + join_nodes(interner, expr.parameters().as_ref()) + } + MethodDefinition::Async(expr) => { + join_nodes(interner, expr.parameters().as_ref()) + } + }, + match &method { + MethodDefinition::Get(expr) + | MethodDefinition::Set(expr) + | MethodDefinition::Ordinary(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::Generator(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::AsyncGenerator(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::Async(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + }, + ) + } + ClassElement::StaticMethodDefinition(name, method) => { + format!( + "{indentation}static {}{}({}) {}\n", + match &method { + MethodDefinition::Get(_) => "get ", + MethodDefinition::Set(_) => "set ", + _ => "", + }, + name.to_interned_string(interner), + match &method { + MethodDefinition::Get(expr) + | MethodDefinition::Set(expr) + | MethodDefinition::Ordinary(expr) => { + join_nodes(interner, expr.parameters().as_ref()) + } + MethodDefinition::Generator(expr) => { + join_nodes(interner, expr.parameters().as_ref()) + } + MethodDefinition::AsyncGenerator(expr) => { + join_nodes(interner, expr.parameters().as_ref()) + } + MethodDefinition::Async(expr) => { + join_nodes(interner, expr.parameters().as_ref()) + } + }, + match &method { + MethodDefinition::Get(expr) + | MethodDefinition::Set(expr) + | MethodDefinition::Ordinary(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::Generator(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::AsyncGenerator(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::Async(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + }, + ) + } + ClassElement::FieldDefinition(name, field) => match field { + Some(expr) => { + format!( + "{indentation}{} = {};\n", + name.to_interned_string(interner), + expr.to_no_indent_string(interner, indent_n + 1) + ) + } + None => { + format!("{indentation}{};\n", name.to_interned_string(interner),) + } + }, + ClassElement::StaticFieldDefinition(name, field) => match field { + Some(expr) => { + format!( + "{indentation}static {} = {};\n", + name.to_interned_string(interner), + expr.to_no_indent_string(interner, indent_n + 1) + ) + } + None => { + format!( + "{indentation}static {};\n", + name.to_interned_string(interner), + ) + } + }, + ClassElement::PrivateMethodDefinition(name, method) => { + format!( + "{indentation}{}#{}({}) {}\n", + match &method { + MethodDefinition::Get(_) => "get ", + MethodDefinition::Set(_) => "set ", + _ => "", + }, + interner.resolve_expect(name.description()), + match &method { + MethodDefinition::Get(expr) + | MethodDefinition::Set(expr) + | MethodDefinition::Ordinary(expr) => { + join_nodes(interner, expr.parameters().as_ref()) + } + MethodDefinition::Generator(expr) => { + join_nodes(interner, expr.parameters().as_ref()) + } + MethodDefinition::AsyncGenerator(expr) => { + join_nodes(interner, expr.parameters().as_ref()) + } + MethodDefinition::Async(expr) => { + join_nodes(interner, expr.parameters().as_ref()) + } + }, + match &method { + MethodDefinition::Get(expr) + | MethodDefinition::Set(expr) + | MethodDefinition::Ordinary(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::Generator(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::AsyncGenerator(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::Async(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + }, + ) + } + ClassElement::PrivateStaticMethodDefinition(name, method) => { + format!( + "{indentation}static {}#{}({}) {}\n", + match &method { + MethodDefinition::Get(_) => "get ", + MethodDefinition::Set(_) => "set ", + _ => "", + }, + interner.resolve_expect(name.description()), + match &method { + MethodDefinition::Get(expr) + | MethodDefinition::Set(expr) + | MethodDefinition::Ordinary(expr) => { + join_nodes(interner, expr.parameters().as_ref()) + } + MethodDefinition::Generator(expr) => { + join_nodes(interner, expr.parameters().as_ref()) + } + MethodDefinition::AsyncGenerator(expr) => { + join_nodes(interner, expr.parameters().as_ref()) + } + MethodDefinition::Async(expr) => { + join_nodes(interner, expr.parameters().as_ref()) + } + }, + match &method { + MethodDefinition::Get(expr) + | MethodDefinition::Set(expr) + | MethodDefinition::Ordinary(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::Generator(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::AsyncGenerator(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::Async(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + }, + ) + } + ClassElement::PrivateFieldDefinition(name, field) => match field { + Some(expr) => { + format!( + "{indentation}#{} = {};\n", + interner.resolve_expect(name.description()), + expr.to_no_indent_string(interner, indent_n + 1) + ) + } + None => { + format!( + "{indentation}#{};\n", + interner.resolve_expect(name.description()), + ) + } + }, + ClassElement::PrivateStaticFieldDefinition(name, field) => match field { + Some(expr) => { + format!( + "{indentation}static #{} = {};\n", + interner.resolve_expect(name.description()), + expr.to_no_indent_string(interner, indent_n + 1) + ) + } + None => { + format!( + "{indentation}static #{};\n", + interner.resolve_expect(name.description()), + ) + } + }, + ClassElement::StaticBlock(statement_list) => { + format!( + "{indentation}static {}\n", + block_to_string(statement_list, interner, indent_n + 1) + ) + } + }); + } + buf.push('}'); + buf + } +} + +impl From for Expression { + fn from(expr: Class) -> Self { + Self::Class(Box::new(expr)) + } +} + +impl From for Declaration { + fn from(f: Class) -> Self { + Self::Class(f) + } +} + +impl VisitWith for Class { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + if let Some(ident) = &self.name { + try_break!(visitor.visit_identifier(ident)); + } + if let Some(expr) = &self.super_ref { + try_break!(visitor.visit_expression(expr)); + } + if let Some(func) = &self.constructor { + try_break!(visitor.visit_function(func)); + } + for elem in self.elements.iter() { + try_break!(visitor.visit_class_element(elem)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + if let Some(ident) = &mut self.name { + try_break!(visitor.visit_identifier_mut(ident)); + } + if let Some(expr) = &mut self.super_ref { + try_break!(visitor.visit_expression_mut(expr)); + } + if let Some(func) = &mut self.constructor { + try_break!(visitor.visit_function_mut(func)); + } + for elem in self.elements.iter_mut() { + try_break!(visitor.visit_class_element_mut(elem)); + } + ControlFlow::Continue(()) + } +} + +/// An element that can be within a [`Class`], as defined by the [spec]. +/// +/// [spec]: https://tc39.es/ecma262/#prod-ClassElement +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum ClassElement { + /// A method definition, including `get` and `set` accessors. + MethodDefinition(PropertyName, MethodDefinition), + + /// A static method definition, accessible from the class constructor object. + StaticMethodDefinition(PropertyName, MethodDefinition), + + /// A field definition. + FieldDefinition(PropertyName, Option), + + /// A static field definition, accessible from the class constructor object + StaticFieldDefinition(PropertyName, Option), + + /// A private method definition, only accessible inside the class declaration. + PrivateMethodDefinition(PrivateName, MethodDefinition), + + /// A private static method definition, only accessible from static methods and fields inside + /// the class declaration. + PrivateStaticMethodDefinition(PrivateName, MethodDefinition), + + /// A private field definition, only accessible inside the class declaration. + PrivateFieldDefinition(PrivateName, Option), + + /// A private static field definition, only accessible from static methods and fields inside the + /// class declaration. + PrivateStaticFieldDefinition(PrivateName, Option), + + /// A static block, where a class can have initialization logic for its static fields. + StaticBlock(StatementList), +} + +impl VisitWith for ClassElement { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::MethodDefinition(pn, md) | Self::StaticMethodDefinition(pn, md) => { + try_break!(visitor.visit_property_name(pn)); + visitor.visit_method_definition(md) + } + Self::FieldDefinition(pn, maybe_expr) | Self::StaticFieldDefinition(pn, maybe_expr) => { + try_break!(visitor.visit_property_name(pn)); + if let Some(expr) = maybe_expr { + visitor.visit_expression(expr) + } else { + ControlFlow::Continue(()) + } + } + Self::PrivateMethodDefinition(name, md) + | Self::PrivateStaticMethodDefinition(name, md) => { + try_break!(visitor.visit_private_name(name)); + visitor.visit_method_definition(md) + } + Self::PrivateFieldDefinition(name, maybe_expr) + | Self::PrivateStaticFieldDefinition(name, maybe_expr) => { + try_break!(visitor.visit_private_name(name)); + if let Some(expr) = maybe_expr { + visitor.visit_expression(expr) + } else { + ControlFlow::Continue(()) + } + } + Self::StaticBlock(sl) => visitor.visit_statement_list(sl), + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::MethodDefinition(pn, md) | Self::StaticMethodDefinition(pn, md) => { + try_break!(visitor.visit_property_name_mut(pn)); + visitor.visit_method_definition_mut(md) + } + Self::FieldDefinition(pn, maybe_expr) | Self::StaticFieldDefinition(pn, maybe_expr) => { + try_break!(visitor.visit_property_name_mut(pn)); + if let Some(expr) = maybe_expr { + visitor.visit_expression_mut(expr) + } else { + ControlFlow::Continue(()) + } + } + Self::PrivateMethodDefinition(name, md) + | Self::PrivateStaticMethodDefinition(name, md) => { + try_break!(visitor.visit_private_name_mut(name)); + visitor.visit_method_definition_mut(md) + } + Self::PrivateFieldDefinition(name, maybe_expr) + | Self::PrivateStaticFieldDefinition(name, maybe_expr) => { + try_break!(visitor.visit_private_name_mut(name)); + if let Some(expr) = maybe_expr { + visitor.visit_expression_mut(expr) + } else { + ControlFlow::Continue(()) + } + } + Self::StaticBlock(sl) => visitor.visit_statement_list_mut(sl), + } + } +} + +/// A private name as defined by the [spec]. +/// +/// [spec]: https://tc39.es/ecma262/#sec-private-names +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct PrivateName { + /// The `[[Description]]` internal slot of the private name. + description: Sym, + + /// The indices of the private name are included to ensure that private names are unique. + pub(crate) indices: (usize, usize), +} + +impl PrivateName { + /// Create a new private name. + #[inline] + #[must_use] + pub const fn new(description: Sym) -> Self { + Self { + description, + indices: (0, 0), + } + } + + /// Get the description of the private name. + #[inline] + #[must_use] + pub const fn description(&self) -> Sym { + self.description + } +} + +impl VisitWith for PrivateName { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + visitor.visit_sym(&self.description) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + visitor.visit_sym_mut(&mut self.description) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/function/generator.rs b/javascript-engine/external/boa/boa_ast/src/function/generator.rs new file mode 100644 index 0000000..9f18623 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/function/generator.rs @@ -0,0 +1,132 @@ +use crate::{ + block_to_string, + expression::{Expression, Identifier}, + join_nodes, Declaration, StatementList, +}; +use core::ops::ControlFlow; + +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use boa_interner::{Interner, ToIndentedString}; + +use super::FormalParameterList; + +/// A generator definition, as defined by the [spec]. +/// +/// [Generators][mdn] are "resumable functions", which can be suspended during execution and +/// resumed at any later time. The main feature of a generator are `yield` expressions, which +/// specifies the value returned when a generator is suspended, and the point from which +/// the execution will resume. +/// +/// [spec]: https://tc39.es/ecma262/#sec-generator-function-definitions +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function* +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Generator { + name: Option, + parameters: FormalParameterList, + body: StatementList, + has_binding_identifier: bool, +} + +impl Generator { + /// Creates a new generator expression + #[inline] + #[must_use] + pub const fn new( + name: Option, + parameters: FormalParameterList, + body: StatementList, + has_binding_identifier: bool, + ) -> Self { + Self { + name, + parameters, + body, + has_binding_identifier, + } + } + + /// Gets the name of the generator declaration. + #[inline] + #[must_use] + pub const fn name(&self) -> Option { + self.name + } + + /// Gets the list of parameters of the generator declaration. + #[inline] + #[must_use] + pub const fn parameters(&self) -> &FormalParameterList { + &self.parameters + } + + /// Gets the body of the generator declaration. + #[inline] + #[must_use] + pub const fn body(&self) -> &StatementList { + &self.body + } + + /// Returns whether the function expression has a binding identifier. + #[inline] + #[must_use] + pub const fn has_binding_identifier(&self) -> bool { + self.has_binding_identifier + } +} + +impl ToIndentedString for Generator { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let mut buf = "function*".to_owned(); + if let Some(name) = self.name { + buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); + } + buf.push_str(&format!( + "({}) {}", + join_nodes(interner, self.parameters.as_ref()), + block_to_string(&self.body, interner, indentation) + )); + + buf + } +} + +impl From for Expression { + #[inline] + fn from(expr: Generator) -> Self { + Self::Generator(expr) + } +} + +impl From for Declaration { + #[inline] + fn from(f: Generator) -> Self { + Self::Generator(f) + } +} + +impl VisitWith for Generator { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + if let Some(ident) = &self.name { + try_break!(visitor.visit_identifier(ident)); + } + try_break!(visitor.visit_formal_parameter_list(&self.parameters)); + visitor.visit_statement_list(&self.body) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + if let Some(ident) = &mut self.name { + try_break!(visitor.visit_identifier_mut(ident)); + } + try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters)); + visitor.visit_statement_list_mut(&mut self.body) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/function/mod.rs b/javascript-engine/external/boa/boa_ast/src/function/mod.rs new file mode 100644 index 0000000..775e8f4 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/function/mod.rs @@ -0,0 +1,184 @@ +//! Functions and classes nodes, as defined by the [spec]. +//! +//! [Functions][func] are mainly subprograms that can be called by external code to execute a sequence of +//! statements (the *body* of the function). Javascript functions fall in several categories: +//! +//! - [`Function`]s. +//! - [`ArrowFunction`]s. +//! - [`AsyncArrowFunction`]s. +//! - [`Generator`]s. +//! - [`AsyncFunction`]s. +//! - [`AsyncGenerator`]s. +//! +//! All of them can be declared in either [declaration][decl] form or [expression][expr] form, +//! except from `ArrowFunction`s and `AsyncArrowFunction`s, which can only be declared in expression form. +//! +//! This module also contains [`Class`]es, which are templates for creating objects. Classes +//! can also be declared in either declaration or expression form. +//! +//! [spec]: https://tc39.es/ecma262/#sec-ecmascript-language-functions-and-classes +//! [func]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions +//! [decl]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function +//! [expr]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function + +mod arrow_function; +mod async_arrow_function; +mod async_function; +mod async_generator; +mod class; +mod generator; +mod parameters; + +pub use arrow_function::ArrowFunction; +pub use async_arrow_function::AsyncArrowFunction; +pub use async_function::AsyncFunction; +pub use async_generator::AsyncGenerator; +pub use class::{Class, ClassElement, PrivateName}; +use core::ops::ControlFlow; +pub use generator::Generator; +pub use parameters::{FormalParameter, FormalParameterList, FormalParameterListFlags}; + +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{block_to_string, join_nodes, StatementList}; +use boa_interner::{Interner, ToIndentedString}; + +use super::expression::{Expression, Identifier}; +use super::Declaration; + +/// A function definition, as defined by the [spec]. +/// +/// By default, functions return `undefined`. To return any other value, the function must have +/// a return statement that specifies the value to return. +/// +/// More information: +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#sec-function-definitions +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Function { + name: Option, + parameters: FormalParameterList, + body: StatementList, + has_binding_identifier: bool, +} + +impl Function { + /// Creates a new function expression. + #[inline] + #[must_use] + pub const fn new( + name: Option, + parameters: FormalParameterList, + body: StatementList, + ) -> Self { + Self { + name, + parameters, + body, + has_binding_identifier: false, + } + } + + /// Creates a new function expression with an expression binding identifier. + #[inline] + #[must_use] + pub const fn new_with_binding_identifier( + name: Option, + parameters: FormalParameterList, + body: StatementList, + has_binding_identifier: bool, + ) -> Self { + Self { + name, + parameters, + body, + has_binding_identifier, + } + } + + /// Gets the name of the function declaration. + #[inline] + #[must_use] + pub const fn name(&self) -> Option { + self.name + } + + /// Gets the list of parameters of the function declaration. + #[inline] + #[must_use] + pub const fn parameters(&self) -> &FormalParameterList { + &self.parameters + } + + /// Gets the body of the function declaration. + #[inline] + #[must_use] + pub const fn body(&self) -> &StatementList { + &self.body + } + + /// Returns whether the function expression has a binding identifier. + #[inline] + #[must_use] + pub const fn has_binding_identifier(&self) -> bool { + self.has_binding_identifier + } +} + +impl ToIndentedString for Function { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let mut buf = "function".to_owned(); + if let Some(name) = self.name { + buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); + } + buf.push_str(&format!( + "({}) {}", + join_nodes(interner, self.parameters.as_ref()), + block_to_string(&self.body, interner, indentation) + )); + + buf + } +} + +impl From for Expression { + #[inline] + fn from(expr: Function) -> Self { + Self::Function(expr) + } +} + +impl From for Declaration { + #[inline] + fn from(f: Function) -> Self { + Self::Function(f) + } +} + +impl VisitWith for Function { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + if let Some(ident) = &self.name { + try_break!(visitor.visit_identifier(ident)); + } + try_break!(visitor.visit_formal_parameter_list(&self.parameters)); + visitor.visit_statement_list(&self.body) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + if let Some(ident) = &mut self.name { + try_break!(visitor.visit_identifier_mut(ident)); + } + try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters)); + visitor.visit_statement_list_mut(&mut self.body) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/function/parameters.rs b/javascript-engine/external/boa/boa_ast/src/function/parameters.rs new file mode 100644 index 0000000..eba5b03 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/function/parameters.rs @@ -0,0 +1,287 @@ +use crate::{ + declaration::{Binding, Variable}, + expression::Expression, + operations::bound_names, + try_break, + visitor::{VisitWith, Visitor, VisitorMut}, +}; +use bitflags::bitflags; +use boa_interner::{Interner, Sym, ToInternedString}; +use core::ops::ControlFlow; +use rustc_hash::FxHashSet; + +/// A list of `FormalParameter`s that describes the parameters of a function, as defined by the [spec]. +/// +/// [spec]: https://tc39.es/ecma262/#prod-FormalParameterList +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, Default, PartialEq)] +pub struct FormalParameterList { + parameters: Box<[FormalParameter]>, + flags: FormalParameterListFlags, + length: u32, +} + +impl FormalParameterList { + /// Creates a new empty formal parameter list. + #[must_use] + pub fn new() -> Self { + Self { + parameters: Box::new([]), + flags: FormalParameterListFlags::default(), + length: 0, + } + } + + /// Creates a `FormalParameterList` from a list of [`FormalParameter`]s. + #[must_use] + pub fn from_parameters(parameters: Vec) -> Self { + let mut flags = FormalParameterListFlags::default(); + let mut length = 0; + let mut names = FxHashSet::default(); + + for parameter in ¶meters { + let parameter_names = bound_names(parameter); + + for name in parameter_names { + if name == Sym::ARGUMENTS { + flags |= FormalParameterListFlags::HAS_ARGUMENTS; + } + if names.contains(&name) { + flags |= FormalParameterListFlags::HAS_DUPLICATES; + } else { + names.insert(name); + } + } + + if parameter.is_rest_param() { + flags |= FormalParameterListFlags::HAS_REST_PARAMETER; + } + if parameter.init().is_some() { + flags |= FormalParameterListFlags::HAS_EXPRESSIONS; + } + if parameter.is_rest_param() || parameter.init().is_some() || !parameter.is_identifier() + { + flags.remove(FormalParameterListFlags::IS_SIMPLE); + } + if !(flags.contains(FormalParameterListFlags::HAS_EXPRESSIONS) + || parameter.is_rest_param() + || parameter.init().is_some()) + { + length += 1; + } + } + + Self { + parameters: parameters.into(), + flags, + length, + } + } + + /// Returns the length of the parameter list. + /// Note that this is not equal to the length of the parameters slice. + #[must_use] + pub const fn length(&self) -> u32 { + self.length + } + + /// Returns the parameter list flags. + #[must_use] + pub const fn flags(&self) -> FormalParameterListFlags { + self.flags + } + + /// Indicates if the parameter list is simple. + #[must_use] + pub const fn is_simple(&self) -> bool { + self.flags.contains(FormalParameterListFlags::IS_SIMPLE) + } + + /// Indicates if the parameter list has duplicate parameters. + #[must_use] + pub const fn has_duplicates(&self) -> bool { + self.flags + .contains(FormalParameterListFlags::HAS_DUPLICATES) + } + + /// Indicates if the parameter list has a rest parameter. + #[must_use] + pub const fn has_rest_parameter(&self) -> bool { + self.flags + .contains(FormalParameterListFlags::HAS_REST_PARAMETER) + } + + /// Indicates if the parameter list has expressions in it's parameters. + #[must_use] + pub const fn has_expressions(&self) -> bool { + self.flags + .contains(FormalParameterListFlags::HAS_EXPRESSIONS) + } + + /// Indicates if the parameter list has parameters named 'arguments'. + #[must_use] + pub const fn has_arguments(&self) -> bool { + self.flags.contains(FormalParameterListFlags::HAS_ARGUMENTS) + } +} + +impl From> for FormalParameterList { + fn from(parameters: Vec) -> Self { + Self::from_parameters(parameters) + } +} + +impl From for FormalParameterList { + fn from(parameter: FormalParameter) -> Self { + Self::from_parameters(vec![parameter]) + } +} + +impl AsRef<[FormalParameter]> for FormalParameterList { + fn as_ref(&self) -> &[FormalParameter] { + &self.parameters + } +} + +impl VisitWith for FormalParameterList { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + for parameter in self.parameters.iter() { + try_break!(visitor.visit_formal_parameter(parameter)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + for parameter in self.parameters.iter_mut() { + try_break!(visitor.visit_formal_parameter_mut(parameter)); + } + // TODO recompute flags + ControlFlow::Continue(()) + } +} + +#[cfg(feature = "fuzz")] +impl<'a> arbitrary::Arbitrary<'a> for FormalParameterList { + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + let params: Vec = u.arbitrary()?; + Ok(Self::from(params)) + } +} + +bitflags! { + /// Flags for a [`FormalParameterList`]. + #[allow(clippy::unsafe_derive_deserialize)] + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + pub struct FormalParameterListFlags: u8 { + /// Has only identifier parameters with no initialization expressions. + const IS_SIMPLE = 0b0000_0001; + /// Has any duplicate parameters. + const HAS_DUPLICATES = 0b0000_0010; + /// Has a rest parameter. + const HAS_REST_PARAMETER = 0b0000_0100; + /// Has any initialization expression. + const HAS_EXPRESSIONS = 0b0000_1000; + /// Has an argument with the name `arguments`. + const HAS_ARGUMENTS = 0b0001_0000; + } +} + +impl Default for FormalParameterListFlags { + fn default() -> Self { + Self::empty().union(Self::IS_SIMPLE) + } +} + +/// "Formal parameter" is a fancy way of saying "function parameter". +/// +/// In the declaration of a function, the parameters must be identifiers, +/// not any value like numbers, strings, or objects. +/// ```text +/// function foo(formalParameter1, formalParameter2) { +/// } +/// ``` +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-FormalParameter +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Missing_formal_parameter +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct FormalParameter { + variable: Variable, + is_rest_param: bool, +} + +impl FormalParameter { + /// Creates a new formal parameter. + pub fn new(variable: D, is_rest_param: bool) -> Self + where + D: Into, + { + Self { + variable: variable.into(), + is_rest_param, + } + } + + /// Gets the variable of the formal parameter + #[must_use] + pub const fn variable(&self) -> &Variable { + &self.variable + } + + /// Gets the initialization node of the formal parameter, if any. + #[must_use] + pub const fn init(&self) -> Option<&Expression> { + self.variable.init() + } + + /// Returns `true` if the parameter is a rest parameter. + #[must_use] + pub const fn is_rest_param(&self) -> bool { + self.is_rest_param + } + + /// Returns `true` if the parameter is an identifier. + #[must_use] + pub const fn is_identifier(&self) -> bool { + matches!(&self.variable.binding(), Binding::Identifier(_)) + } +} + +impl ToInternedString for FormalParameter { + fn to_interned_string(&self, interner: &Interner) -> String { + let mut buf = if self.is_rest_param { + "...".to_owned() + } else { + String::new() + }; + buf.push_str(&self.variable.to_interned_string(interner)); + buf + } +} + +impl VisitWith for FormalParameter { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + visitor.visit_variable(&self.variable) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + visitor.visit_variable_mut(&mut self.variable) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/keyword.rs b/javascript-engine/external/boa/boa_ast/src/keyword.rs new file mode 100644 index 0000000..6b48cd3 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/keyword.rs @@ -0,0 +1,625 @@ +//! The `Keyword` AST node, which represents reserved words of the ECMAScript language. +//! +//! The [specification][spec] defines keywords as tokens that match an `IdentifierName`, but also +//! have special meaning in ECMAScript. In ECMAScript, you cannot use these reserved words as variables, +//! labels, or function names. +//! +//! The [MDN documentation][mdn] contains a more extensive explanation about keywords. +//! +//! [spec]: https://tc39.es/ecma262/#sec-keywords-and-reserved-words +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords + +use crate::expression::operator::binary::{BinaryOp, RelationalOp}; +use boa_interner::{Interner, Sym}; +use boa_macros::utf16; +use std::{convert::TryFrom, error, fmt, str::FromStr}; + +/// List of keywords recognized by the JavaScript grammar. +/// +/// See the [module-level documentation][self] for more details. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum Keyword { + /// The `await` keyword. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AwaitExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await + Await, + + /// The `async` keyword. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AsyncMethod + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function + Async, + + /// The `break` keyword. + /// + /// More information: + /// - [break `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-BreakStatement + /// [node]: ../node/enum.Node.html#variant.Break + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break + Break, + + /// The `case` keyword. + /// + /// More information: + /// - [switch `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-CaseClause + /// [node]: ../node/enum.Node.html#variant.Switch + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch + Case, + + /// The `catch` keyword. + /// + /// More information: + /// - [try `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-Catch + /// [node]: ../node/enum.Node.html#variant.Try + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch + Catch, + + /// The `class` keyword. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-ClassDeclaration + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/class + Class, + + /// The `continue` keyword. + /// + /// More information: + /// - [continue `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-ContinueStatement + /// [node]: ../node/enum.Node.html#variant.Continue + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue + Continue, + + /// The `const` keyword. + /// + /// More information: + /// - [const `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations + /// [node]: ../node/enum.Node.html#variant.ConstDecl + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const + Const, + + /// The `debugger` keyword. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-debugger-statement + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger + Debugger, + + /// The `default` keyword. + /// + /// More information: + /// - [switch `Node` documentation][node] + /// - [ECMAScript reference default clause][spec-clause] + /// - [ECMAScript reference default export][spec-export] + /// - [MDN documentation][mdn] + /// + /// [node]: ../node/enum.Node.html#variant.Switch + /// [spec-clause]: https://tc39.es/ecma262/#prod-DefaultClause + /// [spec-export]: https://tc39.es/ecma262/#prod-ImportedDefaultBinding + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/default + Default, + + /// The `delete` keyword. + /// + /// More information: + /// - [delete `UnaryOp` documentation][unary] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-delete-operator + /// [unary]: ../op/enum.UnaryOp.html#variant.Delete + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete + Delete, + + /// The `do` keyword. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-do-while-statement + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while + Do, + + /// The `else` keyword. + /// + /// More information: + /// - [if `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [node]: ../node/enum.Node.html#variant.If + /// [spec]: https://tc39.es/ecma262/#prod-IfStatement + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else + Else, + + /// The `enum` keyword. + /// + /// Future reserved keyword. + Enum, + + /// The `export` keyword. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-exports + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export + Export, + + /// The `extends` keyword. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-ClassHeritage + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends + Extends, + + /// The `false` keyword. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-BooleanLiteral + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean + False, + + /// The `finally` keyword. + /// + /// More information: + /// - [try `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [node]: ../node/enum.Node.html#variant.Try + /// [spec]: https://tc39.es/ecma262/#prod-Finally + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch + Finally, + + /// The `for` keyword. + /// + /// More information: + /// - [for loop `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [node]: ../node/enum.Node.html#variant.ForLoop + /// [spec]: https://tc39.es/ecma262/#prod-ForDeclaration + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for + For, + + /// The `function` keyword. + /// + /// More information: + /// - [function `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [node]: ../node/enum.Node.html#variant.FunctionDecl + /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-function + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function + Function, + + /// The `if` keyword. + /// + /// More information: + /// - [if `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [node]: ../node/enum.Node.html#variant.If + /// [spec]: https://tc39.es/ecma262/#prod-IfStatement + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else + If, + + /// The `in` keyword. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in + In, + + /// The `instanceof` keyword. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-instanceofoperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof + InstanceOf, + + /// The `import` keyword. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-imports + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import + Import, + + /// The `let` keyword. + /// + /// More information: + /// - [let `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [node]: ../node/enum.Node.html#variant.LetDecl + /// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let + Let, + + /// The `new` keyword. + /// + /// More information: + /// - [new `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [node]: ../node/enum.Node.html#variant.New + /// [spec]: https://tc39.es/ecma262/#prod-NewExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new + New, + + /// The `null` keyword. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-NullLiteral + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null + Null, + + /// The `of` keyword. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-for-in-and-for-of-statements + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of + Of, + + /// The `return` keyword + /// + /// More information: + /// - [return `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [node]: ../node/enum.Node.html#variant.Return + /// [spec]: https://tc39.es/ecma262/#prod-ReturnStatement + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return + Return, + + /// The `super` keyword + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-super-keyword + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super + Super, + + /// The `switch` keyword. + /// + /// More information: + /// - [switch `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [node]: ../node/enum.Node.html#variant.Switch + /// [spec]: https://tc39.es/ecma262/#prod-SwitchStatement + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch + Switch, + + /// The `this` keyword. + /// + /// More information: + /// - [this `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [node]: ../node/enum.Node.html#variant.This + /// [spec]: https://tc39.es/ecma262/#sec-this-keyword + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this + This, + + /// The `throw` keyword. + /// + /// More information: + /// - [throw `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [node]: ../node/enum.Node.html#variant.Throw + /// [spec]: https://tc39.es/ecma262/#sec-throw-statement + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw + Throw, + + /// The `true` keyword + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-BooleanLiteral + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean + True, + + /// The `try` keyword. + /// + /// More information: + /// - [try `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [node]: ../node/enum.Node.html#variant.Try + /// [spec]: https://tc39.es/ecma262/#prod-TryStatement + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch + Try, + + /// The `typeof` keyword. + /// + /// More information: + /// - [typeof `UnaryOp` documentation][unary] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [unary]: ../op/enum.UnaryOp.html#variant.TypeOf + /// [spec]: https://tc39.es/ecma262/#sec-typeof-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof + TypeOf, + + /// The `var` keyword. + /// + /// More information: + /// - [var `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [node]: ../node/enum.Node.html#variant.VarDecl + /// [spec]: https://tc39.es/ecma262/#prod-VariableStatement + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var + Var, + + /// The `void` keyword. + /// + /// More information: + /// - [void `UnaryOp` documentation][unary] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [unary]: ../op/enum.UnaryOp.html#variant.Void + /// [spec]: https://tc39.es/ecma262/#sec-void-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void + Void, + + /// The `while` keyword. + /// + /// More information: + /// - [while `Node` documentation][node] + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [node]: ../node/enum.Node.html#variant.While + /// [spec]: https://tc39.es/ecma262/#prod-grammar-notation-WhileStatement + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while + While, + + /// The `with` keyword. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-WithStatement + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with + With, + + /// The 'yield' keyword. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-YieldExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield + Yield, +} + +impl Keyword { + /// Gets the keyword as a binary operation, if this keyword is the `in` or the `instanceof` + /// keywords. + #[must_use] + pub const fn as_binary_op(self) -> Option { + match self { + Self::In => Some(BinaryOp::Relational(RelationalOp::In)), + Self::InstanceOf => Some(BinaryOp::Relational(RelationalOp::InstanceOf)), + _ => None, + } + } + + /// Gets the keyword as a tuple of strings. + #[must_use] + pub const fn as_str(self) -> (&'static str, &'static [u16]) { + match self { + Self::Await => ("await", utf16!("await")), + Self::Async => ("async", utf16!("async")), + Self::Break => ("break", utf16!("break")), + Self::Case => ("case", utf16!("case")), + Self::Catch => ("catch", utf16!("catch")), + Self::Class => ("class", utf16!("class")), + Self::Continue => ("continue", utf16!("continue")), + Self::Const => ("const", utf16!("const")), + Self::Debugger => ("debugger", utf16!("debugger")), + Self::Default => ("default", utf16!("default")), + Self::Delete => ("delete", utf16!("delete")), + Self::Do => ("do", utf16!("do")), + Self::Else => ("else", utf16!("else")), + Self::Enum => ("enum", utf16!("enum")), + Self::Extends => ("extends", utf16!("extends")), + Self::Export => ("export", utf16!("export")), + Self::False => ("false", utf16!("false")), + Self::Finally => ("finally", utf16!("finally")), + Self::For => ("for", utf16!("for")), + Self::Function => ("function", utf16!("function")), + Self::If => ("if", utf16!("if")), + Self::In => ("in", utf16!("in")), + Self::InstanceOf => ("instanceof", utf16!("instanceof")), + Self::Import => ("import", utf16!("import")), + Self::Let => ("let", utf16!("let")), + Self::New => ("new", utf16!("new")), + Self::Null => ("null", utf16!("null")), + Self::Of => ("of", utf16!("of")), + Self::Return => ("return", utf16!("return")), + Self::Super => ("super", utf16!("super")), + Self::Switch => ("switch", utf16!("switch")), + Self::This => ("this", utf16!("this")), + Self::Throw => ("throw", utf16!("throw")), + Self::True => ("true", utf16!("true")), + Self::Try => ("try", utf16!("try")), + Self::TypeOf => ("typeof", utf16!("typeof")), + Self::Var => ("var", utf16!("var")), + Self::Void => ("void", utf16!("void")), + Self::While => ("while", utf16!("while")), + Self::With => ("with", utf16!("with")), + Self::Yield => ("yield", utf16!("yield")), + } + } + + // TODO: promote all keywords to statics inside Interner + /// Converts the keyword to a symbol in the given interner. + pub fn to_sym(self, interner: &mut Interner) -> Sym { + let (utf8, utf16) = self.as_str(); + interner.get_or_intern_static(utf8, utf16) + } +} + +// TODO: Should use a proper Error +impl TryFrom for BinaryOp { + type Error = String; + + fn try_from(value: Keyword) -> Result { + value + .as_binary_op() + .ok_or_else(|| format!("No binary operation for {value}")) + } +} + +/// The error type which is returned from parsing a [`str`] into a [`Keyword`]. +#[derive(Debug, Clone, Copy)] +pub struct KeywordError; +impl fmt::Display for KeywordError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "invalid token") + } +} + +// This is important for other errors to wrap this one. +impl error::Error for KeywordError { + fn description(&self) -> &str { + "invalid token" + } +} +impl FromStr for Keyword { + type Err = KeywordError; + fn from_str(s: &str) -> Result { + match s { + "await" => Ok(Self::Await), + "async" => Ok(Self::Async), + "break" => Ok(Self::Break), + "case" => Ok(Self::Case), + "catch" => Ok(Self::Catch), + "class" => Ok(Self::Class), + "continue" => Ok(Self::Continue), + "const" => Ok(Self::Const), + "debugger" => Ok(Self::Debugger), + "default" => Ok(Self::Default), + "delete" => Ok(Self::Delete), + "do" => Ok(Self::Do), + "else" => Ok(Self::Else), + "enum" => Ok(Self::Enum), + "extends" => Ok(Self::Extends), + "export" => Ok(Self::Export), + "false" => Ok(Self::False), + "finally" => Ok(Self::Finally), + "for" => Ok(Self::For), + "function" => Ok(Self::Function), + "if" => Ok(Self::If), + "in" => Ok(Self::In), + "instanceof" => Ok(Self::InstanceOf), + "import" => Ok(Self::Import), + "let" => Ok(Self::Let), + "new" => Ok(Self::New), + "null" => Ok(Self::Null), + "of" => Ok(Self::Of), + "return" => Ok(Self::Return), + "super" => Ok(Self::Super), + "switch" => Ok(Self::Switch), + "this" => Ok(Self::This), + "throw" => Ok(Self::Throw), + "true" => Ok(Self::True), + "try" => Ok(Self::Try), + "typeof" => Ok(Self::TypeOf), + "var" => Ok(Self::Var), + "void" => Ok(Self::Void), + "while" => Ok(Self::While), + "with" => Ok(Self::With), + "yield" => Ok(Self::Yield), + _ => Err(KeywordError), + } + } +} + +impl fmt::Display for Keyword { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.as_str().0, f) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/lib.rs b/javascript-engine/external/boa/boa_ast/src/lib.rs new file mode 100644 index 0000000..49ad17f --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/lib.rs @@ -0,0 +1,174 @@ +//! Boa's **`boa_ast`** crate implements an ECMAScript abstract syntax tree. +//! +//! # Crate Overview +//! **`boa_ast`** contains representations of [**Parse Nodes**][grammar] as defined by the ECMAScript +//! spec. Some `Parse Node`s are not represented by Boa's AST, because a lot of grammar productions +//! are only used to throw [**Early Errors**][early], and don't influence the evaluation of the AST +//! itself. +//! +//! Boa's AST is mainly split in three main components: [`Declaration`]s, [`Expression`]s and +//! [`Statement`]s, with [`StatementList`] being the primordial Parse Node that combines +//! all of them to create a proper AST. +//! +//! # About Boa +//! Boa is an open-source, experimental ECMAScript Engine written in Rust for lexing, parsing and executing ECMAScript/JavaScript. Currently, Boa +//! supports some of the [language][boa-conformance]. More information can be viewed at [Boa's website][boa-web]. +//! +//! Try out the most recent release with Boa's live demo [playground][boa-playground]. +//! +//! # Boa Crates +//! - **`boa_ast`** - Boa's ECMAScript Abstract Syntax Tree. +//! - **`boa_engine`** - Boa's implementation of ECMAScript builtin objects and execution. +//! - **`boa_gc`** - Boa's garbage collector. +//! - **`boa_interner`** - Boa's string interner. +//! - **`boa_parser`** - Boa's lexer and parser. +//! - **`boa_profiler`** - Boa's code profiler. +//! - **`boa_unicode`** - Boa's Unicode identifier. +//! - **`boa_icu_provider`** - Boa's ICU4X data provider. +//! +//! [grammar]: https://tc39.es/ecma262/#sec-syntactic-grammar +//! [early]: https://tc39.es/ecma262/#sec-static-semantic-rules +//! [boa-conformance]: https://boa-dev.github.io/boa/test262/ +//! [boa-web]: https://boa-dev.github.io/ +//! [boa-playground]: https://boa-dev.github.io/boa/playground/ + +#![doc( + html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg" +)] +#![cfg_attr(not(test), forbid(clippy::unwrap_used))] +#![warn(missing_docs, clippy::dbg_macro)] +#![deny( + // rustc lint groups https://doc.rust-lang.org/rustc/lints/groups.html + warnings, + future_incompatible, + let_underscore, + nonstandard_style, + rust_2018_compatibility, + rust_2018_idioms, + rust_2021_compatibility, + unused, + + // rustc allowed-by-default lints https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html + macro_use_extern_crate, + meta_variable_misuse, + missing_abi, + missing_copy_implementations, + missing_debug_implementations, + non_ascii_idents, + noop_method_call, + single_use_lifetimes, + trivial_casts, + trivial_numeric_casts, + unreachable_pub, + unsafe_op_in_unsafe_fn, + unused_crate_dependencies, + unused_import_braces, + unused_lifetimes, + unused_qualifications, + unused_tuple_struct_fields, + variant_size_differences, + + // rustdoc lints https://doc.rust-lang.org/rustdoc/lints.html + rustdoc::broken_intra_doc_links, + rustdoc::private_intra_doc_links, + rustdoc::missing_crate_level_docs, + rustdoc::private_doc_tests, + rustdoc::invalid_codeblock_attributes, + rustdoc::invalid_rust_codeblocks, + rustdoc::bare_urls, + + // clippy categories https://doc.rust-lang.org/clippy/ + clippy::all, + clippy::correctness, + clippy::suspicious, + clippy::style, + clippy::complexity, + clippy::perf, + clippy::pedantic, + clippy::nursery, +)] +#![allow( + clippy::module_name_repetitions, + clippy::too_many_lines, + clippy::option_if_let_else, + clippy::use_self +)] + +mod position; +mod punctuator; +mod statement_list; + +pub mod declaration; +pub mod expression; +pub mod function; +pub mod keyword; +pub mod operations; +pub mod pattern; +pub mod property; +pub mod statement; +pub mod visitor; + +use boa_interner::{Interner, ToIndentedString, ToInternedString}; + +pub use self::{ + declaration::Declaration, + expression::Expression, + keyword::Keyword, + position::{Position, Span}, + punctuator::Punctuator, + statement::Statement, + statement_list::{StatementList, StatementListItem}, +}; + +/// Utility to join multiple Nodes into a single string. +fn join_nodes(interner: &Interner, nodes: &[N]) -> String +where + N: ToInternedString, +{ + let mut first = true; + let mut buf = String::new(); + for e in nodes { + if first { + first = false; + } else { + buf.push_str(", "); + } + buf.push_str(&e.to_interned_string(interner)); + } + buf +} + +/// Displays the body of a block or statement list. +/// +/// This includes the curly braces at the start and end. This will not indent the first brace, +/// but will indent the last brace. +fn block_to_string(body: &StatementList, interner: &Interner, indentation: usize) -> String { + if body.statements().is_empty() { + "{}".to_owned() + } else { + format!( + "{{\n{}{}}}", + body.to_indented_string(interner, indentation + 1), + " ".repeat(indentation) + ) + } +} + +/// Utility trait that adds a `UTF-16` escaped representation to every [`[u16]`][slice]. +trait ToStringEscaped { + /// Decodes `self` as an `UTF-16` encoded string, escaping any unpaired surrogates by its + /// codepoint value. + fn to_string_escaped(&self) -> String; +} + +impl ToStringEscaped for [u16] { + fn to_string_escaped(&self) -> String { + char::decode_utf16(self.iter().copied()) + .map(|r| match r { + Ok(c) => String::from(c), + Err(e) => format!("\\u{:04X}", e.unpaired_surrogate()), + }) + .collect() + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/operations.rs b/javascript-engine/external/boa/boa_ast/src/operations.rs new file mode 100644 index 0000000..f4a7e65 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/operations.rs @@ -0,0 +1,769 @@ +//! Definitions of various **Syntax-Directed Operations** used in the [spec]. +//! +//! [spec]: https://tc39.es/ecma262/#sec-syntax-directed-operations + +use core::ops::ControlFlow; +use std::convert::Infallible; + +use boa_interner::Sym; +use rustc_hash::{FxHashMap, FxHashSet}; + +use crate::{ + declaration::VarDeclaration, + expression::{access::SuperPropertyAccess, Await, Identifier, SuperCall, Yield}, + function::{ + ArrowFunction, AsyncArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, + Function, Generator, PrivateName, + }, + property::{MethodDefinition, PropertyDefinition}, + statement::LabelledItem, + try_break, + visitor::{NodeRef, VisitWith, Visitor, VisitorMut}, + Declaration, Expression, Statement, StatementList, StatementListItem, +}; + +/// Represents all the possible symbols searched for by the [`Contains`][contains] operation. +/// +/// [contains]: https://tc39.es/ecma262/#sec-syntax-directed-operations-contains +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum ContainsSymbol { + /// A node with the `super` keyword (`super(args)` or `super.prop`). + Super, + /// A super property access (`super.prop`). + SuperProperty, + /// A super constructor call (`super(args)`). + SuperCall, + /// A yield expression (`yield 5`). + YieldExpression, + /// An await expression (`await 4`). + AwaitExpression, + /// The new target expression (`new.target`). + NewTarget, + /// The body of a class definition. + ClassBody, + /// The super class of a class definition. + ClassHeritage, + /// A this expression (`this`). + This, + /// A method definition. + MethodDefinition, + /// The BindingIdentifier "eval" or "arguments". + EvalOrArguments, +} + +/// Returns `true` if the node contains the given symbol. +/// +/// This is equivalent to the [`Contains`][spec] syntax operation in the spec. +/// +/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains +#[must_use] +pub fn contains(node: &N, symbol: ContainsSymbol) -> bool +where + N: VisitWith, +{ + /// Visitor used by the function to search for a specific symbol in a node. + #[derive(Debug, Clone, Copy)] + struct ContainsVisitor(ContainsSymbol); + + impl<'ast> Visitor<'ast> for ContainsVisitor { + type BreakTy = (); + + fn visit_identifier(&mut self, node: &'ast Identifier) -> ControlFlow { + if self.0 == ContainsSymbol::EvalOrArguments + && (node.sym() == Sym::EVAL || node.sym() == Sym::ARGUMENTS) + { + return ControlFlow::Break(()); + } + ControlFlow::Continue(()) + } + + fn visit_function(&mut self, _: &'ast Function) -> ControlFlow { + ControlFlow::Continue(()) + } + + fn visit_async_function(&mut self, _: &'ast AsyncFunction) -> ControlFlow { + ControlFlow::Continue(()) + } + + fn visit_generator(&mut self, _: &'ast Generator) -> ControlFlow { + ControlFlow::Continue(()) + } + + fn visit_async_generator(&mut self, _: &'ast AsyncGenerator) -> ControlFlow { + ControlFlow::Continue(()) + } + + fn visit_class(&mut self, node: &'ast Class) -> ControlFlow { + if !node.elements().is_empty() && self.0 == ContainsSymbol::ClassBody { + return ControlFlow::Break(()); + } + + if node.super_ref().is_some() && self.0 == ContainsSymbol::ClassHeritage { + return ControlFlow::Break(()); + } + + node.visit_with(self) + } + + // `ComputedPropertyContains`: https://tc39.es/ecma262/#sec-static-semantics-computedpropertycontains + fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow { + match node { + ClassElement::MethodDefinition(name, _) + | ClassElement::StaticMethodDefinition(name, _) + | ClassElement::FieldDefinition(name, _) + | ClassElement::StaticFieldDefinition(name, _) => name.visit_with(self), + _ => ControlFlow::Continue(()), + } + } + + fn visit_property_definition( + &mut self, + node: &'ast PropertyDefinition, + ) -> ControlFlow { + if let PropertyDefinition::MethodDefinition(name, _) = node { + if self.0 == ContainsSymbol::MethodDefinition { + return ControlFlow::Break(()); + } + return name.visit_with(self); + } + + node.visit_with(self) + } + + fn visit_arrow_function( + &mut self, + node: &'ast ArrowFunction, + ) -> ControlFlow { + if ![ + ContainsSymbol::NewTarget, + ContainsSymbol::SuperProperty, + ContainsSymbol::SuperCall, + ContainsSymbol::Super, + ContainsSymbol::This, + ] + .contains(&self.0) + { + return ControlFlow::Continue(()); + } + + node.visit_with(self) + } + + fn visit_async_arrow_function( + &mut self, + node: &'ast AsyncArrowFunction, + ) -> ControlFlow { + if ![ + ContainsSymbol::NewTarget, + ContainsSymbol::SuperProperty, + ContainsSymbol::SuperCall, + ContainsSymbol::Super, + ContainsSymbol::This, + ] + .contains(&self.0) + { + return ControlFlow::Continue(()); + } + + node.visit_with(self) + } + + fn visit_super_property_access( + &mut self, + node: &'ast SuperPropertyAccess, + ) -> ControlFlow { + if [ContainsSymbol::SuperProperty, ContainsSymbol::Super].contains(&self.0) { + return ControlFlow::Break(()); + } + node.visit_with(self) + } + + fn visit_super_call(&mut self, node: &'ast SuperCall) -> ControlFlow { + if [ContainsSymbol::SuperCall, ContainsSymbol::Super].contains(&self.0) { + return ControlFlow::Break(()); + } + node.visit_with(self) + } + + fn visit_yield(&mut self, node: &'ast Yield) -> ControlFlow { + if self.0 == ContainsSymbol::YieldExpression { + return ControlFlow::Break(()); + } + + node.visit_with(self) + } + + fn visit_await(&mut self, node: &'ast Await) -> ControlFlow { + if self.0 == ContainsSymbol::AwaitExpression { + return ControlFlow::Break(()); + } + + node.visit_with(self) + } + + fn visit_expression(&mut self, node: &'ast Expression) -> ControlFlow { + if node == &Expression::This && self.0 == ContainsSymbol::This { + return ControlFlow::Break(()); + } + if node == &Expression::NewTarget && self.0 == ContainsSymbol::NewTarget { + return ControlFlow::Break(()); + } + node.visit_with(self) + } + } + + node.visit_with(&mut ContainsVisitor(symbol)).is_break() +} + +/// Returns true if the node contains an identifier reference with name `arguments`. +/// +/// This is equivalent to the [`ContainsArguments`][spec] syntax operation in the spec. +/// +/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments +#[must_use] +pub fn contains_arguments(node: &N) -> bool +where + N: VisitWith, +{ + /// Visitor used by the function to search for an identifier with the name `arguments`. + #[derive(Debug, Clone, Copy)] + struct ContainsArgsVisitor; + + impl<'ast> Visitor<'ast> for ContainsArgsVisitor { + type BreakTy = (); + + fn visit_identifier(&mut self, node: &'ast Identifier) -> ControlFlow { + if node.sym() == Sym::ARGUMENTS { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + } + + fn visit_function(&mut self, _: &'ast Function) -> ControlFlow { + ControlFlow::Continue(()) + } + + fn visit_async_function(&mut self, _: &'ast AsyncFunction) -> ControlFlow { + ControlFlow::Continue(()) + } + + fn visit_generator(&mut self, _: &'ast Generator) -> ControlFlow { + ControlFlow::Continue(()) + } + + fn visit_async_generator(&mut self, _: &'ast AsyncGenerator) -> ControlFlow { + ControlFlow::Continue(()) + } + + fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow { + match node { + ClassElement::MethodDefinition(name, _) + | ClassElement::StaticMethodDefinition(name, _) => return name.visit_with(self), + _ => {} + } + node.visit_with(self) + } + + fn visit_property_definition( + &mut self, + node: &'ast PropertyDefinition, + ) -> ControlFlow { + if let PropertyDefinition::MethodDefinition(name, _) = node { + name.visit_with(self) + } else { + node.visit_with(self) + } + } + } + node.visit_with(&mut ContainsArgsVisitor).is_break() +} + +/// Returns `true` if `method` has a super call in its parameters or body. +/// +/// This is equivalent to the [`HasDirectSuper`][spec] syntax operation in the spec. +/// +/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-hasdirectsuper +#[must_use] +#[inline] +pub fn has_direct_super(method: &MethodDefinition) -> bool { + match method { + MethodDefinition::Get(f) | MethodDefinition::Set(f) | MethodDefinition::Ordinary(f) => { + contains(f, ContainsSymbol::SuperCall) + } + MethodDefinition::Generator(f) => contains(f, ContainsSymbol::SuperCall), + MethodDefinition::AsyncGenerator(f) => contains(f, ContainsSymbol::SuperCall), + MethodDefinition::Async(f) => contains(f, ContainsSymbol::SuperCall), + } +} + +/// A container that [`BoundNamesVisitor`] can use to push the found identifiers. +trait IdentList { + fn add(&mut self, value: Identifier, function: bool); +} + +impl IdentList for Vec { + fn add(&mut self, value: Identifier, _function: bool) { + self.push(value); + } +} + +impl IdentList for Vec<(Identifier, bool)> { + fn add(&mut self, value: Identifier, function: bool) { + self.push((value, function)); + } +} + +impl IdentList for FxHashSet { + fn add(&mut self, value: Identifier, _function: bool) { + self.insert(value); + } +} + +/// The [`Visitor`] used to obtain the bound names of a node. +#[derive(Debug)] +struct BoundNamesVisitor<'a, T: IdentList>(&'a mut T); + +impl<'ast, T: IdentList> Visitor<'ast> for BoundNamesVisitor<'_, T> { + type BreakTy = Infallible; + + fn visit_identifier(&mut self, node: &'ast Identifier) -> ControlFlow { + self.0.add(*node, false); + ControlFlow::Continue(()) + } + + fn visit_expression(&mut self, _: &'ast Expression) -> ControlFlow { + ControlFlow::Continue(()) + } + + // TODO: add "*default" for module default functions without name + fn visit_function(&mut self, node: &'ast Function) -> ControlFlow { + if let Some(ident) = node.name() { + self.0.add(ident, true); + } + ControlFlow::Continue(()) + } + + fn visit_generator(&mut self, node: &'ast Generator) -> ControlFlow { + if let Some(ident) = node.name() { + self.0.add(ident, false); + } + ControlFlow::Continue(()) + } + + fn visit_async_function(&mut self, node: &'ast AsyncFunction) -> ControlFlow { + if let Some(ident) = node.name() { + self.0.add(ident, false); + } + ControlFlow::Continue(()) + } + + fn visit_async_generator(&mut self, node: &'ast AsyncGenerator) -> ControlFlow { + if let Some(ident) = node.name() { + self.0.add(ident, false); + } + ControlFlow::Continue(()) + } + + fn visit_class(&mut self, node: &'ast Class) -> ControlFlow { + if let Some(ident) = node.name() { + self.0.add(ident, false); + } + ControlFlow::Continue(()) + } +} + +/// Returns a list with the bound names of an AST node, which may contain duplicates. +/// +/// This is equivalent to the [`BoundNames`][spec] syntax operation in the spec. +/// +/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-boundnames +#[must_use] +pub fn bound_names<'a, N>(node: &'a N) -> Vec +where + &'a N: Into>, +{ + let mut names = Vec::new(); + BoundNamesVisitor(&mut names).visit(node.into()); + + names +} + +/// The [`Visitor`] used to obtain the lexically declared names of a node. +#[derive(Debug)] +struct LexicallyDeclaredNamesVisitor<'a, T: IdentList>(&'a mut T); + +impl<'ast, T: IdentList> Visitor<'ast> for LexicallyDeclaredNamesVisitor<'_, T> { + type BreakTy = Infallible; + + fn visit_expression(&mut self, _: &'ast Expression) -> ControlFlow { + ControlFlow::Continue(()) + } + fn visit_statement(&mut self, node: &'ast Statement) -> ControlFlow { + if let Statement::Labelled(labelled) = node { + return self.visit_labelled(labelled); + } + ControlFlow::Continue(()) + } + fn visit_declaration(&mut self, node: &'ast Declaration) -> ControlFlow { + BoundNamesVisitor(self.0).visit_declaration(node) + } + fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow { + match node { + LabelledItem::Function(f) => BoundNamesVisitor(self.0).visit_function(f), + LabelledItem::Statement(_) => ControlFlow::Continue(()), + } + } + fn visit_function(&mut self, node: &'ast Function) -> ControlFlow { + top_level_lexicals(node.body(), self.0); + ControlFlow::Continue(()) + } + fn visit_async_function(&mut self, node: &'ast AsyncFunction) -> ControlFlow { + top_level_lexicals(node.body(), self.0); + ControlFlow::Continue(()) + } + fn visit_generator(&mut self, node: &'ast Generator) -> ControlFlow { + top_level_lexicals(node.body(), self.0); + ControlFlow::Continue(()) + } + fn visit_async_generator(&mut self, node: &'ast AsyncGenerator) -> ControlFlow { + top_level_lexicals(node.body(), self.0); + ControlFlow::Continue(()) + } + fn visit_arrow_function(&mut self, node: &'ast ArrowFunction) -> ControlFlow { + top_level_lexicals(node.body(), self.0); + ControlFlow::Continue(()) + } + fn visit_async_arrow_function( + &mut self, + node: &'ast AsyncArrowFunction, + ) -> ControlFlow { + top_level_lexicals(node.body(), self.0); + ControlFlow::Continue(()) + } + fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow { + if let ClassElement::StaticBlock(stmts) = node { + top_level_lexicals(stmts, self.0); + } + ControlFlow::Continue(()) + } + + // TODO: ScriptBody : StatementList + // 1. Return TopLevelLexicallyDeclaredNames of StatementList. + // But we don't have that node yet. In the meantime, use `top_level_lexically_declared_names` directly. +} + +/// Returns a list with the lexical bindings of a node, which may contain duplicates. +/// +/// This is equivalent to the [`LexicallyDeclaredNames`][spec] syntax operation in the spec. +/// +/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-lexicallydeclarednames +#[must_use] +pub fn lexically_declared_names<'a, N>(node: &'a N) -> Vec +where + &'a N: Into>, +{ + let mut names = Vec::new(); + LexicallyDeclaredNamesVisitor(&mut names).visit(node.into()); + names +} + +/// Returns a list with the lexical bindings of a node, which may contain duplicates. +/// +/// If a declared name originates from a function declaration it is flagged as `true` in the returned +/// list. (See [B.3.2.4 Changes to Block Static Semantics: Early Errors]) +/// +/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-lexicallydeclarednames +/// [changes]: https://tc39.es/ecma262/#sec-block-duplicates-allowed-static-semantics +#[must_use] +pub fn lexically_declared_names_legacy<'a, N>(node: &'a N) -> Vec<(Identifier, bool)> +where + &'a N: Into>, +{ + let mut names = Vec::new(); + LexicallyDeclaredNamesVisitor(&mut names).visit(node.into()); + names +} + +/// The [`Visitor`] used to obtain the var declared names of a node. +#[derive(Debug)] +struct VarDeclaredNamesVisitor<'a>(&'a mut FxHashSet); + +impl<'ast> Visitor<'ast> for VarDeclaredNamesVisitor<'_> { + type BreakTy = Infallible; + + fn visit_expression(&mut self, _: &'ast Expression) -> ControlFlow { + ControlFlow::Continue(()) + } + + fn visit_declaration(&mut self, _: &'ast Declaration) -> ControlFlow { + ControlFlow::Continue(()) + } + + fn visit_var_declaration(&mut self, node: &'ast VarDeclaration) -> ControlFlow { + BoundNamesVisitor(self.0).visit_var_declaration(node) + } + + fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow { + match node { + LabelledItem::Function(_) => ControlFlow::Continue(()), + LabelledItem::Statement(stmt) => stmt.visit_with(self), + } + } + + fn visit_function(&mut self, node: &'ast Function) -> ControlFlow { + top_level_vars(node.body(), self.0); + ControlFlow::Continue(()) + } + + fn visit_async_function(&mut self, node: &'ast AsyncFunction) -> ControlFlow { + top_level_vars(node.body(), self.0); + ControlFlow::Continue(()) + } + + fn visit_generator(&mut self, node: &'ast Generator) -> ControlFlow { + top_level_vars(node.body(), self.0); + ControlFlow::Continue(()) + } + + fn visit_async_generator(&mut self, node: &'ast AsyncGenerator) -> ControlFlow { + top_level_vars(node.body(), self.0); + ControlFlow::Continue(()) + } + + fn visit_arrow_function(&mut self, node: &'ast ArrowFunction) -> ControlFlow { + top_level_vars(node.body(), self.0); + ControlFlow::Continue(()) + } + + fn visit_async_arrow_function( + &mut self, + node: &'ast AsyncArrowFunction, + ) -> ControlFlow { + top_level_vars(node.body(), self.0); + ControlFlow::Continue(()) + } + + fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow { + if let ClassElement::StaticBlock(stmts) = node { + top_level_vars(stmts, self.0); + } + node.visit_with(self) + } + + // TODO: ScriptBody : StatementList + // 1. Return TopLevelVarDeclaredNames of StatementList. + // But we don't have that node yet. In the meantime, use `top_level_var_declared_names` directly. +} + +/// Returns a set with the var bindings of a node, with no duplicates. +/// +/// This is equivalent to the [`VarDeclaredNames`][spec] syntax operation in the spec. +/// +/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-vardeclarednames +#[must_use] +pub fn var_declared_names<'a, N>(node: &'a N) -> FxHashSet +where + &'a N: Into>, +{ + let mut names = FxHashSet::default(); + VarDeclaredNamesVisitor(&mut names).visit(node.into()); + names +} + +/// Utility function that collects the top level lexicals of a statement list into `names`. +fn top_level_lexicals(stmts: &StatementList, names: &mut T) { + for stmt in stmts.statements() { + if let StatementListItem::Declaration(decl) = stmt { + match decl { + // Note + // At the top level of a function, or script, function declarations are treated like + // var declarations rather than like lexical declarations. + Declaration::Function(_) + | Declaration::Generator(_) + | Declaration::AsyncFunction(_) + | Declaration::AsyncGenerator(_) => {} + Declaration::Class(class) => { + BoundNamesVisitor(names).visit_class(class); + } + Declaration::Lexical(decl) => { + BoundNamesVisitor(names).visit_lexical_declaration(decl); + } + } + } + } +} + +/// Returns a list with the lexical bindings of a top-level statement list, which may contain duplicates. +/// +/// This is equivalent to the [`TopLevelLexicallyDeclaredNames`][spec] syntax operation in the spec. +/// +/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-toplevellexicallydeclarednames +#[must_use] +#[inline] +pub fn top_level_lexically_declared_names(stmts: &StatementList) -> Vec { + let mut names = Vec::new(); + top_level_lexicals(stmts, &mut names); + names +} + +/// Utility function that collects the top level vars of a statement list into `names`. +fn top_level_vars(stmts: &StatementList, names: &mut FxHashSet) { + for stmt in stmts.statements() { + match stmt { + StatementListItem::Declaration(decl) => { + match decl { + // Note + // At the top level of a function, or script, function declarations are treated like + // var declarations rather than like lexical declarations. + Declaration::Function(f) => { + BoundNamesVisitor(names).visit_function(f); + } + Declaration::Generator(f) => { + BoundNamesVisitor(names).visit_generator(f); + } + Declaration::AsyncFunction(f) => { + BoundNamesVisitor(names).visit_async_function(f); + } + Declaration::AsyncGenerator(f) => { + BoundNamesVisitor(names).visit_async_generator(f); + } + Declaration::Class(_) | Declaration::Lexical(_) => {} + } + } + StatementListItem::Statement(stmt) => { + let mut stmt = Some(stmt); + while let Some(Statement::Labelled(labelled)) = stmt { + match labelled.item() { + LabelledItem::Function(f) => { + BoundNamesVisitor(names).visit_function(f); + stmt = None; + } + LabelledItem::Statement(s) => stmt = Some(s), + } + } + if let Some(stmt) = stmt { + VarDeclaredNamesVisitor(names).visit(stmt); + } + } + } + } +} + +/// Returns a list with the var bindings of a top-level statement list, with no duplicates. +/// +/// This is equivalent to the [`TopLevelVarDeclaredNames`][spec] syntax operation in the spec. +/// +/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-toplevelvardeclarednames +#[must_use] +#[inline] +pub fn top_level_var_declared_names(stmts: &StatementList) -> FxHashSet { + let mut names = FxHashSet::default(); + top_level_vars(stmts, &mut names); + names +} + +/// Resolves the private names of a class and all of the contained classes and private identifiers. +pub fn class_private_name_resolver(node: &mut Class, top_level_class_index: usize) -> bool { + /// Visitor used by the function to search for an identifier with the name `arguments`. + #[derive(Debug, Clone)] + struct ClassPrivateNameResolver { + private_environments_stack: Vec>, + top_level_class_index: usize, + } + + impl<'ast> VisitorMut<'ast> for ClassPrivateNameResolver { + type BreakTy = (); + + #[inline] + fn visit_class_mut(&mut self, node: &'ast mut Class) -> ControlFlow { + let mut names = FxHashMap::default(); + + for element in node.elements.iter_mut() { + match element { + ClassElement::PrivateMethodDefinition(name, _) + | ClassElement::PrivateStaticMethodDefinition(name, _) + | ClassElement::PrivateFieldDefinition(name, _) + | ClassElement::PrivateStaticFieldDefinition(name, _) => { + name.indices = ( + self.top_level_class_index, + self.private_environments_stack.len(), + ); + names.insert(name.description(), *name); + } + _ => {} + } + } + + self.private_environments_stack.push(names); + + for element in node.elements.iter_mut() { + match element { + ClassElement::MethodDefinition(name, method) + | ClassElement::StaticMethodDefinition(name, method) => { + try_break!(self.visit_property_name_mut(name)); + try_break!(self.visit_method_definition_mut(method)); + } + ClassElement::PrivateMethodDefinition(_, method) + | ClassElement::PrivateStaticMethodDefinition(_, method) => { + try_break!(self.visit_method_definition_mut(method)); + } + ClassElement::FieldDefinition(name, expression) + | ClassElement::StaticFieldDefinition(name, expression) => { + try_break!(self.visit_property_name_mut(name)); + if let Some(expression) = expression { + try_break!(self.visit_expression_mut(expression)); + } + } + ClassElement::PrivateFieldDefinition(_, expression) + | ClassElement::PrivateStaticFieldDefinition(_, expression) => { + if let Some(expression) = expression { + try_break!(self.visit_expression_mut(expression)); + } + } + ClassElement::StaticBlock(statement_list) => { + try_break!(self.visit_statement_list_mut(statement_list)); + } + } + } + + if let Some(function) = &mut node.constructor { + try_break!(self.visit_function_mut(function)); + } + + self.private_environments_stack.pop(); + + ControlFlow::Continue(()) + } + + #[inline] + fn visit_private_name_mut( + &mut self, + node: &'ast mut PrivateName, + ) -> ControlFlow { + let mut found = false; + + for environment in self.private_environments_stack.iter().rev() { + if let Some(n) = environment.get(&node.description()) { + found = true; + node.indices = n.indices; + break; + } + } + + if found { + ControlFlow::Continue(()) + } else { + ControlFlow::Break(()) + } + } + } + + let mut visitor = ClassPrivateNameResolver { + private_environments_stack: Vec::new(), + top_level_class_index, + }; + + visitor.visit_class_mut(node).is_continue() +} diff --git a/javascript-engine/external/boa/boa_ast/src/pattern.rs b/javascript-engine/external/boa/boa_ast/src/pattern.rs new file mode 100644 index 0000000..a8b1644 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/pattern.rs @@ -0,0 +1,787 @@ +//! A pattern binding or assignment node. +//! +//! A [`Pattern`] Corresponds to the [`BindingPattern`][spec1] and the [`AssignmentPattern`][spec2] +//! nodes, each of which is used in different situations and have slightly different grammars. +//! For example, a variable declaration combined with a destructuring expression is a `BindingPattern`: +//! +//! ```Javascript +//! const obj = { a: 1, b: 2 }; +//! const { a, b } = obj; // BindingPattern +//! ``` +//! +//! On the other hand, a simple destructuring expression with already declared variables is called +//! an `AssignmentPattern`: +//! +//! ```Javascript +//! let a = 1; +//! let b = 3; +//! [a, b] = [b, a]; // AssignmentPattern +//! ``` +//! +//! [spec1]: https://tc39.es/ecma262/#prod-BindingPattern +//! [spec2]: https://tc39.es/ecma262/#prod-AssignmentPattern +//! [destr]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment + +use crate::{ + expression::{access::PropertyAccess, Identifier}, + property::PropertyName, + try_break, + visitor::{VisitWith, Visitor, VisitorMut}, + Expression, +}; +use boa_interner::{Interner, ToInternedString}; +use core::ops::ControlFlow; + +/// An object or array pattern binding or assignment. +/// +/// See the [module level documentation][self] for more information. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum Pattern { + /// An object pattern (`let {a, b, c} = object`). + Object(ObjectPattern), + /// An array pattern (`[a, b, c] = array`). + Array(ArrayPattern), +} + +impl From for Pattern { + fn from(obj: ObjectPattern) -> Self { + Self::Object(obj) + } +} + +impl From for Pattern { + fn from(obj: ArrayPattern) -> Self { + Self::Array(obj) + } +} + +impl From> for Pattern { + fn from(elements: Vec) -> Self { + ObjectPattern::new(elements.into()).into() + } +} +impl From> for Pattern { + fn from(elements: Vec) -> Self { + ArrayPattern::new(elements.into()).into() + } +} + +impl ToInternedString for Pattern { + fn to_interned_string(&self, interner: &Interner) -> String { + match &self { + Self::Object(o) => o.to_interned_string(interner), + Self::Array(a) => a.to_interned_string(interner), + } + } +} + +impl VisitWith for Pattern { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::Object(op) => visitor.visit_object_pattern(op), + Self::Array(ap) => visitor.visit_array_pattern(ap), + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::Object(op) => visitor.visit_object_pattern_mut(op), + Self::Array(ap) => visitor.visit_array_pattern_mut(ap), + } + } +} + +/// An object binding or assignment pattern. +/// +/// Corresponds to the [`ObjectBindingPattern`][spec1] and the [`ObjectAssignmentPattern`][spec2] +/// Parse Nodes. +/// +/// For more information on what is a valid binding in an object pattern, see [`ObjectPatternElement`]. +/// +/// [spec1]: https://tc39.es/ecma262/#prod-ObjectBindingPattern +/// [spec2]: https://tc39.es/ecma262/#prod-ObjectAssignmentPattern +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectPattern(Box<[ObjectPatternElement]>); + +impl From> for ObjectPattern { + fn from(elements: Vec) -> Self { + Self(elements.into()) + } +} + +impl ToInternedString for ObjectPattern { + fn to_interned_string(&self, interner: &Interner) -> String { + let mut buf = "{".to_owned(); + for (i, binding) in self.0.iter().enumerate() { + let binding = binding.to_interned_string(interner); + let str = if i == self.0.len() - 1 { + format!("{binding} ") + } else { + format!("{binding},") + }; + + buf.push_str(&str); + } + if self.0.is_empty() { + buf.push(' '); + } + buf.push('}'); + buf + } +} + +impl ObjectPattern { + /// Creates a new object binding pattern. + #[inline] + #[must_use] + pub fn new(bindings: Box<[ObjectPatternElement]>) -> Self { + Self(bindings) + } + + /// Gets the bindings for the object binding pattern. + #[inline] + #[must_use] + pub const fn bindings(&self) -> &[ObjectPatternElement] { + &self.0 + } + + /// Returns true if the object binding pattern has a rest element. + #[inline] + #[must_use] + pub const fn has_rest(&self) -> bool { + matches!( + self.0.last(), + Some(ObjectPatternElement::RestProperty { .. }) + ) + } +} + +impl VisitWith for ObjectPattern { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + for elem in self.0.iter() { + try_break!(visitor.visit_object_pattern_element(elem)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + for elem in self.0.iter_mut() { + try_break!(visitor.visit_object_pattern_element_mut(elem)); + } + ControlFlow::Continue(()) + } +} + +/// An array binding or assignment pattern. +/// +/// Corresponds to the [`ArrayBindingPattern`][spec1] and the [`ArrayAssignmentPattern`][spec2] +/// Parse Nodes. +/// +/// For more information on what is a valid binding in an array pattern, see [`ArrayPatternElement`]. +/// +/// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern +/// [spec2]: https://tc39.es/ecma262/#prod-ArrayAssignmentPattern +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct ArrayPattern(Box<[ArrayPatternElement]>); + +impl From> for ArrayPattern { + fn from(elements: Vec) -> Self { + Self(elements.into()) + } +} + +impl ToInternedString for ArrayPattern { + fn to_interned_string(&self, interner: &Interner) -> String { + let mut buf = "[".to_owned(); + for (i, binding) in self.0.iter().enumerate() { + if i == self.0.len() - 1 { + match binding { + ArrayPatternElement::Elision => { + buf.push_str(&format!("{}, ", binding.to_interned_string(interner))); + } + _ => buf.push_str(&format!("{} ", binding.to_interned_string(interner))), + } + } else { + buf.push_str(&format!("{},", binding.to_interned_string(interner))); + } + } + buf.push(']'); + buf + } +} + +impl ArrayPattern { + /// Creates a new array binding pattern. + #[inline] + #[must_use] + pub fn new(bindings: Box<[ArrayPatternElement]>) -> Self { + Self(bindings) + } + + /// Gets the bindings for the array binding pattern. + #[inline] + #[must_use] + pub const fn bindings(&self) -> &[ArrayPatternElement] { + &self.0 + } +} + +impl VisitWith for ArrayPattern { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + for elem in self.0.iter() { + try_break!(visitor.visit_array_pattern_element(elem)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + for elem in self.0.iter_mut() { + try_break!(visitor.visit_array_pattern_element_mut(elem)); + } + ControlFlow::Continue(()) + } +} + +/// The different types of bindings that an [`ObjectPattern`] may contain. +/// +/// Corresponds to the [`BindingProperty`][spec1] and the [`AssignmentProperty`][spec2] nodes. +/// +/// [spec1]: https://tc39.es/ecma262/#prod-BindingProperty +/// [spec2]: https://tc39.es/ecma262/#prod-AssignmentProperty +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum ObjectPatternElement { + /// SingleName represents one of the following properties: + /// + /// - `SingleName` with an identifier and an optional default initializer. + /// - `BindingProperty` with an property name and a `SingleNameBinding` as the `BindingElement`. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - SingleNameBinding][spec1] + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingProperty][spec2] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-SingleNameBinding + /// [spec2]: https://tc39.es/ecma262/#prod-BindingProperty + SingleName { + /// The identifier name of the property to be destructured. + name: PropertyName, + /// The variable name where the property value will be stored. + ident: Identifier, + /// An optional default value for the variable, in case the property doesn't exist. + default_init: Option, + }, + + /// RestProperty represents a `BindingRestProperty` with an identifier. + /// + /// It also includes a list of the property keys that should be excluded from the rest, + /// because they where already assigned. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestProperty][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestProperty + RestProperty { + /// The variable name where the unassigned properties will be stored. + ident: Identifier, + /// A list of the excluded property keys that were already destructured. + excluded_keys: Vec, + }, + + /// AssignmentGetField represents an AssignmentProperty with an expression field member expression AssignmentElement. + /// + /// Note: According to the spec this is not part of an ObjectBindingPattern. + /// This is only used when a object literal is used to cover an AssignmentPattern. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentProperty + AssignmentPropertyAccess { + /// The identifier name of the property to be destructured. + name: PropertyName, + /// The property access where the property value will be destructured. + access: PropertyAccess, + /// An optional default value for the variable, in case the property doesn't exist. + default_init: Option, + }, + + /// AssignmentRestProperty represents a rest property with a DestructuringAssignmentTarget. + /// + /// Note: According to the spec this is not part of an ObjectBindingPattern. + /// This is only used when a object literal is used to cover an AssignmentPattern. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentRestProperty + AssignmentRestPropertyAccess { + /// The property access where the unassigned properties will be stored. + access: PropertyAccess, + /// A list of the excluded property keys that were already destructured. + excluded_keys: Vec, + }, + + /// Pattern represents a property with a `Pattern` as the element. + /// + /// Additionally to the identifier of the new property and the nested pattern, + /// this may also include an optional default initializer. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingProperty][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-BindingProperty + Pattern { + /// The identifier name of the property to be destructured. + name: PropertyName, + /// The pattern where the property value will be destructured. + pattern: Pattern, + /// An optional default value for the variable, in case the property doesn't exist. + default_init: Option, + }, +} + +impl ToInternedString for ObjectPatternElement { + fn to_interned_string(&self, interner: &Interner) -> String { + match self { + Self::SingleName { + ident, + name, + default_init, + } => { + let mut buf = match name { + PropertyName::Literal(name) if name == ident => { + format!(" {}", interner.resolve_expect(ident.sym())) + } + PropertyName::Literal(name) => { + format!( + " {} : {}", + interner.resolve_expect(*name), + interner.resolve_expect(ident.sym()) + ) + } + PropertyName::Computed(node) => { + format!( + " [{}] : {}", + node.to_interned_string(interner), + interner.resolve_expect(ident.sym()) + ) + } + }; + if let Some(ref init) = default_init { + buf.push_str(&format!(" = {}", init.to_interned_string(interner))); + } + buf + } + Self::RestProperty { + ident, + excluded_keys: _, + } => { + format!(" ... {}", interner.resolve_expect(ident.sym())) + } + Self::AssignmentRestPropertyAccess { access, .. } => { + format!(" ... {}", access.to_interned_string(interner)) + } + Self::AssignmentPropertyAccess { + name, + access, + default_init, + } => { + let mut buf = match name { + PropertyName::Literal(name) => { + format!( + " {} : {}", + interner.resolve_expect(*name), + access.to_interned_string(interner) + ) + } + PropertyName::Computed(node) => { + format!( + " [{}] : {}", + node.to_interned_string(interner), + access.to_interned_string(interner) + ) + } + }; + if let Some(init) = &default_init { + buf.push_str(&format!(" = {}", init.to_interned_string(interner))); + } + buf + } + Self::Pattern { + name, + pattern, + default_init, + } => { + let mut buf = match name { + PropertyName::Literal(name) => { + format!( + " {} : {}", + interner.resolve_expect(*name), + pattern.to_interned_string(interner), + ) + } + PropertyName::Computed(node) => { + format!( + " [{}] : {}", + node.to_interned_string(interner), + pattern.to_interned_string(interner), + ) + } + }; + if let Some(ref init) = default_init { + buf.push_str(&format!(" = {}", init.to_interned_string(interner))); + } + buf + } + } + } +} + +impl VisitWith for ObjectPatternElement { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::SingleName { + name, + ident, + default_init, + } => { + try_break!(visitor.visit_property_name(name)); + try_break!(visitor.visit_identifier(ident)); + if let Some(expr) = default_init { + visitor.visit_expression(expr) + } else { + ControlFlow::Continue(()) + } + } + Self::RestProperty { ident, .. } => visitor.visit_identifier(ident), + Self::AssignmentPropertyAccess { + name, + access, + default_init, + } => { + try_break!(visitor.visit_property_name(name)); + try_break!(visitor.visit_property_access(access)); + if let Some(expr) = default_init { + visitor.visit_expression(expr) + } else { + ControlFlow::Continue(()) + } + } + Self::AssignmentRestPropertyAccess { access, .. } => { + visitor.visit_property_access(access) + } + Self::Pattern { + name, + pattern, + default_init, + } => { + try_break!(visitor.visit_property_name(name)); + try_break!(visitor.visit_pattern(pattern)); + if let Some(expr) = default_init { + visitor.visit_expression(expr) + } else { + ControlFlow::Continue(()) + } + } + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::SingleName { + name, + ident, + default_init, + } => { + try_break!(visitor.visit_property_name_mut(name)); + try_break!(visitor.visit_identifier_mut(ident)); + if let Some(expr) = default_init { + visitor.visit_expression_mut(expr) + } else { + ControlFlow::Continue(()) + } + } + Self::RestProperty { ident, .. } => visitor.visit_identifier_mut(ident), + Self::AssignmentPropertyAccess { + name, + access, + default_init, + } => { + try_break!(visitor.visit_property_name_mut(name)); + try_break!(visitor.visit_property_access_mut(access)); + if let Some(expr) = default_init { + visitor.visit_expression_mut(expr) + } else { + ControlFlow::Continue(()) + } + } + Self::AssignmentRestPropertyAccess { access, .. } => { + visitor.visit_property_access_mut(access) + } + Self::Pattern { + name, + pattern, + default_init, + } => { + try_break!(visitor.visit_property_name_mut(name)); + try_break!(visitor.visit_pattern_mut(pattern)); + if let Some(expr) = default_init { + visitor.visit_expression_mut(expr) + } else { + ControlFlow::Continue(()) + } + } + } + } +} + +/// The different types of bindings that an array binding pattern may contain. +/// +/// Corresponds to the [`BindingElement`][spec1] and the [`AssignmentElement`][spec2] nodes. +/// +/// [spec1]: https://tc39.es/ecma262/#prod-BindingElement +/// [spec2]: https://tc39.es/ecma262/#prod-AssignmentElement +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum ArrayPatternElement { + /// Elision represents the elision of an item in the array binding pattern. + /// + /// An `Elision` may occur at multiple points in the pattern and may be multiple elisions. + /// This variant strictly represents one elision. If there are multiple, this should be used multiple times. + /// + /// More information: + /// - [ECMAScript reference: 13.2.4 Array Initializer - Elision][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-Elision + Elision, + + /// SingleName represents a `SingleName` with an identifier and an optional default initializer. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - SingleNameBinding][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-SingleNameBinding + SingleName { + /// The variable name where the index element will be stored. + ident: Identifier, + /// An optional default value for the variable, in case the index element doesn't exist. + default_init: Option, + }, + + /// PropertyAccess represents a binding with a property accessor. + /// + /// Note: According to the spec this is not part of an ArrayBindingPattern. + /// This is only used when a array literal is used as the left-hand-side of an assignment expression. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression + PropertyAccess { + /// The property access where the index element will be stored. + access: PropertyAccess, + }, + + /// Pattern represents a `Pattern` in an `Element` of an array pattern. + /// + /// The pattern and the optional default initializer are both stored in the DeclarationPattern. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingElement][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-BindingElement + Pattern { + /// The pattern where the index element will be stored. + pattern: Pattern, + /// An optional default value for the pattern, in case the index element doesn't exist. + default_init: Option, + }, + + /// SingleNameRest represents a `BindingIdentifier` in a `BindingRestElement` of an array pattern. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestElement + SingleNameRest { + /// The variable where the unassigned index elements will be stored. + ident: Identifier, + }, + + /// PropertyAccess represents a rest (spread operator) with a property accessor. + /// + /// Note: According to the spec this is not part of an ArrayBindingPattern. + /// This is only used when a array literal is used as the left-hand-side of an assignment expression. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression + PropertyAccessRest { + /// The property access where the unassigned index elements will be stored. + access: PropertyAccess, + }, + + /// PatternRest represents a `Pattern` in a `RestElement` of an array pattern. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestElement + PatternRest { + /// The pattern where the unassigned index elements will be stored. + pattern: Pattern, + }, +} + +impl ToInternedString for ArrayPatternElement { + fn to_interned_string(&self, interner: &Interner) -> String { + match self { + Self::Elision => " ".to_owned(), + Self::SingleName { + ident, + default_init, + } => { + let mut buf = format!(" {}", interner.resolve_expect(ident.sym())); + if let Some(ref init) = default_init { + buf.push_str(&format!(" = {}", init.to_interned_string(interner))); + } + buf + } + Self::PropertyAccess { access } => { + format!(" {}", access.to_interned_string(interner)) + } + Self::Pattern { + pattern, + default_init, + } => { + let mut buf = format!(" {}", pattern.to_interned_string(interner)); + if let Some(init) = default_init { + buf.push_str(&format!(" = {}", init.to_interned_string(interner))); + } + buf + } + Self::SingleNameRest { ident } => { + format!(" ... {}", interner.resolve_expect(ident.sym())) + } + Self::PropertyAccessRest { access } => { + format!(" ... {}", access.to_interned_string(interner)) + } + Self::PatternRest { pattern } => { + format!(" ... {}", pattern.to_interned_string(interner)) + } + } + } +} + +impl VisitWith for ArrayPatternElement { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::SingleName { + ident, + default_init, + } => { + try_break!(visitor.visit_identifier(ident)); + if let Some(expr) = default_init { + visitor.visit_expression(expr) + } else { + ControlFlow::Continue(()) + } + } + Self::PropertyAccess { access } | Self::PropertyAccessRest { access } => { + visitor.visit_property_access(access) + } + Self::Pattern { + pattern, + default_init, + } => { + try_break!(visitor.visit_pattern(pattern)); + if let Some(expr) = default_init { + visitor.visit_expression(expr) + } else { + ControlFlow::Continue(()) + } + } + Self::SingleNameRest { ident } => visitor.visit_identifier(ident), + Self::PatternRest { pattern } => visitor.visit_pattern(pattern), + Self::Elision => { + // special case to be handled by user + ControlFlow::Continue(()) + } + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::SingleName { + ident, + default_init, + } => { + try_break!(visitor.visit_identifier_mut(ident)); + if let Some(expr) = default_init { + visitor.visit_expression_mut(expr) + } else { + ControlFlow::Continue(()) + } + } + Self::PropertyAccess { access } | Self::PropertyAccessRest { access } => { + visitor.visit_property_access_mut(access) + } + Self::Pattern { + pattern, + default_init, + } => { + try_break!(visitor.visit_pattern_mut(pattern)); + if let Some(expr) = default_init { + visitor.visit_expression_mut(expr) + } else { + ControlFlow::Continue(()) + } + } + Self::SingleNameRest { ident } => visitor.visit_identifier_mut(ident), + Self::PatternRest { pattern } => visitor.visit_pattern_mut(pattern), + Self::Elision => { + // special case to be handled by user + ControlFlow::Continue(()) + } + } + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/position.rs b/javascript-engine/external/boa/boa_ast/src/position.rs new file mode 100644 index 0000000..216693e --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/position.rs @@ -0,0 +1,302 @@ +use std::{cmp::Ordering, fmt, num::NonZeroU32}; + +/// A position in the ECMAScript source code. +/// +/// Stores both the column number and the line number. +/// +/// ## Similar Implementations +/// [V8: Location](https://cs.chromium.org/chromium/src/v8/src/parsing/scanner.h?type=cs&q=isValid+Location&g=0&l=216) +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Position { + /// Line number. + line_number: NonZeroU32, + /// Column number. + column_number: NonZeroU32, +} + +impl Position { + /// Creates a new `Position`. + #[inline] + #[track_caller] + #[must_use] + pub fn new(line_number: u32, column_number: u32) -> Self { + Self { + line_number: NonZeroU32::new(line_number).expect("line number cannot be 0"), + column_number: NonZeroU32::new(column_number).expect("column number cannot be 0"), + } + } + + /// Gets the line number of the position. + #[inline] + #[must_use] + pub const fn line_number(self) -> u32 { + self.line_number.get() + } + + /// Gets the column number of the position. + #[inline] + #[must_use] + pub const fn column_number(self) -> u32 { + self.column_number.get() + } +} + +impl fmt::Display for Position { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}:{}", self.line_number, self.column_number) + } +} + +/// A span in the ECMAScript source code. +/// +/// Stores a start position and an end position. +/// +/// Note that spans are of the form [start, end) i.e. that the start position is inclusive +/// and the end position is exclusive. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Span { + start: Position, + end: Position, +} + +impl Span { + /// Creates a new `Span`. + /// + /// # Panics + /// + /// Panics if the start position is bigger than the end position. + #[inline] + #[track_caller] + #[must_use] + pub fn new(start: Position, end: Position) -> Self { + assert!(start <= end, "a span cannot start after its end"); + + Self { start, end } + } + + /// Gets the starting position of the span. + #[inline] + #[must_use] + pub const fn start(self) -> Position { + self.start + } + + /// Gets the final position of the span. + #[inline] + #[must_use] + pub const fn end(self) -> Position { + self.end + } + + /// Checks if this span inclusively contains another span or position. + pub fn contains(self, other: S) -> bool + where + S: Into, + { + let other = other.into(); + self.start <= other.start && self.end >= other.end + } +} + +impl From for Span { + fn from(pos: Position) -> Self { + Self { + start: pos, + end: pos, + } + } +} + +impl PartialOrd for Span { + fn partial_cmp(&self, other: &Self) -> Option { + if self == other { + Some(Ordering::Equal) + } else if self.end < other.start { + Some(Ordering::Less) + } else if self.start > other.end { + Some(Ordering::Greater) + } else { + None + } + } +} + +impl fmt::Display for Span { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "[{}..{}]", self.start, self.end) + } +} + +#[cfg(test)] +mod tests { + #![allow(clippy::similar_names)] + #![allow(unused_must_use)] + use super::{Position, Span}; + + /// Checks that we cannot create a position with 0 as the column. + #[test] + #[should_panic] + fn invalid_position_column() { + Position::new(10, 0); + } + + /// Checks that we cannot create a position with 0 as the line. + #[test] + #[should_panic] + fn invalid_position_line() { + Position::new(0, 10); + } + + /// Checks that the `PartialEq` implementation of `Position` is consistent. + #[test] + fn position_equality() { + assert_eq!(Position::new(10, 50), Position::new(10, 50)); + assert_ne!(Position::new(10, 50), Position::new(10, 51)); + assert_ne!(Position::new(10, 50), Position::new(11, 50)); + assert_ne!(Position::new(10, 50), Position::new(11, 51)); + } + + /// Checks that the `PartialOrd` implementation of `Position` is consistent. + #[test] + fn position_order() { + assert!(Position::new(10, 50) < Position::new(10, 51)); + assert!(Position::new(9, 50) < Position::new(10, 50)); + assert!(Position::new(10, 50) < Position::new(11, 51)); + assert!(Position::new(10, 50) < Position::new(11, 49)); + + assert!(Position::new(10, 51) > Position::new(10, 50)); + assert!(Position::new(10, 50) > Position::new(9, 50)); + assert!(Position::new(11, 51) > Position::new(10, 50)); + assert!(Position::new(11, 49) > Position::new(10, 50)); + } + + /// Checks that the position getters actually retrieve correct values. + #[test] + fn position_getters() { + let pos = Position::new(10, 50); + assert_eq!(pos.line_number(), 10); + assert_eq!(pos.column_number(), 50); + } + + /// Checks that the string representation of a position is correct. + #[test] + fn position_to_string() { + let pos = Position::new(10, 50); + + assert_eq!("10:50", pos.to_string()); + assert_eq!("10:50", pos.to_string()); + } + + /// Checks that we cannot create an invalid span. + #[test] + #[should_panic] + fn invalid_span() { + let a = Position::new(10, 30); + let b = Position::new(10, 50); + Span::new(b, a); + } + + /// Checks that we can create valid spans. + #[test] + fn span_creation() { + let a = Position::new(10, 30); + let b = Position::new(10, 50); + + let _ = Span::new(a, b); + let _ = Span::new(a, a); + let _ = Span::from(a); + } + + /// Checks that the `PartialEq` implementation of `Span` is consistent. + #[test] + fn span_equality() { + let a = Position::new(10, 50); + let b = Position::new(10, 52); + let c = Position::new(11, 20); + + let span_ab = Span::new(a, b); + let span_ab_2 = Span::new(a, b); + let span_ac = Span::new(a, c); + let span_bc = Span::new(b, c); + + assert_eq!(span_ab, span_ab_2); + assert_ne!(span_ab, span_ac); + assert_ne!(span_ab, span_bc); + assert_ne!(span_bc, span_ac); + + let span_a = Span::from(a); + let span_aa = Span::new(a, a); + + assert_eq!(span_a, span_aa); + } + + /// Checks that the getters retrieve the correct value. + #[test] + fn span_getters() { + let a = Position::new(10, 50); + let b = Position::new(10, 52); + + let span = Span::new(a, b); + + assert_eq!(span.start(), a); + assert_eq!(span.end(), b); + } + + /// Checks that the `Span::contains()` method works properly. + #[test] + fn span_contains() { + let a = Position::new(10, 50); + let b = Position::new(10, 52); + let c = Position::new(11, 20); + let d = Position::new(12, 5); + + let span_ac = Span::new(a, c); + assert!(span_ac.contains(b)); + + let span_ab = Span::new(a, b); + let span_cd = Span::new(c, d); + + assert!(!span_ab.contains(span_cd)); + assert!(span_ab.contains(b)); + + let span_ad = Span::new(a, d); + let span_bc = Span::new(b, c); + + assert!(span_ad.contains(span_bc)); + assert!(!span_bc.contains(span_ad)); + + let span_ac = Span::new(a, c); + let span_bd = Span::new(b, d); + + assert!(!span_ac.contains(span_bd)); + assert!(!span_bd.contains(span_ac)); + } + + /// Checks that the string representation of a span is correct. + #[test] + fn span_to_string() { + let a = Position::new(10, 50); + let b = Position::new(11, 20); + let span = Span::new(a, b); + + assert_eq!("[10:50..11:20]", span.to_string()); + assert_eq!("[10:50..11:20]", span.to_string()); + } + + /// Checks that the ordering of spans is correct. + #[test] + fn span_ordering() { + let a = Position::new(10, 50); + let b = Position::new(10, 52); + let c = Position::new(11, 20); + let d = Position::new(12, 5); + + let span_ab = Span::new(a, b); + let span_cd = Span::new(c, d); + + assert!(span_ab < span_cd); + assert!(span_cd > span_ab); + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/property.rs b/javascript-engine/external/boa/boa_ast/src/property.rs new file mode 100644 index 0000000..c44b5c5 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/property.rs @@ -0,0 +1,371 @@ +//! Property definition related types, used in object literals and class definitions. + +use crate::function::PrivateName; +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use boa_interner::{Interner, Sym, ToInternedString}; +use core::ops::ControlFlow; + +use super::{ + expression::{literal::Literal, Identifier}, + function::{AsyncFunction, AsyncGenerator, Function, Generator}, + Expression, +}; + +/// Describes the definition of a property within an object literal. +/// +/// A property has a name (a string) and a value (primitive, method, or object reference). +/// Note that when we say that "a property holds an object", that is shorthand for "a property holds an object reference". +/// This distinction matters because the original referenced object remains unchanged when you change the property's value. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition +/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/property/JavaScript +// TODO: Support all features: https://tc39.es/ecma262/#prod-PropertyDefinition +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum PropertyDefinition { + /// Puts a variable into an object. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-IdentifierReference + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Property_definitions + IdentifierReference(Identifier), + + /// Binds a property name to a JavaScript value. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Property_definitions + Property(PropertyName, Expression), + + /// A property of an object can also refer to a function or a getter or setter method. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Method_definitions + MethodDefinition(PropertyName, MethodDefinition), + + /// The Rest/Spread Properties for ECMAScript proposal (stage 4) adds spread properties to object literals. + /// It copies own enumerable properties from a provided object onto a new object. + /// + /// Shallow-cloning (excluding `prototype`) or merging objects is now possible using a shorter syntax than `Object.assign()`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Spread_properties + SpreadObject(Expression), + + /// Cover grammar for when an object literal is used as an object binding pattern. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#prod-CoverInitializedName + CoverInitializedName(Identifier, Expression), +} + +impl VisitWith for PropertyDefinition { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::IdentifierReference(id) => visitor.visit_identifier(id), + Self::Property(pn, expr) => { + try_break!(visitor.visit_property_name(pn)); + visitor.visit_expression(expr) + } + Self::MethodDefinition(pn, md) => { + try_break!(visitor.visit_property_name(pn)); + visitor.visit_method_definition(md) + } + Self::SpreadObject(expr) => visitor.visit_expression(expr), + Self::CoverInitializedName(id, expr) => { + try_break!(visitor.visit_identifier(id)); + visitor.visit_expression(expr) + } + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::IdentifierReference(id) => visitor.visit_identifier_mut(id), + Self::Property(pn, expr) => { + try_break!(visitor.visit_property_name_mut(pn)); + visitor.visit_expression_mut(expr) + } + Self::MethodDefinition(pn, md) => { + try_break!(visitor.visit_property_name_mut(pn)); + visitor.visit_method_definition_mut(md) + } + Self::SpreadObject(expr) => visitor.visit_expression_mut(expr), + Self::CoverInitializedName(id, expr) => { + try_break!(visitor.visit_identifier_mut(id)); + visitor.visit_expression_mut(expr) + } + } + } +} + +/// Method definition. +/// +/// Starting with ECMAScript 2015, a shorter syntax for method definitions on objects initializers is introduced. +/// It is a shorthand for a function assigned to the method's name. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum MethodDefinition { + /// The `get` syntax binds an object property to a function that will be called when that property is looked up. + /// + /// Sometimes it is desirable to allow access to a property that returns a dynamically computed value, + /// or you may want to reflect the status of an internal variable without requiring the use of explicit method calls. + /// In JavaScript, this can be accomplished with the use of a getter. + /// + /// It is not possible to simultaneously have a getter bound to a property and have that property actually hold a value, + /// although it is possible to use a getter and a setter in conjunction to create a type of pseudo-property. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get + Get(Function), + + /// The `set` syntax binds an object property to a function to be called when there is an attempt to set that property. + /// + /// In JavaScript, a setter can be used to execute a function whenever a specified property is attempted to be changed. + /// Setters are most often used in conjunction with getters to create a type of pseudo-property. + /// It is not possible to simultaneously have a setter on a property that holds an actual value. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set + Set(Function), + + /// Starting with ECMAScript 2015, you are able to define own methods in a shorter syntax, similar to the getters and setters. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Method_definition_syntax + Ordinary(Function), + + /// Starting with ECMAScript 2015, you are able to define own methods in a shorter syntax, similar to the getters and setters. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions#generator_methods + Generator(Generator), + + /// Async generators can be used to define a method + /// + /// More information + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorMethod + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions#async_generator_methods + AsyncGenerator(AsyncGenerator), + + /// Async function can be used to define a method + /// + /// More information + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AsyncMethod + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions#async_methods + Async(AsyncFunction), +} + +impl VisitWith for MethodDefinition { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::Get(f) | Self::Set(f) | Self::Ordinary(f) => visitor.visit_function(f), + Self::Generator(g) => visitor.visit_generator(g), + Self::AsyncGenerator(ag) => visitor.visit_async_generator(ag), + Self::Async(af) => visitor.visit_async_function(af), + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::Get(f) | Self::Set(f) | Self::Ordinary(f) => visitor.visit_function_mut(f), + Self::Generator(g) => visitor.visit_generator_mut(g), + Self::AsyncGenerator(ag) => visitor.visit_async_generator_mut(ag), + Self::Async(af) => visitor.visit_async_function_mut(af), + } + } +} + +/// `PropertyName` can be either a literal or computed. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#prod-PropertyName +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum PropertyName { + /// A `Literal` property name can be either an identifier, a string or a numeric literal. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#prod-LiteralPropertyName + Literal(Sym), + + /// A `Computed` property name is an expression that gets evaluated and converted into a property name. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#prod-ComputedPropertyName + Computed(Expression), +} + +impl PropertyName { + /// Returns the literal property name if it exists. + #[must_use] + pub const fn literal(&self) -> Option { + if let Self::Literal(sym) = self { + Some(*sym) + } else { + None + } + } + + /// Returns the expression if the property name is computed. + #[must_use] + pub const fn computed(&self) -> Option<&Expression> { + if let Self::Computed(expr) = self { + Some(expr) + } else { + None + } + } + + /// Returns either the literal property name or the computed const string property name. + #[must_use] + pub const fn prop_name(&self) -> Option { + match self { + Self::Literal(sym) | Self::Computed(Expression::Literal(Literal::String(sym))) => { + Some(*sym) + } + Self::Computed(_) => None, + } + } +} + +impl ToInternedString for PropertyName { + fn to_interned_string(&self, interner: &Interner) -> String { + match self { + Self::Literal(key) => interner.resolve_expect(*key).to_string(), + Self::Computed(key) => key.to_interned_string(interner), + } + } +} + +impl From for PropertyName { + fn from(name: Sym) -> Self { + Self::Literal(name) + } +} + +impl From for PropertyName { + fn from(name: Expression) -> Self { + Self::Computed(name) + } +} + +impl VisitWith for PropertyName { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::Literal(sym) => visitor.visit_sym(sym), + Self::Computed(expr) => visitor.visit_expression(expr), + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::Literal(sym) => visitor.visit_sym_mut(sym), + Self::Computed(expr) => visitor.visit_expression_mut(expr), + } + } +} + +/// `ClassElementName` can be either a property name or a private identifier. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#prod-ClassElementName +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub enum ClassElementName { + /// A public property. + PropertyName(PropertyName), + /// A private property. + PrivateIdentifier(PrivateName), +} + +impl ClassElementName { + /// Returns the property name if it exists. + #[must_use] + pub const fn literal(&self) -> Option { + if let Self::PropertyName(name) = self { + name.literal() + } else { + None + } + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/punctuator.rs b/javascript-engine/external/boa/boa_ast/src/punctuator.rs new file mode 100644 index 0000000..9bda165 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/punctuator.rs @@ -0,0 +1,285 @@ +//! The `Punctuator` enum, which contains all punctuators used in ECMAScript. +//! +//! More information: +//! - [ECMAScript Reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#prod-Punctuator + +use crate::expression::operator::{ + assign::AssignOp, + binary::{ArithmeticOp, BinaryOp, BitwiseOp, LogicalOp, RelationalOp}, +}; +use std::{ + convert::TryInto, + fmt::{Display, Error, Formatter}, +}; + +/// All of the punctuators used in ECMAScript. +/// +/// More information: +/// - [ECMAScript Reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#prod-Punctuator +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub enum Punctuator { + /// `+` + Add, + /// `&` + And, + /// `=>` + Arrow, + /// `=` + Assign, + /// `+=` + AssignAdd, + /// `&=` + AssignAnd, + /// `&&=` + AssignBoolAnd, + /// `||=` + AssignBoolOr, + /// `??=`, + AssignCoalesce, + /// `/=` + AssignDiv, + /// `<<=` + AssignLeftSh, + /// `%=` + AssignMod, + /// `*=` + AssignMul, + /// `|=` + AssignOr, + /// `**=` + AssignPow, + /// `>>=` + AssignRightSh, + /// `-=` + AssignSub, + /// `>>>=` + AssignURightSh, + /// `^=` + AssignXor, + /// `&&` + BoolAnd, + /// `||` + BoolOr, + /// `}` + CloseBlock, + /// `]` + CloseBracket, + /// `)` + CloseParen, + /// `??` + Coalesce, + /// `:` + Colon, + /// `,` + Comma, + /// `--` + Dec, + /// `/` + Div, + /// `.` + Dot, + /// `==` + Eq, + /// `>` + GreaterThan, + /// `>=` + GreaterThanOrEq, + /// `++` + Inc, + /// `<<` + LeftSh, + /// `<` + LessThan, + /// `<=` + LessThanOrEq, + /// `%` + Mod, + /// `*` + Mul, + /// `~` + Neg, + /// `!` + Not, + /// `!=` + NotEq, + /// `{` + OpenBlock, + /// `[` + OpenBracket, + /// `(` + OpenParen, + /// `?.` + Optional, + /// `|` + Or, + /// `**` + Exp, + /// `?` + Question, + /// `>>` + RightSh, + /// `;` + Semicolon, + /// `...` + Spread, + /// `===` + StrictEq, + /// `!==` + StrictNotEq, + /// `-` + Sub, + /// `>>>` + URightSh, + /// `^` + Xor, +} + +impl Punctuator { + /// Attempts to convert a punctuator (`+`, `=`...) to an Assign Operator + /// + /// If there is no match, `None` will be returned. + #[must_use] + pub const fn as_assign_op(self) -> Option { + match self { + Self::Assign => Some(AssignOp::Assign), + Self::AssignAdd => Some(AssignOp::Add), + Self::AssignAnd => Some(AssignOp::And), + Self::AssignBoolAnd => Some(AssignOp::BoolAnd), + Self::AssignBoolOr => Some(AssignOp::BoolOr), + Self::AssignCoalesce => Some(AssignOp::Coalesce), + Self::AssignDiv => Some(AssignOp::Div), + Self::AssignLeftSh => Some(AssignOp::Shl), + Self::AssignMod => Some(AssignOp::Mod), + Self::AssignMul => Some(AssignOp::Mul), + Self::AssignOr => Some(AssignOp::Or), + Self::AssignPow => Some(AssignOp::Exp), + Self::AssignRightSh => Some(AssignOp::Shr), + Self::AssignSub => Some(AssignOp::Sub), + Self::AssignURightSh => Some(AssignOp::Ushr), + Self::AssignXor => Some(AssignOp::Xor), + _ => None, + } + } + + /// Attempts to convert a punctuator (`+`, `=`...) to a Binary Operator + /// + /// If there is no match, `None` will be returned. + #[must_use] + pub const fn as_binary_op(self) -> Option { + match self { + Self::Add => Some(BinaryOp::Arithmetic(ArithmeticOp::Add)), + Self::Sub => Some(BinaryOp::Arithmetic(ArithmeticOp::Sub)), + Self::Mul => Some(BinaryOp::Arithmetic(ArithmeticOp::Mul)), + Self::Div => Some(BinaryOp::Arithmetic(ArithmeticOp::Div)), + Self::Mod => Some(BinaryOp::Arithmetic(ArithmeticOp::Mod)), + Self::And => Some(BinaryOp::Bitwise(BitwiseOp::And)), + Self::Or => Some(BinaryOp::Bitwise(BitwiseOp::Or)), + Self::Xor => Some(BinaryOp::Bitwise(BitwiseOp::Xor)), + Self::BoolAnd => Some(BinaryOp::Logical(LogicalOp::And)), + Self::BoolOr => Some(BinaryOp::Logical(LogicalOp::Or)), + Self::Coalesce => Some(BinaryOp::Logical(LogicalOp::Coalesce)), + Self::Eq => Some(BinaryOp::Relational(RelationalOp::Equal)), + Self::NotEq => Some(BinaryOp::Relational(RelationalOp::NotEqual)), + Self::StrictEq => Some(BinaryOp::Relational(RelationalOp::StrictEqual)), + Self::StrictNotEq => Some(BinaryOp::Relational(RelationalOp::StrictNotEqual)), + Self::LessThan => Some(BinaryOp::Relational(RelationalOp::LessThan)), + Self::GreaterThan => Some(BinaryOp::Relational(RelationalOp::GreaterThan)), + Self::GreaterThanOrEq => Some(BinaryOp::Relational(RelationalOp::GreaterThanOrEqual)), + Self::LessThanOrEq => Some(BinaryOp::Relational(RelationalOp::LessThanOrEqual)), + Self::LeftSh => Some(BinaryOp::Bitwise(BitwiseOp::Shl)), + Self::RightSh => Some(BinaryOp::Bitwise(BitwiseOp::Shr)), + Self::URightSh => Some(BinaryOp::Bitwise(BitwiseOp::UShr)), + Self::Comma => Some(BinaryOp::Comma), + _ => None, + } + } + + /// Retrieves the punctuator as a static string. + #[must_use] + pub const fn as_str(self) -> &'static str { + match self { + Self::Add => "+", + Self::And => "&", + Self::Arrow => "=>", + Self::Assign => "=", + Self::AssignAdd => "+=", + Self::AssignAnd => "&=", + Self::AssignBoolAnd => "&&=", + Self::AssignBoolOr => "||=", + Self::AssignCoalesce => "??=", + Self::AssignDiv => "/=", + Self::AssignLeftSh => "<<=", + Self::AssignMod => "%=", + Self::AssignMul => "*=", + Self::AssignOr => "|=", + Self::AssignPow => "**=", + Self::AssignRightSh => ">>=", + Self::AssignSub => "-=", + Self::AssignURightSh => ">>>=", + Self::AssignXor => "^=", + Self::BoolAnd => "&&", + Self::BoolOr => "||", + Self::Coalesce => "??", + Self::CloseBlock => "}", + Self::CloseBracket => "]", + Self::CloseParen => ")", + Self::Colon => ":", + Self::Comma => ",", + Self::Dec => "--", + Self::Div => "/", + Self::Dot => ".", + Self::Eq => "==", + Self::GreaterThan => ">", + Self::GreaterThanOrEq => ">=", + Self::Inc => "++", + Self::LeftSh => "<<", + Self::LessThan => "<", + Self::LessThanOrEq => "<=", + Self::Mod => "%", + Self::Mul => "*", + Self::Neg => "~", + Self::Not => "!", + Self::NotEq => "!=", + Self::OpenBlock => "{", + Self::OpenBracket => "[", + Self::OpenParen => "(", + Self::Optional => "?.", + Self::Or => "|", + Self::Exp => "**", + Self::Question => "?", + Self::RightSh => ">>", + Self::Semicolon => ";", + Self::Spread => "...", + Self::StrictEq => "===", + Self::StrictNotEq => "!==", + Self::Sub => "-", + Self::URightSh => ">>>", + Self::Xor => "^", + } + } +} + +impl TryInto for Punctuator { + type Error = String; + fn try_into(self) -> Result { + self.as_binary_op() + .ok_or_else(|| format!("No binary operation for {self}")) + } +} + +impl Display for Punctuator { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + write!(f, "{}", self.as_str()) + } +} + +impl From for Box { + fn from(p: Punctuator) -> Self { + p.as_str().into() + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/statement/block.rs b/javascript-engine/external/boa/boa_ast/src/statement/block.rs new file mode 100644 index 0000000..5f926d6 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/statement/block.rs @@ -0,0 +1,85 @@ +//! Block AST node. + +use crate::{ + visitor::{VisitWith, Visitor, VisitorMut}, + Statement, StatementList, +}; +use boa_interner::{Interner, ToIndentedString}; +use core::ops::ControlFlow; + +/// A `block` statement (or compound statement in other languages) is used to group zero or +/// more statements. +/// +/// The block statement is often called compound statement in other languages. +/// It allows you to use multiple statements where ECMAScript expects only one statement. +/// Combining statements into blocks is a common practice in ECMAScript. The opposite behavior +/// is possible using an empty statement, where you provide no statement, although one is +/// required. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-BlockStatement +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq, Default)] +pub struct Block { + #[cfg_attr(feature = "serde", serde(flatten))] + statements: StatementList, +} + +impl Block { + /// Gets the list of statements and declarations in this block. + #[inline] + #[must_use] + pub const fn statement_list(&self) -> &StatementList { + &self.statements + } +} + +impl From for Block +where + T: Into, +{ + fn from(list: T) -> Self { + Self { + statements: list.into(), + } + } +} + +impl ToIndentedString for Block { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + format!( + "{{\n{}{}}}", + self.statements + .to_indented_string(interner, indentation + 1), + " ".repeat(indentation) + ) + } +} + +impl From for Statement { + #[inline] + fn from(block: Block) -> Self { + Self::Block(block) + } +} + +impl VisitWith for Block { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + visitor.visit_statement_list(&self.statements) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + visitor.visit_statement_list_mut(&mut self.statements) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/statement/if.rs b/javascript-engine/external/boa/boa_ast/src/statement/if.rs new file mode 100644 index 0000000..141bfb5 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/statement/if.rs @@ -0,0 +1,119 @@ +//! If statement + +use crate::{ + expression::Expression, + statement::Statement, + try_break, + visitor::{VisitWith, Visitor, VisitorMut}, +}; +use boa_interner::{Interner, ToIndentedString, ToInternedString}; +use core::ops::ControlFlow; + +/// The `if` statement executes a statement if a specified condition is [`truthy`][truthy]. If +/// the condition is [`falsy`][falsy], another statement can be executed. +/// +/// Multiple `if...else` statements can be nested to create an else if clause. +/// +/// Note that there is no elseif (in one word) keyword in JavaScript. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-IfStatement +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else +/// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/truthy +/// [falsy]: https://developer.mozilla.org/en-US/docs/Glossary/falsy +/// [expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct If { + condition: Expression, + body: Box, + else_node: Option>, +} + +impl If { + /// Gets the condition of the if statement. + #[inline] + #[must_use] + pub const fn cond(&self) -> &Expression { + &self.condition + } + + /// Gets the body to execute if the condition is true. + #[inline] + #[must_use] + pub const fn body(&self) -> &Statement { + &self.body + } + + /// Gets the `else` node, if it has one. + #[inline] + pub fn else_node(&self) -> Option<&Statement> { + self.else_node.as_ref().map(Box::as_ref) + } + + /// Creates an `If` AST node. + #[inline] + #[must_use] + pub fn new(condition: Expression, body: Statement, else_node: Option) -> Self { + Self { + condition, + body: body.into(), + else_node: else_node.map(Box::new), + } + } +} + +impl ToIndentedString for If { + fn to_indented_string(&self, interner: &Interner, indent: usize) -> String { + let mut buf = format!("if ({}) ", self.cond().to_interned_string(interner)); + match self.else_node() { + Some(else_e) => { + buf.push_str(&format!( + "{} else {}", + self.body().to_indented_string(interner, indent), + else_e.to_indented_string(interner, indent) + )); + } + None => { + buf.push_str(&self.body().to_indented_string(interner, indent)); + } + } + buf + } +} + +impl From for Statement { + fn from(if_stm: If) -> Self { + Self::If(if_stm) + } +} + +impl VisitWith for If { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_expression(&self.condition)); + try_break!(visitor.visit_statement(&self.body)); + if let Some(stmt) = &self.else_node { + try_break!(visitor.visit_statement(stmt)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_expression_mut(&mut self.condition)); + try_break!(visitor.visit_statement_mut(&mut self.body)); + if let Some(stmt) = &mut self.else_node { + try_break!(visitor.visit_statement_mut(stmt)); + } + ControlFlow::Continue(()) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/statement/iteration/break.rs b/javascript-engine/external/boa/boa_ast/src/statement/iteration/break.rs new file mode 100644 index 0000000..f1f5f82 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/statement/iteration/break.rs @@ -0,0 +1,79 @@ +use boa_interner::{Interner, Sym, ToInternedString}; +use core::ops::ControlFlow; + +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::Statement; + +/// The `break` statement terminates the current loop, switch, or label statement and transfers +/// program control to the statement following the terminated statement. +/// +/// The break statement includes an optional label that allows the program to break out of a +/// labeled statement. The break statement needs to be nested within the referenced label. The +/// labeled statement can be any block statement; it does not have to be preceded by a loop +/// statement. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-BreakStatement +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Break { + label: Option, +} + +impl Break { + /// Creates a `Break` AST node. + #[must_use] + pub const fn new(label: Option) -> Self { + Self { label } + } + + /// Gets the label of the break statement, if any. + #[must_use] + pub const fn label(&self) -> Option { + self.label + } +} + +impl ToInternedString for Break { + fn to_interned_string(&self, interner: &Interner) -> String { + self.label.map_or_else( + || "break".to_owned(), + |label| format!("break {}", interner.resolve_expect(label)), + ) + } +} + +impl From for Statement { + fn from(break_smt: Break) -> Self { + Self::Break(break_smt) + } +} + +impl VisitWith for Break { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + if let Some(sym) = &self.label { + visitor.visit_sym(sym) + } else { + ControlFlow::Continue(()) + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + if let Some(sym) = &mut self.label { + visitor.visit_sym_mut(sym) + } else { + ControlFlow::Continue(()) + } + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/statement/iteration/continue.rs b/javascript-engine/external/boa/boa_ast/src/statement/iteration/continue.rs new file mode 100644 index 0000000..ed5ddb2 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/statement/iteration/continue.rs @@ -0,0 +1,77 @@ +use crate::statement::Statement; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use boa_interner::{Interner, Sym, ToInternedString}; +use core::ops::ControlFlow; + +/// The `continue` statement terminates execution of the statements in the current iteration of +/// the current or labeled loop, and continues execution of the loop with the next iteration. +/// +/// The continue statement can include an optional label that allows the program to jump to the +/// next iteration of a labeled loop statement instead of the current loop. In this case, the +/// continue statement needs to be nested within this labeled statement. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-ContinueStatement +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Continue { + label: Option, +} + +impl Continue { + /// Creates a `Continue` AST node. + #[must_use] + pub const fn new(label: Option) -> Self { + Self { label } + } + + /// Gets the label of this `Continue` statement. + #[must_use] + pub const fn label(&self) -> Option { + self.label + } +} + +impl ToInternedString for Continue { + fn to_interned_string(&self, interner: &Interner) -> String { + self.label.map_or_else( + || "continue".to_owned(), + |label| format!("continue {}", interner.resolve_expect(label)), + ) + } +} + +impl From for Statement { + fn from(cont: Continue) -> Self { + Self::Continue(cont) + } +} + +impl VisitWith for Continue { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + if let Some(sym) = &self.label { + visitor.visit_sym(sym) + } else { + ControlFlow::Continue(()) + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + if let Some(sym) = &mut self.label { + visitor.visit_sym_mut(sym) + } else { + ControlFlow::Continue(()) + } + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/statement/iteration/do_while_loop.rs b/javascript-engine/external/boa/boa_ast/src/statement/iteration/do_while_loop.rs new file mode 100644 index 0000000..f3e0b30 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/statement/iteration/do_while_loop.rs @@ -0,0 +1,87 @@ +use crate::{ + expression::Expression, + statement::Statement, + try_break, + visitor::{VisitWith, Visitor, VisitorMut}, +}; +use boa_interner::{Interner, ToIndentedString, ToInternedString}; +use core::ops::ControlFlow; + +/// The `do...while` statement creates a loop that executes a specified statement until the +/// test condition evaluates to false. +/// +/// The condition is evaluated after executing the statement, resulting in the specified +/// statement executing at least once. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#sec-do-while-statement +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct DoWhileLoop { + body: Box, + condition: Expression, +} + +impl DoWhileLoop { + /// Gets the body of the do-while loop. + #[inline] + #[must_use] + pub const fn body(&self) -> &Statement { + &self.body + } + + /// Gets the condition of the do-while loop. + #[inline] + #[must_use] + pub const fn cond(&self) -> &Expression { + &self.condition + } + /// Creates a `DoWhileLoop` AST node. + #[inline] + #[must_use] + pub fn new(body: Statement, condition: Expression) -> Self { + Self { + body: body.into(), + condition, + } + } +} + +impl ToIndentedString for DoWhileLoop { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + format!( + "do {} while ({})", + self.body().to_indented_string(interner, indentation), + self.cond().to_interned_string(interner) + ) + } +} + +impl From for Statement { + fn from(do_while: DoWhileLoop) -> Self { + Self::DoWhileLoop(do_while) + } +} + +impl VisitWith for DoWhileLoop { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_statement(&self.body)); + visitor.visit_expression(&self.condition) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_statement_mut(&mut self.body)); + visitor.visit_expression_mut(&mut self.condition) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/statement/iteration/for_in_loop.rs b/javascript-engine/external/boa/boa_ast/src/statement/iteration/for_in_loop.rs new file mode 100644 index 0000000..9c2774a --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/statement/iteration/for_in_loop.rs @@ -0,0 +1,98 @@ +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ + expression::Expression, + statement::{iteration::IterableLoopInitializer, Statement}, +}; +use boa_interner::{Interner, ToIndentedString, ToInternedString}; +use core::ops::ControlFlow; + +/// A `for...in` loop statement, as defined by the [spec]. +/// +/// [`for...in`][forin] statements loop over all enumerable string properties of an object, including +/// inherited properties. +/// +/// [forin]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in +/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct ForInLoop { + initializer: IterableLoopInitializer, + target: Expression, + body: Box, +} + +impl ForInLoop { + /// Creates a new `ForInLoop`. + #[inline] + #[must_use] + pub fn new(initializer: IterableLoopInitializer, target: Expression, body: Statement) -> Self { + Self { + initializer, + target, + body: body.into(), + } + } + + /// Gets the initializer of the for...in loop. + #[inline] + #[must_use] + pub const fn initializer(&self) -> &IterableLoopInitializer { + &self.initializer + } + + /// Gets the target object of the for...in loop. + #[inline] + #[must_use] + pub const fn target(&self) -> &Expression { + &self.target + } + + /// Gets the body of the for...in loop. + #[inline] + #[must_use] + pub const fn body(&self) -> &Statement { + &self.body + } +} + +impl ToIndentedString for ForInLoop { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let mut buf = format!( + "for ({} in {}) ", + self.initializer.to_interned_string(interner), + self.target.to_interned_string(interner) + ); + buf.push_str(&self.body().to_indented_string(interner, indentation)); + + buf + } +} + +impl From for Statement { + #[inline] + fn from(for_in: ForInLoop) -> Self { + Self::ForInLoop(for_in) + } +} + +impl VisitWith for ForInLoop { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_iterable_loop_initializer(&self.initializer)); + try_break!(visitor.visit_expression(&self.target)); + visitor.visit_statement(&self.body) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_iterable_loop_initializer_mut(&mut self.initializer)); + try_break!(visitor.visit_expression_mut(&mut self.target)); + visitor.visit_statement_mut(&mut self.body) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/statement/iteration/for_loop.rs b/javascript-engine/external/boa/boa_ast/src/statement/iteration/for_loop.rs new file mode 100644 index 0000000..d0c79dc --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/statement/iteration/for_loop.rs @@ -0,0 +1,263 @@ +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ + declaration::{LexicalDeclaration, VarDeclaration}, + statement::Statement, + Expression, +}; +use boa_interner::{Interner, ToIndentedString, ToInternedString}; +use core::ops::ControlFlow; + +/// The `for` statement creates a loop that consists of three optional expressions. +/// +/// A [`for`][mdn] loop repeats until a specified condition evaluates to `false`. +/// The JavaScript for loop is similar to the Java and C for loop. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#prod-ForDeclaration +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct ForLoop { + #[cfg_attr(feature = "serde", serde(flatten))] + inner: Box, +} + +impl ForLoop { + /// Creates a new for loop AST node. + #[inline] + #[must_use] + pub fn new( + init: Option, + condition: Option, + final_expr: Option, + body: Statement, + ) -> Self { + Self { + inner: Box::new(InnerForLoop::new(init, condition, final_expr, body)), + } + } + + /// Gets the initialization node. + #[inline] + #[must_use] + pub const fn init(&self) -> Option<&ForLoopInitializer> { + self.inner.init() + } + + /// Gets the loop condition node. + #[inline] + #[must_use] + pub const fn condition(&self) -> Option<&Expression> { + self.inner.condition() + } + + /// Gets the final expression node. + #[inline] + #[must_use] + pub const fn final_expr(&self) -> Option<&Expression> { + self.inner.final_expr() + } + + /// Gets the body of the for loop. + #[inline] + #[must_use] + pub const fn body(&self) -> &Statement { + self.inner.body() + } +} + +impl ToIndentedString for ForLoop { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let mut buf = String::from("for ("); + if let Some(init) = self.init() { + buf.push_str(&init.to_interned_string(interner)); + } + buf.push_str("; "); + if let Some(condition) = self.condition() { + buf.push_str(&condition.to_interned_string(interner)); + } + buf.push_str("; "); + if let Some(final_expr) = self.final_expr() { + buf.push_str(&final_expr.to_interned_string(interner)); + } + buf.push_str(&format!( + ") {}", + self.inner.body().to_indented_string(interner, indentation) + )); + + buf + } +} + +impl From for Statement { + #[inline] + fn from(for_loop: ForLoop) -> Self { + Self::ForLoop(for_loop) + } +} + +impl VisitWith for ForLoop { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + if let Some(fli) = &self.inner.init { + try_break!(visitor.visit_for_loop_initializer(fli)); + } + if let Some(expr) = &self.inner.condition { + try_break!(visitor.visit_expression(expr)); + } + if let Some(expr) = &self.inner.final_expr { + try_break!(visitor.visit_expression(expr)); + } + visitor.visit_statement(&self.inner.body) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + if let Some(fli) = &mut self.inner.init { + try_break!(visitor.visit_for_loop_initializer_mut(fli)); + } + if let Some(expr) = &mut self.inner.condition { + try_break!(visitor.visit_expression_mut(expr)); + } + if let Some(expr) = &mut self.inner.final_expr { + try_break!(visitor.visit_expression_mut(expr)); + } + visitor.visit_statement_mut(&mut self.inner.body) + } +} + +/// Inner structure to avoid multiple indirections in the heap. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +struct InnerForLoop { + init: Option, + condition: Option, + final_expr: Option, + body: Statement, +} + +impl InnerForLoop { + /// Creates a new inner for loop. + #[inline] + const fn new( + init: Option, + condition: Option, + final_expr: Option, + body: Statement, + ) -> Self { + Self { + init, + condition, + final_expr, + body, + } + } + + /// Gets the initialization node. + #[inline] + const fn init(&self) -> Option<&ForLoopInitializer> { + self.init.as_ref() + } + + /// Gets the loop condition node. + #[inline] + const fn condition(&self) -> Option<&Expression> { + self.condition.as_ref() + } + + /// Gets the final expression node. + #[inline] + const fn final_expr(&self) -> Option<&Expression> { + self.final_expr.as_ref() + } + + /// Gets the body of the for loop. + #[inline] + const fn body(&self) -> &Statement { + &self.body + } +} + +/// A [`ForLoop`] initializer, as defined by the [spec]. +/// +/// A `ForLoop` initializer differs a lot from an +/// [`IterableLoopInitializer`][super::IterableLoopInitializer], since it can contain any arbitrary +/// expression instead of only accessors and patterns. Additionally, it can also contain many variable +/// declarations instead of only one. +/// +/// [spec]: https://tc39.es/ecma262/#prod-ForStatement +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum ForLoopInitializer { + /// An expression initializer. + Expression(Expression), + /// A var declaration initializer. + Var(VarDeclaration), + /// A lexical declaration initializer. + Lexical(LexicalDeclaration), +} + +impl ToInternedString for ForLoopInitializer { + fn to_interned_string(&self, interner: &Interner) -> String { + match self { + Self::Var(var) => var.to_interned_string(interner), + Self::Lexical(lex) => lex.to_interned_string(interner), + Self::Expression(expr) => expr.to_interned_string(interner), + } + } +} + +impl From for ForLoopInitializer { + #[inline] + fn from(expr: Expression) -> Self { + Self::Expression(expr) + } +} + +impl From for ForLoopInitializer { + #[inline] + fn from(list: LexicalDeclaration) -> Self { + Self::Lexical(list) + } +} + +impl From for ForLoopInitializer { + #[inline] + fn from(list: VarDeclaration) -> Self { + Self::Var(list) + } +} + +impl VisitWith for ForLoopInitializer { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::Expression(expr) => visitor.visit_expression(expr), + Self::Var(vd) => visitor.visit_var_declaration(vd), + Self::Lexical(ld) => visitor.visit_lexical_declaration(ld), + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::Expression(expr) => visitor.visit_expression_mut(expr), + Self::Var(vd) => visitor.visit_var_declaration_mut(vd), + Self::Lexical(ld) => visitor.visit_lexical_declaration_mut(ld), + } + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/statement/iteration/for_of_loop.rs b/javascript-engine/external/boa/boa_ast/src/statement/iteration/for_of_loop.rs new file mode 100644 index 0000000..d5b02aa --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/statement/iteration/for_of_loop.rs @@ -0,0 +1,115 @@ +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ + expression::Expression, + statement::{iteration::IterableLoopInitializer, Statement}, +}; +use boa_interner::{Interner, ToIndentedString, ToInternedString}; +use core::ops::ControlFlow; + +/// A `for...of` loop statement, as defined by the [spec]. +/// +/// [`for..of`][forof] statements loop over a sequence of values obtained from an iterable object (Array, +/// String, Map, generators). +/// +/// This type combines `for..of` and [`for await...of`][forawait] statements in a single structure, +/// since `for await...of` is essentially the same statement but with async iterable objects +/// as the source of iteration. +/// +/// [forof]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of +/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement +/// [forawait]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct ForOfLoop { + init: IterableLoopInitializer, + iterable: Expression, + body: Box, + r#await: bool, +} + +impl ForOfLoop { + /// Creates a new "for of" loop AST node. + #[inline] + #[must_use] + pub fn new( + init: IterableLoopInitializer, + iterable: Expression, + body: Statement, + r#await: bool, + ) -> Self { + Self { + init, + iterable, + body: body.into(), + r#await, + } + } + + /// Gets the initializer of the for...of loop. + #[inline] + #[must_use] + pub const fn initializer(&self) -> &IterableLoopInitializer { + &self.init + } + + /// Gets the iterable expression of the for...of loop. + #[inline] + #[must_use] + pub const fn iterable(&self) -> &Expression { + &self.iterable + } + + /// Gets the body to execute in the for...of loop. + #[inline] + #[must_use] + pub const fn body(&self) -> &Statement { + &self.body + } + + /// Returns true if this "for...of" loop is an "for await...of" loop. + #[inline] + #[must_use] + pub const fn r#await(&self) -> bool { + self.r#await + } +} + +impl ToIndentedString for ForOfLoop { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + format!( + "for ({} of {}) {}", + self.init.to_interned_string(interner), + self.iterable.to_interned_string(interner), + self.body().to_indented_string(interner, indentation) + ) + } +} + +impl From for Statement { + #[inline] + fn from(for_of: ForOfLoop) -> Self { + Self::ForOfLoop(for_of) + } +} + +impl VisitWith for ForOfLoop { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_iterable_loop_initializer(&self.init)); + try_break!(visitor.visit_expression(&self.iterable)); + visitor.visit_statement(&self.body) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_iterable_loop_initializer_mut(&mut self.init)); + try_break!(visitor.visit_expression_mut(&mut self.iterable)); + visitor.visit_statement_mut(&mut self.body) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/statement/iteration/mod.rs b/javascript-engine/external/boa/boa_ast/src/statement/iteration/mod.rs new file mode 100644 index 0000000..de7ebc7 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/statement/iteration/mod.rs @@ -0,0 +1,94 @@ +//! Iteration nodes + +mod r#break; +mod r#continue; +mod do_while_loop; +mod for_in_loop; +mod for_loop; +mod for_of_loop; +mod while_loop; + +use crate::{ + declaration::Binding, + expression::{access::PropertyAccess, Identifier}, + pattern::Pattern, +}; +use core::ops::ControlFlow; + +pub use self::{ + do_while_loop::DoWhileLoop, + for_in_loop::ForInLoop, + for_loop::{ForLoop, ForLoopInitializer}, + for_of_loop::ForOfLoop, + r#break::Break, + r#continue::Continue, + while_loop::WhileLoop, +}; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use boa_interner::{Interner, ToInternedString}; + +/// A `for-in`, `for-of` and `for-await-of` loop initializer. +/// +/// The [spec] specifies only single bindings for the listed types of loops, which makes us +/// unable to use plain `LexicalDeclaration`s or `VarStatement`s as initializers, since those +/// can have more than one binding. +/// +/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum IterableLoopInitializer { + /// An already declared variable. + Identifier(Identifier), + /// A property access. + Access(PropertyAccess), + /// A new var declaration. + Var(Binding), + /// A new let declaration. + Let(Binding), + /// A new const declaration. + Const(Binding), + /// A pattern with already declared variables. + Pattern(Pattern), +} + +impl ToInternedString for IterableLoopInitializer { + fn to_interned_string(&self, interner: &Interner) -> String { + let (binding, pre) = match self { + Self::Identifier(ident) => return ident.to_interned_string(interner), + Self::Pattern(pattern) => return pattern.to_interned_string(interner), + Self::Access(access) => return access.to_interned_string(interner), + Self::Var(binding) => (binding, "var"), + Self::Let(binding) => (binding, "let"), + Self::Const(binding) => (binding, "const"), + }; + + format!("{pre} {}", binding.to_interned_string(interner)) + } +} + +impl VisitWith for IterableLoopInitializer { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::Identifier(id) => visitor.visit_identifier(id), + Self::Access(pa) => visitor.visit_property_access(pa), + Self::Var(b) | Self::Let(b) | Self::Const(b) => visitor.visit_binding(b), + Self::Pattern(p) => visitor.visit_pattern(p), + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::Identifier(id) => visitor.visit_identifier_mut(id), + Self::Access(pa) => visitor.visit_property_access_mut(pa), + Self::Var(b) | Self::Let(b) | Self::Const(b) => visitor.visit_binding_mut(b), + Self::Pattern(p) => visitor.visit_pattern_mut(p), + } + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/statement/iteration/while_loop.rs b/javascript-engine/external/boa/boa_ast/src/statement/iteration/while_loop.rs new file mode 100644 index 0000000..00494d6 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/statement/iteration/while_loop.rs @@ -0,0 +1,88 @@ +use crate::{ + expression::Expression, + statement::Statement, + try_break, + visitor::{VisitWith, Visitor, VisitorMut}, +}; +use boa_interner::{Interner, ToIndentedString, ToInternedString}; +use core::ops::ControlFlow; + +/// The `while` statement creates a loop that executes a specified statement as long as the +/// test condition evaluates to `true`. +/// +/// The condition is evaluated before executing the statement. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-grammar-notation-WhileStatement +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct WhileLoop { + condition: Expression, + body: Box, +} + +impl WhileLoop { + /// Creates a `WhileLoop` AST node. + #[inline] + #[must_use] + pub fn new(condition: Expression, body: Statement) -> Self { + Self { + condition, + body: body.into(), + } + } + + /// Gets the condition of the while loop. + #[inline] + #[must_use] + pub const fn condition(&self) -> &Expression { + &self.condition + } + + /// Gets the body of the while loop. + #[inline] + #[must_use] + pub const fn body(&self) -> &Statement { + &self.body + } +} + +impl ToIndentedString for WhileLoop { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + format!( + "while ({}) {}", + self.condition().to_interned_string(interner), + self.body().to_indented_string(interner, indentation) + ) + } +} + +impl From for Statement { + #[inline] + fn from(while_loop: WhileLoop) -> Self { + Self::WhileLoop(while_loop) + } +} + +impl VisitWith for WhileLoop { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_expression(&self.condition)); + visitor.visit_statement(&self.body) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_expression_mut(&mut self.condition)); + visitor.visit_statement_mut(&mut self.body) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/statement/labelled.rs b/javascript-engine/external/boa/boa_ast/src/statement/labelled.rs new file mode 100644 index 0000000..9f9c46c --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/statement/labelled.rs @@ -0,0 +1,154 @@ +use crate::{ + function::Function, + try_break, + visitor::{VisitWith, Visitor, VisitorMut}, + Statement, +}; +use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString}; +use core::ops::ControlFlow; + +/// The set of Parse Nodes that can be preceded by a label, as defined by the [spec]. +/// +/// Semantically, a [`Labelled`] statement should only wrap [`Statement`] nodes. However, +/// old ECMAScript implementations supported [labelled function declarations][label-fn] as an extension +/// of the grammar. In the ECMAScript 2015 spec, the production of `LabelledStatement` was +/// modified to include labelled [`Function`]s as a valid node. +/// +/// [spec]: https://tc39.es/ecma262/#prod-LabelledItem +/// [label-fn]: https://tc39.es/ecma262/#sec-labelled-function-declarations +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum LabelledItem { + /// A labelled [`Function`]. + Function(Function), + /// A labelled [`Statement`]. + Statement(Statement), +} + +impl LabelledItem { + pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + match self { + Self::Function(f) => f.to_indented_string(interner, indentation), + Self::Statement(stmt) => stmt.to_indented_string(interner, indentation), + } + } +} + +impl ToInternedString for LabelledItem { + fn to_interned_string(&self, interner: &Interner) -> String { + self.to_indented_string(interner, 0) + } +} + +impl From for LabelledItem { + fn from(f: Function) -> Self { + Self::Function(f) + } +} + +impl From for LabelledItem { + fn from(stmt: Statement) -> Self { + Self::Statement(stmt) + } +} + +impl VisitWith for LabelledItem { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::Function(f) => visitor.visit_function(f), + Self::Statement(s) => visitor.visit_statement(s), + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::Function(f) => visitor.visit_function_mut(f), + Self::Statement(s) => visitor.visit_statement_mut(s), + } + } +} + +/// Labelled statement nodes, as defined by the [spec]. +/// +/// The method [`Labelled::item`] doesn't return a [`Statement`] for compatibility reasons. +/// See [`LabelledItem`] for more information. +/// +/// [spec]: https://tc39.es/ecma262/#sec-labelled-statements +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Labelled { + item: Box, + label: Sym, +} + +impl Labelled { + /// Creates a new `Labelled` statement. + #[inline] + #[must_use] + pub fn new(item: LabelledItem, label: Sym) -> Self { + Self { + item: Box::new(item), + label, + } + } + + /// Gets the labelled item. + #[inline] + #[must_use] + pub const fn item(&self) -> &LabelledItem { + &self.item + } + + /// Gets the label name. + #[inline] + #[must_use] + pub const fn label(&self) -> Sym { + self.label + } + + pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + format!( + "{}: {}", + interner.resolve_expect(self.label), + self.item.to_indented_string(interner, indentation) + ) + } +} + +impl ToInternedString for Labelled { + fn to_interned_string(&self, interner: &Interner) -> String { + self.to_indented_string(interner, 0) + } +} + +impl From for Statement { + fn from(labelled: Labelled) -> Self { + Self::Labelled(labelled) + } +} + +impl VisitWith for Labelled { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_labelled_item(&self.item)); + visitor.visit_sym(&self.label) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_labelled_item_mut(&mut self.item)); + visitor.visit_sym_mut(&mut self.label) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/statement/mod.rs b/javascript-engine/external/boa/boa_ast/src/statement/mod.rs new file mode 100644 index 0000000..14fa656 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/statement/mod.rs @@ -0,0 +1,241 @@ +//! The [`Statement`] Parse Node, as defined by the [spec]. +//! +//! ECMAScript [statements] are mainly composed of control flow operations, such as [`If`], +//! [`WhileLoop`], and [`Break`]. However, it also contains statements such as [`VarDeclaration`], +//! [`Block`] or [`Expression`] which are not strictly used for control flow. +//! +//! [spec]: https://tc39.es/ecma262/#prod-Statement +//! [statements]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements + +mod block; +mod r#if; +mod labelled; +mod r#return; +mod switch; +mod throw; +mod r#try; + +pub mod iteration; + +pub use self::{ + block::Block, + iteration::{Break, Continue, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, WhileLoop}, + labelled::{Labelled, LabelledItem}, + r#if::If, + r#return::Return, + r#try::{Catch, ErrorHandler, Finally, Try}, + switch::{Case, Switch}, + throw::Throw, +}; +use core::ops::ControlFlow; + +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use boa_interner::{Interner, ToIndentedString, ToInternedString}; + +use super::{declaration::VarDeclaration, expression::Expression}; + +/// The `Statement` Parse Node. +/// +/// See the [module level documentation][self] for more information. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum Statement { + /// See [`Block`]. + Block(Block), + + /// See [`VarDeclaration`] + Var(VarDeclaration), + + /// An empty statement. + /// + /// Empty statements do nothing, just return undefined. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-EmptyStatement + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/Empty + Empty, + + /// See [`Expression`]. + Expression(Expression), + + /// See [`If`]. + If(If), + + /// See [`DoWhileLoop`]. + DoWhileLoop(DoWhileLoop), + + /// See [`WhileLoop`]. + WhileLoop(WhileLoop), + + /// See [`ForLoop`]. + ForLoop(ForLoop), + + /// See [`ForInLoop`]. + ForInLoop(ForInLoop), + + /// See [`ForOfLoop`]. + ForOfLoop(ForOfLoop), + + /// See[`Switch`]. + Switch(Switch), + + /// See [`Continue`]. + Continue(Continue), + + /// See [`Break`]. + Break(Break), + + /// See [`Return`]. + Return(Return), + + // TODO: Possibly add `with` statements. + /// See [`Labelled`]. + Labelled(Labelled), + + /// See [`Throw`]. + Throw(Throw), + + /// See [`Try`]. + Try(Try), +} + +impl Statement { + /// Implements the display formatting with indentation. + /// + /// This will not prefix the value with any indentation. If you want to prefix this with proper + /// indents, use [`to_indented_string()`](Self::to_indented_string). + pub(super) fn to_no_indent_string(&self, interner: &Interner, indentation: usize) -> String { + let mut s = match self { + Self::Block(block) => return block.to_indented_string(interner, indentation), + Self::Var(var) => var.to_interned_string(interner), + Self::Empty => return ";".to_owned(), + Self::Expression(expr) => expr.to_indented_string(interner, indentation), + Self::If(if_smt) => return if_smt.to_indented_string(interner, indentation), + Self::DoWhileLoop(do_while) => do_while.to_indented_string(interner, indentation), + Self::WhileLoop(while_loop) => { + return while_loop.to_indented_string(interner, indentation) + } + Self::ForLoop(for_loop) => return for_loop.to_indented_string(interner, indentation), + Self::ForInLoop(for_in) => return for_in.to_indented_string(interner, indentation), + Self::ForOfLoop(for_of) => return for_of.to_indented_string(interner, indentation), + Self::Switch(switch) => return switch.to_indented_string(interner, indentation), + Self::Continue(cont) => cont.to_interned_string(interner), + Self::Break(break_smt) => break_smt.to_interned_string(interner), + Self::Return(ret) => ret.to_interned_string(interner), + Self::Labelled(labelled) => return labelled.to_interned_string(interner), + Self::Throw(throw) => throw.to_interned_string(interner), + Self::Try(try_catch) => return try_catch.to_indented_string(interner, indentation), + }; + s.push(';'); + s + } + + /// Abstract operation [`IsLabelledFunction`][spec]. + /// + /// This recursively checks if this `Statement` is a labelled function, since adding + /// several labels in a function should not change the return value of the abstract operation: + /// + /// ```Javascript + /// l1: l2: l3: l4: function f(){ } + /// ``` + /// + /// This should return `true` for that snippet. + /// + /// [spec]: https://tc39.es/ecma262/#sec-islabelledfunction + #[inline] + #[must_use] + pub fn is_labelled_function(&self) -> bool { + match self { + Self::Labelled(stmt) => match stmt.item() { + LabelledItem::Function(_) => true, + LabelledItem::Statement(stmt) => stmt.is_labelled_function(), + }, + _ => false, + } + } +} + +impl ToIndentedString for Statement { + /// Creates a string of the value of the node with the given indentation. For example, an + /// indent level of 2 would produce this: + /// + /// ```js + /// function hello() { + /// console.log("hello"); + /// } + /// hello(); + /// a = 2; + /// ``` + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let mut buf = match *self { + Self::Block(_) => String::new(), + _ => " ".repeat(indentation), + }; + + buf.push_str(&self.to_no_indent_string(interner, indentation)); + + buf + } +} + +impl VisitWith for Statement { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::Block(b) => visitor.visit_block(b), + Self::Var(v) => visitor.visit_var_declaration(v), + Self::Empty => { + // do nothing; there is nothing to visit here + ControlFlow::Continue(()) + } + Self::Expression(e) => visitor.visit_expression(e), + Self::If(i) => visitor.visit_if(i), + Self::DoWhileLoop(dw) => visitor.visit_do_while_loop(dw), + Self::WhileLoop(w) => visitor.visit_while_loop(w), + Self::ForLoop(f) => visitor.visit_for_loop(f), + Self::ForInLoop(fi) => visitor.visit_for_in_loop(fi), + Self::ForOfLoop(fo) => visitor.visit_for_of_loop(fo), + Self::Switch(s) => visitor.visit_switch(s), + Self::Continue(c) => visitor.visit_continue(c), + Self::Break(b) => visitor.visit_break(b), + Self::Return(r) => visitor.visit_return(r), + Self::Labelled(l) => visitor.visit_labelled(l), + Self::Throw(th) => visitor.visit_throw(th), + Self::Try(tr) => visitor.visit_try(tr), + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::Block(b) => visitor.visit_block_mut(b), + Self::Var(v) => visitor.visit_var_declaration_mut(v), + Self::Empty => { + // do nothing; there is nothing to visit here + ControlFlow::Continue(()) + } + Self::Expression(e) => visitor.visit_expression_mut(e), + Self::If(i) => visitor.visit_if_mut(i), + Self::DoWhileLoop(dw) => visitor.visit_do_while_loop_mut(dw), + Self::WhileLoop(w) => visitor.visit_while_loop_mut(w), + Self::ForLoop(f) => visitor.visit_for_loop_mut(f), + Self::ForInLoop(fi) => visitor.visit_for_in_loop_mut(fi), + Self::ForOfLoop(fo) => visitor.visit_for_of_loop_mut(fo), + Self::Switch(s) => visitor.visit_switch_mut(s), + Self::Continue(c) => visitor.visit_continue_mut(c), + Self::Break(b) => visitor.visit_break_mut(b), + Self::Return(r) => visitor.visit_return_mut(r), + Self::Labelled(l) => visitor.visit_labelled_mut(l), + Self::Throw(th) => visitor.visit_throw_mut(th), + Self::Try(tr) => visitor.visit_try_mut(tr), + } + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/statement/return.rs b/javascript-engine/external/boa/boa_ast/src/statement/return.rs new file mode 100644 index 0000000..9477a28 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/statement/return.rs @@ -0,0 +1,84 @@ +use crate::{ + expression::Expression, + statement::Statement, + visitor::{VisitWith, Visitor, VisitorMut}, +}; +use boa_interner::{Interner, ToInternedString}; +use core::ops::ControlFlow; + +/// The `return` statement ends function execution and specifies a value to be returned to the +/// function caller. +/// +/// Syntax: `return [expression];` +/// +/// `expression`: +/// > The expression whose value is to be returned. If omitted, `undefined` is returned instead. +/// +/// When a `return` statement is used in a function body, the execution of the function is +/// stopped. If specified, a given value is returned to the function caller. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-ReturnStatement +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Return { + target: Option, +} + +impl Return { + /// Gets the target expression value of this `Return` statement. + #[must_use] + pub const fn target(&self) -> Option<&Expression> { + self.target.as_ref() + } + + /// Creates a `Return` AST node. + #[must_use] + pub const fn new(expression: Option) -> Self { + Self { target: expression } + } +} + +impl From for Statement { + fn from(return_smt: Return) -> Self { + Self::Return(return_smt) + } +} + +impl ToInternedString for Return { + fn to_interned_string(&self, interner: &Interner) -> String { + self.target().map_or_else( + || "return".to_owned(), + |ex| format!("return {}", ex.to_interned_string(interner)), + ) + } +} + +impl VisitWith for Return { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + if let Some(expr) = &self.target { + visitor.visit_expression(expr) + } else { + ControlFlow::Continue(()) + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + if let Some(expr) = &mut self.target { + visitor.visit_expression_mut(expr) + } else { + ControlFlow::Continue(()) + } + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/statement/switch.rs b/javascript-engine/external/boa/boa_ast/src/statement/switch.rs new file mode 100644 index 0000000..6da9f8e --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/statement/switch.rs @@ -0,0 +1,189 @@ +//! Switch node. +//! +use crate::{ + expression::Expression, + statement::Statement, + try_break, + visitor::{VisitWith, Visitor, VisitorMut}, + StatementList, +}; +use boa_interner::{Interner, ToIndentedString, ToInternedString}; +use core::ops::ControlFlow; + +/// A case clause inside a [`Switch`] statement, as defined by the [spec]. +/// +/// Even though every [`Case`] body is a [`StatementList`], it doesn't create a new lexical +/// environment. This means any variable declared in a `Case` will be considered as part of the +/// lexical environment of the parent [`Switch`] block. +/// +/// [spec]: https://tc39.es/ecma262/#prod-CaseClause +/// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/Truthy +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Case { + condition: Expression, + body: StatementList, +} + +impl Case { + /// Creates a `Case` AST node. + #[inline] + #[must_use] + pub const fn new(condition: Expression, body: StatementList) -> Self { + Self { condition, body } + } + + /// Gets the condition of the case. + #[inline] + #[must_use] + pub const fn condition(&self) -> &Expression { + &self.condition + } + + /// Gets the statement listin the body of the case. + #[inline] + #[must_use] + pub const fn body(&self) -> &StatementList { + &self.body + } +} + +impl VisitWith for Case { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_expression(&self.condition)); + visitor.visit_statement_list(&self.body) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_expression_mut(&mut self.condition)); + visitor.visit_statement_list_mut(&mut self.body) + } +} + +/// The `switch` statement evaluates an expression, matching the expression's value to a case +/// clause, and executes statements associated with that case, as well as statements in cases +/// that follow the matching case. +/// +/// A `switch` statement first evaluates its expression. It then looks for the first case +/// clause whose expression evaluates to the same value as the result of the input expression +/// (using the strict comparison, `===`) and transfers control to that clause, executing the +/// associated statements. (If multiple cases match the provided value, the first case that +/// matches is selected, even if the cases are not equal to each other.) +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-SwitchStatement +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Switch { + val: Expression, + cases: Box<[Case]>, + default: Option, +} + +impl Switch { + /// Creates a `Switch` AST node. + #[inline] + #[must_use] + pub fn new(val: Expression, cases: Box<[Case]>, default: Option) -> Self { + Self { + val, + cases, + default, + } + } + + /// Gets the value to switch. + #[inline] + #[must_use] + pub const fn val(&self) -> &Expression { + &self.val + } + + /// Gets the list of cases for the switch statement. + #[inline] + #[must_use] + pub const fn cases(&self) -> &[Case] { + &self.cases + } + + /// Gets the default statement list, if any. + #[inline] + #[must_use] + pub const fn default(&self) -> Option<&StatementList> { + self.default.as_ref() + } +} + +impl ToIndentedString for Switch { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let indent = " ".repeat(indentation); + let mut buf = format!("switch ({}) {{\n", self.val().to_interned_string(interner)); + for e in self.cases().iter() { + buf.push_str(&format!( + "{} case {}:\n{}", + indent, + e.condition().to_interned_string(interner), + e.body().to_indented_string(interner, indentation + 2) + )); + } + + if let Some(ref default) = self.default { + buf.push_str(&format!( + "{indent} default:\n{}", + default.to_indented_string(interner, indentation + 2) + )); + } + buf.push_str(&format!("{indent}}}")); + + buf + } +} + +impl From for Statement { + #[inline] + fn from(switch: Switch) -> Self { + Self::Switch(switch) + } +} + +impl VisitWith for Switch { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_expression(&self.val)); + for case in self.cases.iter() { + try_break!(visitor.visit_case(case)); + } + if let Some(sl) = &self.default { + try_break!(visitor.visit_statement_list(sl)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_expression_mut(&mut self.val)); + for case in self.cases.iter_mut() { + try_break!(visitor.visit_case_mut(case)); + } + if let Some(sl) = &mut self.default { + try_break!(visitor.visit_statement_list_mut(sl)); + } + ControlFlow::Continue(()) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/statement/throw.rs b/javascript-engine/external/boa/boa_ast/src/statement/throw.rs new file mode 100644 index 0000000..90df3b0 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/statement/throw.rs @@ -0,0 +1,70 @@ +use crate::{ + statement::Statement, + visitor::{VisitWith, Visitor, VisitorMut}, + Expression, +}; +use boa_interner::{Interner, ToInternedString}; +use core::ops::ControlFlow; + +/// The `throw` statement throws a user-defined exception. +/// +/// Syntax: `throw expression;` +/// +/// Execution of the current function will stop (the statements after throw won't be executed), +/// and control will be passed to the first catch block in the call stack. If no catch block +/// exists among caller functions, the program will terminate. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-ThrowStatement +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Throw { + target: Expression, +} + +impl Throw { + /// Gets the target expression of this `Throw` statement. + #[must_use] + pub const fn target(&self) -> &Expression { + &self.target + } + + /// Creates a `Throw` AST node. + #[must_use] + pub const fn new(target: Expression) -> Self { + Self { target } + } +} + +impl ToInternedString for Throw { + fn to_interned_string(&self, interner: &Interner) -> String { + format!("throw {}", self.target.to_interned_string(interner)) + } +} + +impl From for Statement { + fn from(trw: Throw) -> Self { + Self::Throw(trw) + } +} + +impl VisitWith for Throw { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + visitor.visit_expression(&self.target) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + visitor.visit_expression_mut(&mut self.target) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/statement/try.rs b/javascript-engine/external/boa/boa_ast/src/statement/try.rs new file mode 100644 index 0000000..2e5a67b --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/statement/try.rs @@ -0,0 +1,256 @@ +//! Error handling statements + +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ + declaration::Binding, + statement::{Block, Statement}, +}; +use boa_interner::{Interner, ToIndentedString, ToInternedString}; +use core::ops::ControlFlow; + +/// The `try...catch` statement marks a block of statements to try and specifies a response +/// should an exception be thrown. +/// +/// The `try` statement consists of a `try`-block, which contains one or more statements. `{}` +/// must always be used, even for single statements. At least one `catch`-block, or a +/// `finally`-block, must be present. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-TryStatement +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Try { + block: Block, + handler: ErrorHandler, +} + +/// The type of error handler in a [`Try`] statement. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum ErrorHandler { + /// A [`Catch`] error handler. + Catch(Catch), + /// A [`Finally`] error handler. + Finally(Finally), + /// A [`Catch`] and [`Finally`] error handler. + Full(Catch, Finally), +} + +impl Try { + /// Creates a new `Try` AST node. + #[inline] + #[must_use] + pub const fn new(block: Block, handler: ErrorHandler) -> Self { + Self { block, handler } + } + + /// Gets the `try` block. + #[inline] + #[must_use] + pub const fn block(&self) -> &Block { + &self.block + } + + /// Gets the `catch` block, if any. + #[inline] + #[must_use] + pub const fn catch(&self) -> Option<&Catch> { + match &self.handler { + ErrorHandler::Catch(c) | ErrorHandler::Full(c, _) => Some(c), + ErrorHandler::Finally(_) => None, + } + } + + /// Gets the `finally` block, if any. + #[inline] + #[must_use] + pub const fn finally(&self) -> Option<&Finally> { + match &self.handler { + ErrorHandler::Finally(f) | ErrorHandler::Full(_, f) => Some(f), + ErrorHandler::Catch(_) => None, + } + } +} + +impl ToIndentedString for Try { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let mut buf = format!( + "{}try {}", + " ".repeat(indentation), + self.block.to_indented_string(interner, indentation) + ); + + if let Some(catch) = self.catch() { + buf.push_str(&catch.to_indented_string(interner, indentation)); + } + + if let Some(finally) = self.finally() { + buf.push_str(&finally.to_indented_string(interner, indentation)); + } + buf + } +} + +impl From for Statement { + #[inline] + fn from(try_catch: Try) -> Self { + Self::Try(try_catch) + } +} + +impl VisitWith for Try { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_block(&self.block)); + if let Some(catch) = &self.catch() { + try_break!(visitor.visit_catch(catch)); + } + if let Some(finally) = &self.finally() { + try_break!(visitor.visit_finally(finally)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_block_mut(&mut self.block)); + match &mut self.handler { + ErrorHandler::Catch(c) => try_break!(visitor.visit_catch_mut(c)), + ErrorHandler::Finally(f) => try_break!(visitor.visit_finally_mut(f)), + ErrorHandler::Full(c, f) => { + try_break!(visitor.visit_catch_mut(c)); + try_break!(visitor.visit_finally_mut(f)); + } + } + ControlFlow::Continue(()) + } +} + +/// Catch block. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Catch { + parameter: Option, + block: Block, +} + +impl Catch { + /// Creates a new catch block. + #[inline] + #[must_use] + pub const fn new(parameter: Option, block: Block) -> Self { + Self { parameter, block } + } + + /// Gets the parameter of the catch block. + #[inline] + #[must_use] + pub const fn parameter(&self) -> Option<&Binding> { + self.parameter.as_ref() + } + + /// Retrieves the catch execution block. + #[inline] + #[must_use] + pub const fn block(&self) -> &Block { + &self.block + } +} + +impl ToIndentedString for Catch { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let mut buf = " catch".to_owned(); + if let Some(ref param) = self.parameter { + buf.push_str(&format!("({})", param.to_interned_string(interner))); + } + buf.push_str(&format!( + " {}", + self.block.to_indented_string(interner, indentation) + )); + + buf + } +} + +impl VisitWith for Catch { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + if let Some(binding) = &self.parameter { + try_break!(visitor.visit_binding(binding)); + } + visitor.visit_block(&self.block) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + if let Some(binding) = &mut self.parameter { + try_break!(visitor.visit_binding_mut(binding)); + } + visitor.visit_block_mut(&mut self.block) + } +} + +/// Finally block. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub struct Finally { + block: Block, +} + +impl Finally { + /// Gets the finally block. + #[inline] + #[must_use] + pub const fn block(&self) -> &Block { + &self.block + } +} + +impl ToIndentedString for Finally { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + format!( + " finally {}", + self.block.to_indented_string(interner, indentation) + ) + } +} + +impl From for Finally { + #[inline] + fn from(block: Block) -> Self { + Self { block } + } +} + +impl VisitWith for Finally { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + visitor.visit_block(&self.block) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + visitor.visit_block_mut(&mut self.block) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/statement_list.rs b/javascript-engine/external/boa/boa_ast/src/statement_list.rs new file mode 100644 index 0000000..2202cc8 --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/statement_list.rs @@ -0,0 +1,215 @@ +//! Statement list node. + +use super::Declaration; +use crate::{ + statement::Statement, + try_break, + visitor::{VisitWith, Visitor, VisitorMut}, +}; +use boa_interner::{Interner, ToIndentedString}; +use core::ops::ControlFlow; + +use std::cmp::Ordering; + +/// An item inside a [`StatementList`] Parse Node, as defined by the [spec]. +/// +/// Items in a `StatementList` can be either [`Declaration`]s (functions, classes, let/const declarations) +/// or [`Statement`]s (if, while, var statement). +/// +/// [spec]: https://tc39.es/ecma262/#prod-StatementListItem +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Debug, PartialEq)] +pub enum StatementListItem { + /// See [`Statement`]. + Statement(Statement), + /// See [`Declaration`]. + Declaration(Declaration), +} + +impl StatementListItem { + /// Returns a node ordering based on the hoistability of each statement. + #[must_use] + pub const fn hoistable_order(a: &Self, b: &Self) -> Ordering { + match (a, b) { + ( + Self::Declaration(Declaration::Function(_)), + Self::Declaration(Declaration::Function(_)), + ) => Ordering::Equal, + (_, Self::Declaration(Declaration::Function(_))) => Ordering::Greater, + (Self::Declaration(Declaration::Function(_)), _) => Ordering::Less, + + (_, _) => Ordering::Equal, + } + } +} + +impl ToIndentedString for StatementListItem { + /// Creates a string of the value of the node with the given indentation. For example, an + /// indent level of 2 would produce this: + /// + /// ```js + /// function hello() { + /// console.log("hello"); + /// } + /// hello(); + /// a = 2; + /// ``` + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let mut buf = " ".repeat(indentation); + + match self { + Self::Statement(stmt) => { + buf.push_str(&stmt.to_no_indent_string(interner, indentation)); + } + Self::Declaration(decl) => { + buf.push_str(&decl.to_indented_string(interner, indentation)); + } + } + + buf + } +} + +impl From for StatementListItem { + #[inline] + fn from(stmt: Statement) -> Self { + Self::Statement(stmt) + } +} + +impl From for StatementListItem { + #[inline] + fn from(decl: Declaration) -> Self { + Self::Declaration(decl) + } +} + +impl VisitWith for StatementListItem { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + Self::Statement(statement) => visitor.visit_statement(statement), + Self::Declaration(declaration) => visitor.visit_declaration(declaration), + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + Self::Statement(statement) => visitor.visit_statement_mut(statement), + Self::Declaration(declaration) => visitor.visit_declaration_mut(declaration), + } + } +} + +/// List of statements. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#prod-StatementList +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, Default, PartialEq)] +pub struct StatementList { + statements: Box<[StatementListItem]>, + strict: bool, +} + +impl StatementList { + /// Creates a new `StatementList` AST node. + #[must_use] + pub fn new(statements: S, strict: bool) -> Self + where + S: Into>, + { + Self { + statements: statements.into(), + strict, + } + } + + /// Gets the list of statements. + #[inline] + #[must_use] + pub const fn statements(&self) -> &[StatementListItem] { + &self.statements + } + + /// Get the strict mode. + #[inline] + #[must_use] + pub const fn strict(&self) -> bool { + self.strict + } +} + +impl From> for StatementList { + #[inline] + fn from(stm: Box<[StatementListItem]>) -> Self { + Self { + statements: stm, + strict: false, + } + } +} + +impl From> for StatementList { + #[inline] + fn from(stm: Vec) -> Self { + Self { + statements: stm.into(), + strict: false, + } + } +} + +impl ToIndentedString for StatementList { + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let mut buf = String::new(); + // Print statements + for item in self.statements.iter() { + // We rely on the node to add the correct indent. + buf.push_str(&item.to_indented_string(interner, indentation)); + + buf.push('\n'); + } + buf + } +} + +impl VisitWith for StatementList { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + for statement in self.statements.iter() { + try_break!(visitor.visit_statement_list_item(statement)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + for statement in self.statements.iter_mut() { + try_break!(visitor.visit_statement_list_item_mut(statement)); + } + ControlFlow::Continue(()) + } +} + +#[cfg(feature = "fuzz")] +impl<'a> arbitrary::Arbitrary<'a> for StatementList { + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + Ok(Self { + statements: u.arbitrary()?, + strict: false, // disable strictness; this is *not* in source data + }) + } +} diff --git a/javascript-engine/external/boa/boa_ast/src/visitor.rs b/javascript-engine/external/boa/boa_ast/src/visitor.rs new file mode 100644 index 0000000..2b4e59b --- /dev/null +++ b/javascript-engine/external/boa/boa_ast/src/visitor.rs @@ -0,0 +1,569 @@ +//! ECMAScript Abstract Syntax Tree visitors. +//! +//! This module contains visitors which can be used to inspect or modify AST nodes. This allows for +//! fine-grained manipulation of ASTs for analysis, rewriting, or instrumentation. + +use std::ops::ControlFlow; + +use crate::{ + declaration::{ + Binding, Declaration, LexicalDeclaration, VarDeclaration, Variable, VariableList, + }, + expression::{ + access::{ + PrivatePropertyAccess, PropertyAccess, PropertyAccessField, SimplePropertyAccess, + SuperPropertyAccess, + }, + literal::{ArrayLiteral, Literal, ObjectLiteral, TemplateElement, TemplateLiteral}, + operator::{ + assign::{Assign, AssignTarget}, + Binary, Conditional, Unary, + }, + Await, Call, Expression, Identifier, New, Optional, OptionalOperation, + OptionalOperationKind, Spread, SuperCall, TaggedTemplate, Yield, + }, + function::{ + ArrowFunction, AsyncArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, + FormalParameter, FormalParameterList, Function, Generator, PrivateName, + }, + pattern::{ArrayPattern, ArrayPatternElement, ObjectPattern, ObjectPatternElement, Pattern}, + property::{MethodDefinition, PropertyDefinition, PropertyName}, + statement::{ + iteration::{ + Break, Continue, DoWhileLoop, ForInLoop, ForLoop, ForLoopInitializer, ForOfLoop, + IterableLoopInitializer, WhileLoop, + }, + Block, Case, Catch, Finally, If, Labelled, LabelledItem, Return, Statement, Switch, Throw, + Try, + }, + StatementList, StatementListItem, +}; +use boa_interner::Sym; + +/// `Try`-like conditional unwrapping of `ControlFlow`. +#[macro_export] +macro_rules! try_break { + ($expr:expr) => { + match $expr { + core::ops::ControlFlow::Continue(c) => c, + core::ops::ControlFlow::Break(b) => return core::ops::ControlFlow::Break(b), + } + }; +} + +/// Creates the default visit function implementation for a particular type +macro_rules! define_visit { + ($fn_name:ident, $type_name:ident) => { + #[doc = concat!("Visits a `", stringify!($type_name), "` with this visitor")] + fn $fn_name(&mut self, node: &'ast $type_name) -> ControlFlow { + node.visit_with(self) + } + }; +} + +/// Creates the default mutable visit function implementation for a particular type +macro_rules! define_visit_mut { + ($fn_name:ident, $type_name:ident) => { + #[doc = concat!("Visits a `", stringify!($type_name), "` with this visitor, mutably")] + fn $fn_name(&mut self, node: &'ast mut $type_name) -> ControlFlow { + node.visit_with_mut(self) + } + }; +} + +/// Generates the `NodeRef` and `NodeMutRef` enums from a list of variants. +macro_rules! node_ref { + ( + $( + $Variant:ident + ),* + $(,)? + ) => { + /// A reference to a node visitable by a [`Visitor`]. + #[derive(Debug, Clone, Copy)] + #[allow(missing_docs)] + pub enum NodeRef<'a> { + $( + $Variant(&'a $Variant) + ),* + } + + $( + impl<'a> From<&'a $Variant> for NodeRef<'a> { + fn from(node: &'a $Variant) -> NodeRef<'a> { + Self::$Variant(node) + } + } + )* + + /// A mutable reference to a node visitable by a [`VisitorMut`]. + #[derive(Debug)] + #[allow(missing_docs)] + pub enum NodeRefMut<'a> { + $( + $Variant(&'a mut $Variant) + ),* + } + + $( + impl<'a> From<&'a mut $Variant> for NodeRefMut<'a> { + fn from(node: &'a mut $Variant) -> NodeRefMut<'a> { + Self::$Variant(node) + } + } + )* + } +} + +node_ref! { + StatementList, + StatementListItem, + Statement, + Declaration, + Function, + Generator, + AsyncFunction, + AsyncGenerator, + Class, + LexicalDeclaration, + Block, + VarDeclaration, + Expression, + If, + DoWhileLoop, + WhileLoop, + ForLoop, + ForInLoop, + ForOfLoop, + Switch, + Continue, + Break, + Return, + Labelled, + Throw, + Try, + Identifier, + FormalParameterList, + ClassElement, + PrivateName, + VariableList, + Variable, + Binding, + Pattern, + Literal, + ArrayLiteral, + ObjectLiteral, + Spread, + ArrowFunction, + AsyncArrowFunction, + TemplateLiteral, + PropertyAccess, + New, + Call, + SuperCall, + Optional, + TaggedTemplate, + Assign, + Unary, + Binary, + Conditional, + Await, + Yield, + ForLoopInitializer, + IterableLoopInitializer, + Case, + Sym, + LabelledItem, + Catch, + Finally, + FormalParameter, + PropertyName, + MethodDefinition, + ObjectPattern, + ArrayPattern, + PropertyDefinition, + TemplateElement, + SimplePropertyAccess, + PrivatePropertyAccess, + SuperPropertyAccess, + OptionalOperation, + AssignTarget, + ObjectPatternElement, + ArrayPatternElement, + PropertyAccessField, + OptionalOperationKind, +} + +/// Represents an AST visitor. +/// +/// This implementation is based largely on [chalk](https://github.com/rust-lang/chalk/blob/23d39c90ceb9242fbd4c43e9368e813e7c2179f7/chalk-ir/src/visit.rs)'s +/// visitor pattern. +pub trait Visitor<'ast>: Sized { + /// Type which will be propagated from the visitor if completing early. + type BreakTy; + + define_visit!(visit_statement_list, StatementList); + define_visit!(visit_statement_list_item, StatementListItem); + define_visit!(visit_statement, Statement); + define_visit!(visit_declaration, Declaration); + define_visit!(visit_function, Function); + define_visit!(visit_generator, Generator); + define_visit!(visit_async_function, AsyncFunction); + define_visit!(visit_async_generator, AsyncGenerator); + define_visit!(visit_class, Class); + define_visit!(visit_lexical_declaration, LexicalDeclaration); + define_visit!(visit_block, Block); + define_visit!(visit_var_declaration, VarDeclaration); + define_visit!(visit_expression, Expression); + define_visit!(visit_if, If); + define_visit!(visit_do_while_loop, DoWhileLoop); + define_visit!(visit_while_loop, WhileLoop); + define_visit!(visit_for_loop, ForLoop); + define_visit!(visit_for_in_loop, ForInLoop); + define_visit!(visit_for_of_loop, ForOfLoop); + define_visit!(visit_switch, Switch); + define_visit!(visit_continue, Continue); + define_visit!(visit_break, Break); + define_visit!(visit_return, Return); + define_visit!(visit_labelled, Labelled); + define_visit!(visit_throw, Throw); + define_visit!(visit_try, Try); + define_visit!(visit_identifier, Identifier); + define_visit!(visit_formal_parameter_list, FormalParameterList); + define_visit!(visit_class_element, ClassElement); + define_visit!(visit_private_name, PrivateName); + define_visit!(visit_variable_list, VariableList); + define_visit!(visit_variable, Variable); + define_visit!(visit_binding, Binding); + define_visit!(visit_pattern, Pattern); + define_visit!(visit_literal, Literal); + define_visit!(visit_array_literal, ArrayLiteral); + define_visit!(visit_object_literal, ObjectLiteral); + define_visit!(visit_spread, Spread); + define_visit!(visit_arrow_function, ArrowFunction); + define_visit!(visit_async_arrow_function, AsyncArrowFunction); + define_visit!(visit_template_literal, TemplateLiteral); + define_visit!(visit_property_access, PropertyAccess); + define_visit!(visit_new, New); + define_visit!(visit_call, Call); + define_visit!(visit_super_call, SuperCall); + define_visit!(visit_optional, Optional); + define_visit!(visit_tagged_template, TaggedTemplate); + define_visit!(visit_assign, Assign); + define_visit!(visit_unary, Unary); + define_visit!(visit_binary, Binary); + define_visit!(visit_conditional, Conditional); + define_visit!(visit_await, Await); + define_visit!(visit_yield, Yield); + define_visit!(visit_for_loop_initializer, ForLoopInitializer); + define_visit!(visit_iterable_loop_initializer, IterableLoopInitializer); + define_visit!(visit_case, Case); + define_visit!(visit_sym, Sym); + define_visit!(visit_labelled_item, LabelledItem); + define_visit!(visit_catch, Catch); + define_visit!(visit_finally, Finally); + define_visit!(visit_formal_parameter, FormalParameter); + define_visit!(visit_property_name, PropertyName); + define_visit!(visit_method_definition, MethodDefinition); + define_visit!(visit_object_pattern, ObjectPattern); + define_visit!(visit_array_pattern, ArrayPattern); + define_visit!(visit_property_definition, PropertyDefinition); + define_visit!(visit_template_element, TemplateElement); + define_visit!(visit_simple_property_access, SimplePropertyAccess); + define_visit!(visit_private_property_access, PrivatePropertyAccess); + define_visit!(visit_super_property_access, SuperPropertyAccess); + define_visit!(visit_optional_operation, OptionalOperation); + define_visit!(visit_assign_target, AssignTarget); + define_visit!(visit_object_pattern_element, ObjectPatternElement); + define_visit!(visit_array_pattern_element, ArrayPatternElement); + define_visit!(visit_property_access_field, PropertyAccessField); + define_visit!(visit_optional_operation_kind, OptionalOperationKind); + + /// Generic entry point for a node that is visitable by a `Visitor`. + /// + /// This is usually used for generic functions that need to visit an unnamed AST node. + fn visit>>(&mut self, node: N) -> ControlFlow { + let node = node.into(); + match node { + NodeRef::StatementList(n) => self.visit_statement_list(n), + NodeRef::StatementListItem(n) => self.visit_statement_list_item(n), + NodeRef::Statement(n) => self.visit_statement(n), + NodeRef::Declaration(n) => self.visit_declaration(n), + NodeRef::Function(n) => self.visit_function(n), + NodeRef::Generator(n) => self.visit_generator(n), + NodeRef::AsyncFunction(n) => self.visit_async_function(n), + NodeRef::AsyncGenerator(n) => self.visit_async_generator(n), + NodeRef::Class(n) => self.visit_class(n), + NodeRef::LexicalDeclaration(n) => self.visit_lexical_declaration(n), + NodeRef::Block(n) => self.visit_block(n), + NodeRef::VarDeclaration(n) => self.visit_var_declaration(n), + NodeRef::Expression(n) => self.visit_expression(n), + NodeRef::If(n) => self.visit_if(n), + NodeRef::DoWhileLoop(n) => self.visit_do_while_loop(n), + NodeRef::WhileLoop(n) => self.visit_while_loop(n), + NodeRef::ForLoop(n) => self.visit_for_loop(n), + NodeRef::ForInLoop(n) => self.visit_for_in_loop(n), + NodeRef::ForOfLoop(n) => self.visit_for_of_loop(n), + NodeRef::Switch(n) => self.visit_switch(n), + NodeRef::Continue(n) => self.visit_continue(n), + NodeRef::Break(n) => self.visit_break(n), + NodeRef::Return(n) => self.visit_return(n), + NodeRef::Labelled(n) => self.visit_labelled(n), + NodeRef::Throw(n) => self.visit_throw(n), + NodeRef::Try(n) => self.visit_try(n), + NodeRef::Identifier(n) => self.visit_identifier(n), + NodeRef::FormalParameterList(n) => self.visit_formal_parameter_list(n), + NodeRef::ClassElement(n) => self.visit_class_element(n), + NodeRef::PrivateName(n) => self.visit_private_name(n), + NodeRef::VariableList(n) => self.visit_variable_list(n), + NodeRef::Variable(n) => self.visit_variable(n), + NodeRef::Binding(n) => self.visit_binding(n), + NodeRef::Pattern(n) => self.visit_pattern(n), + NodeRef::Literal(n) => self.visit_literal(n), + NodeRef::ArrayLiteral(n) => self.visit_array_literal(n), + NodeRef::ObjectLiteral(n) => self.visit_object_literal(n), + NodeRef::Spread(n) => self.visit_spread(n), + NodeRef::ArrowFunction(n) => self.visit_arrow_function(n), + NodeRef::AsyncArrowFunction(n) => self.visit_async_arrow_function(n), + NodeRef::TemplateLiteral(n) => self.visit_template_literal(n), + NodeRef::PropertyAccess(n) => self.visit_property_access(n), + NodeRef::New(n) => self.visit_new(n), + NodeRef::Call(n) => self.visit_call(n), + NodeRef::SuperCall(n) => self.visit_super_call(n), + NodeRef::Optional(n) => self.visit_optional(n), + NodeRef::TaggedTemplate(n) => self.visit_tagged_template(n), + NodeRef::Assign(n) => self.visit_assign(n), + NodeRef::Unary(n) => self.visit_unary(n), + NodeRef::Binary(n) => self.visit_binary(n), + NodeRef::Conditional(n) => self.visit_conditional(n), + NodeRef::Await(n) => self.visit_await(n), + NodeRef::Yield(n) => self.visit_yield(n), + NodeRef::ForLoopInitializer(n) => self.visit_for_loop_initializer(n), + NodeRef::IterableLoopInitializer(n) => self.visit_iterable_loop_initializer(n), + NodeRef::Case(n) => self.visit_case(n), + NodeRef::Sym(n) => self.visit_sym(n), + NodeRef::LabelledItem(n) => self.visit_labelled_item(n), + NodeRef::Catch(n) => self.visit_catch(n), + NodeRef::Finally(n) => self.visit_finally(n), + NodeRef::FormalParameter(n) => self.visit_formal_parameter(n), + NodeRef::PropertyName(n) => self.visit_property_name(n), + NodeRef::MethodDefinition(n) => self.visit_method_definition(n), + NodeRef::ObjectPattern(n) => self.visit_object_pattern(n), + NodeRef::ArrayPattern(n) => self.visit_array_pattern(n), + NodeRef::PropertyDefinition(n) => self.visit_property_definition(n), + NodeRef::TemplateElement(n) => self.visit_template_element(n), + NodeRef::SimplePropertyAccess(n) => self.visit_simple_property_access(n), + NodeRef::PrivatePropertyAccess(n) => self.visit_private_property_access(n), + NodeRef::SuperPropertyAccess(n) => self.visit_super_property_access(n), + NodeRef::OptionalOperation(n) => self.visit_optional_operation(n), + NodeRef::AssignTarget(n) => self.visit_assign_target(n), + NodeRef::ObjectPatternElement(n) => self.visit_object_pattern_element(n), + NodeRef::ArrayPatternElement(n) => self.visit_array_pattern_element(n), + NodeRef::PropertyAccessField(n) => self.visit_property_access_field(n), + NodeRef::OptionalOperationKind(n) => self.visit_optional_operation_kind(n), + } + } +} + +/// Represents an AST visitor which can modify AST content. +/// +/// This implementation is based largely on [chalk](https://github.com/rust-lang/chalk/blob/23d39c90ceb9242fbd4c43e9368e813e7c2179f7/chalk-ir/src/visit.rs)'s +/// visitor pattern. +pub trait VisitorMut<'ast>: Sized { + /// Type which will be propagated from the visitor if completing early. + type BreakTy; + + define_visit_mut!(visit_statement_list_mut, StatementList); + define_visit_mut!(visit_statement_list_item_mut, StatementListItem); + define_visit_mut!(visit_statement_mut, Statement); + define_visit_mut!(visit_declaration_mut, Declaration); + define_visit_mut!(visit_function_mut, Function); + define_visit_mut!(visit_generator_mut, Generator); + define_visit_mut!(visit_async_function_mut, AsyncFunction); + define_visit_mut!(visit_async_generator_mut, AsyncGenerator); + define_visit_mut!(visit_class_mut, Class); + define_visit_mut!(visit_lexical_declaration_mut, LexicalDeclaration); + define_visit_mut!(visit_block_mut, Block); + define_visit_mut!(visit_var_declaration_mut, VarDeclaration); + define_visit_mut!(visit_expression_mut, Expression); + define_visit_mut!(visit_if_mut, If); + define_visit_mut!(visit_do_while_loop_mut, DoWhileLoop); + define_visit_mut!(visit_while_loop_mut, WhileLoop); + define_visit_mut!(visit_for_loop_mut, ForLoop); + define_visit_mut!(visit_for_in_loop_mut, ForInLoop); + define_visit_mut!(visit_for_of_loop_mut, ForOfLoop); + define_visit_mut!(visit_switch_mut, Switch); + define_visit_mut!(visit_continue_mut, Continue); + define_visit_mut!(visit_break_mut, Break); + define_visit_mut!(visit_return_mut, Return); + define_visit_mut!(visit_labelled_mut, Labelled); + define_visit_mut!(visit_throw_mut, Throw); + define_visit_mut!(visit_try_mut, Try); + define_visit_mut!(visit_identifier_mut, Identifier); + define_visit_mut!(visit_formal_parameter_list_mut, FormalParameterList); + define_visit_mut!(visit_class_element_mut, ClassElement); + define_visit_mut!(visit_private_name_mut, PrivateName); + define_visit_mut!(visit_variable_list_mut, VariableList); + define_visit_mut!(visit_variable_mut, Variable); + define_visit_mut!(visit_binding_mut, Binding); + define_visit_mut!(visit_pattern_mut, Pattern); + define_visit_mut!(visit_literal_mut, Literal); + define_visit_mut!(visit_array_literal_mut, ArrayLiteral); + define_visit_mut!(visit_object_literal_mut, ObjectLiteral); + define_visit_mut!(visit_spread_mut, Spread); + define_visit_mut!(visit_arrow_function_mut, ArrowFunction); + define_visit_mut!(visit_async_arrow_function_mut, AsyncArrowFunction); + define_visit_mut!(visit_template_literal_mut, TemplateLiteral); + define_visit_mut!(visit_property_access_mut, PropertyAccess); + define_visit_mut!(visit_new_mut, New); + define_visit_mut!(visit_call_mut, Call); + define_visit_mut!(visit_super_call_mut, SuperCall); + define_visit_mut!(visit_optional_mut, Optional); + define_visit_mut!(visit_tagged_template_mut, TaggedTemplate); + define_visit_mut!(visit_assign_mut, Assign); + define_visit_mut!(visit_unary_mut, Unary); + define_visit_mut!(visit_binary_mut, Binary); + define_visit_mut!(visit_conditional_mut, Conditional); + define_visit_mut!(visit_await_mut, Await); + define_visit_mut!(visit_yield_mut, Yield); + define_visit_mut!(visit_for_loop_initializer_mut, ForLoopInitializer); + define_visit_mut!(visit_iterable_loop_initializer_mut, IterableLoopInitializer); + define_visit_mut!(visit_case_mut, Case); + define_visit_mut!(visit_sym_mut, Sym); + define_visit_mut!(visit_labelled_item_mut, LabelledItem); + define_visit_mut!(visit_catch_mut, Catch); + define_visit_mut!(visit_finally_mut, Finally); + define_visit_mut!(visit_formal_parameter_mut, FormalParameter); + define_visit_mut!(visit_property_name_mut, PropertyName); + define_visit_mut!(visit_method_definition_mut, MethodDefinition); + define_visit_mut!(visit_object_pattern_mut, ObjectPattern); + define_visit_mut!(visit_array_pattern_mut, ArrayPattern); + define_visit_mut!(visit_property_definition_mut, PropertyDefinition); + define_visit_mut!(visit_template_element_mut, TemplateElement); + define_visit_mut!(visit_simple_property_access_mut, SimplePropertyAccess); + define_visit_mut!(visit_private_property_access_mut, PrivatePropertyAccess); + define_visit_mut!(visit_super_property_access_mut, SuperPropertyAccess); + define_visit_mut!(visit_optional_operation_mut, OptionalOperation); + define_visit_mut!(visit_assign_target_mut, AssignTarget); + define_visit_mut!(visit_object_pattern_element_mut, ObjectPatternElement); + define_visit_mut!(visit_array_pattern_element_mut, ArrayPatternElement); + define_visit_mut!(visit_property_access_field_mut, PropertyAccessField); + define_visit_mut!(visit_optional_operation_kind_mut, OptionalOperationKind); + + /// Generic entry point for a node that is visitable by a `VisitorMut`. + /// + /// This is usually used for generic functions that need to visit an unnamed AST node. + fn visit>>(&mut self, node: N) -> ControlFlow { + let node = node.into(); + match node { + NodeRefMut::StatementList(n) => self.visit_statement_list_mut(n), + NodeRefMut::StatementListItem(n) => self.visit_statement_list_item_mut(n), + NodeRefMut::Statement(n) => self.visit_statement_mut(n), + NodeRefMut::Declaration(n) => self.visit_declaration_mut(n), + NodeRefMut::Function(n) => self.visit_function_mut(n), + NodeRefMut::Generator(n) => self.visit_generator_mut(n), + NodeRefMut::AsyncFunction(n) => self.visit_async_function_mut(n), + NodeRefMut::AsyncGenerator(n) => self.visit_async_generator_mut(n), + NodeRefMut::Class(n) => self.visit_class_mut(n), + NodeRefMut::LexicalDeclaration(n) => self.visit_lexical_declaration_mut(n), + NodeRefMut::Block(n) => self.visit_block_mut(n), + NodeRefMut::VarDeclaration(n) => self.visit_var_declaration_mut(n), + NodeRefMut::Expression(n) => self.visit_expression_mut(n), + NodeRefMut::If(n) => self.visit_if_mut(n), + NodeRefMut::DoWhileLoop(n) => self.visit_do_while_loop_mut(n), + NodeRefMut::WhileLoop(n) => self.visit_while_loop_mut(n), + NodeRefMut::ForLoop(n) => self.visit_for_loop_mut(n), + NodeRefMut::ForInLoop(n) => self.visit_for_in_loop_mut(n), + NodeRefMut::ForOfLoop(n) => self.visit_for_of_loop_mut(n), + NodeRefMut::Switch(n) => self.visit_switch_mut(n), + NodeRefMut::Continue(n) => self.visit_continue_mut(n), + NodeRefMut::Break(n) => self.visit_break_mut(n), + NodeRefMut::Return(n) => self.visit_return_mut(n), + NodeRefMut::Labelled(n) => self.visit_labelled_mut(n), + NodeRefMut::Throw(n) => self.visit_throw_mut(n), + NodeRefMut::Try(n) => self.visit_try_mut(n), + NodeRefMut::Identifier(n) => self.visit_identifier_mut(n), + NodeRefMut::FormalParameterList(n) => self.visit_formal_parameter_list_mut(n), + NodeRefMut::ClassElement(n) => self.visit_class_element_mut(n), + NodeRefMut::PrivateName(n) => self.visit_private_name_mut(n), + NodeRefMut::VariableList(n) => self.visit_variable_list_mut(n), + NodeRefMut::Variable(n) => self.visit_variable_mut(n), + NodeRefMut::Binding(n) => self.visit_binding_mut(n), + NodeRefMut::Pattern(n) => self.visit_pattern_mut(n), + NodeRefMut::Literal(n) => self.visit_literal_mut(n), + NodeRefMut::ArrayLiteral(n) => self.visit_array_literal_mut(n), + NodeRefMut::ObjectLiteral(n) => self.visit_object_literal_mut(n), + NodeRefMut::Spread(n) => self.visit_spread_mut(n), + NodeRefMut::ArrowFunction(n) => self.visit_arrow_function_mut(n), + NodeRefMut::AsyncArrowFunction(n) => self.visit_async_arrow_function_mut(n), + NodeRefMut::TemplateLiteral(n) => self.visit_template_literal_mut(n), + NodeRefMut::PropertyAccess(n) => self.visit_property_access_mut(n), + NodeRefMut::New(n) => self.visit_new_mut(n), + NodeRefMut::Call(n) => self.visit_call_mut(n), + NodeRefMut::SuperCall(n) => self.visit_super_call_mut(n), + NodeRefMut::Optional(n) => self.visit_optional_mut(n), + NodeRefMut::TaggedTemplate(n) => self.visit_tagged_template_mut(n), + NodeRefMut::Assign(n) => self.visit_assign_mut(n), + NodeRefMut::Unary(n) => self.visit_unary_mut(n), + NodeRefMut::Binary(n) => self.visit_binary_mut(n), + NodeRefMut::Conditional(n) => self.visit_conditional_mut(n), + NodeRefMut::Await(n) => self.visit_await_mut(n), + NodeRefMut::Yield(n) => self.visit_yield_mut(n), + NodeRefMut::ForLoopInitializer(n) => self.visit_for_loop_initializer_mut(n), + NodeRefMut::IterableLoopInitializer(n) => self.visit_iterable_loop_initializer_mut(n), + NodeRefMut::Case(n) => self.visit_case_mut(n), + NodeRefMut::Sym(n) => self.visit_sym_mut(n), + NodeRefMut::LabelledItem(n) => self.visit_labelled_item_mut(n), + NodeRefMut::Catch(n) => self.visit_catch_mut(n), + NodeRefMut::Finally(n) => self.visit_finally_mut(n), + NodeRefMut::FormalParameter(n) => self.visit_formal_parameter_mut(n), + NodeRefMut::PropertyName(n) => self.visit_property_name_mut(n), + NodeRefMut::MethodDefinition(n) => self.visit_method_definition_mut(n), + NodeRefMut::ObjectPattern(n) => self.visit_object_pattern_mut(n), + NodeRefMut::ArrayPattern(n) => self.visit_array_pattern_mut(n), + NodeRefMut::PropertyDefinition(n) => self.visit_property_definition_mut(n), + NodeRefMut::TemplateElement(n) => self.visit_template_element_mut(n), + NodeRefMut::SimplePropertyAccess(n) => self.visit_simple_property_access_mut(n), + NodeRefMut::PrivatePropertyAccess(n) => self.visit_private_property_access_mut(n), + NodeRefMut::SuperPropertyAccess(n) => self.visit_super_property_access_mut(n), + NodeRefMut::OptionalOperation(n) => self.visit_optional_operation_mut(n), + NodeRefMut::AssignTarget(n) => self.visit_assign_target_mut(n), + NodeRefMut::ObjectPatternElement(n) => self.visit_object_pattern_element_mut(n), + NodeRefMut::ArrayPatternElement(n) => self.visit_array_pattern_element_mut(n), + NodeRefMut::PropertyAccessField(n) => self.visit_property_access_field_mut(n), + NodeRefMut::OptionalOperationKind(n) => self.visit_optional_operation_kind_mut(n), + } + } +} + +/// Denotes that a type may be visited, providing a method which allows a visitor to traverse its +/// private fields. +pub trait VisitWith { + /// Visit this node with the provided visitor. + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>; + + /// Visit this node with the provided visitor mutably, allowing the visitor to modify private + /// fields. + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>; +} + +// implementation for Sym as it is out-of-crate +impl VisitWith for Sym { + fn visit_with<'a, V>(&'a self, _visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + core::ops::ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, _visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + core::ops::ControlFlow::Continue(()) + } +} diff --git a/javascript-engine/external/boa/boa_cli/Cargo.toml b/javascript-engine/external/boa/boa_cli/Cargo.toml new file mode 100644 index 0000000..610dc8d --- /dev/null +++ b/javascript-engine/external/boa/boa_cli/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "boa_cli" +keywords = ["javascript", "compiler", "js", "cli"] +categories = ["command-line-utilities"] +default-run = "boa" +description.workspace = true +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +boa_engine = { workspace = true, features = ["deser", "console", "flowgraph"] } +boa_ast = { workspace = true, features = ["serde"]} +boa_parser.workspace = true +rustyline = "10.1.1" +rustyline-derive = "0.7.0" +clap = { version = "4.1.1", features = ["derive"] } +serde_json = "1.0.91" +colored = "2.0.0" +regex = "1.7.1" +phf = { version = "0.11.1", features = ["macros"] } + +[features] +default = ["intl"] +intl = ["boa_engine/intl"] + +[target.x86_64-unknown-linux-gnu.dependencies] +jemallocator = "0.5.0" + +[[bin]] +name = "boa" +doc = false +path = "src/main.rs" diff --git a/javascript-engine/external/boa/boa_cli/src/helper.rs b/javascript-engine/external/boa/boa_cli/src/helper.rs new file mode 100644 index 0000000..b867073 --- /dev/null +++ b/javascript-engine/external/boa/boa_cli/src/helper.rs @@ -0,0 +1,172 @@ +use colored::{Color, Colorize}; +use phf::{phf_set, Set}; +use regex::{Captures, Regex}; +use rustyline::{ + error::ReadlineError, + highlight::Highlighter, + validate::{MatchingBracketValidator, ValidationContext, ValidationResult, Validator}, +}; +use rustyline_derive::{Completer, Helper, Hinter}; +use std::borrow::Cow; + +const STRING_COLOR: Color = Color::Green; +const KEYWORD_COLOR: Color = Color::Yellow; +const PROPERTY_COLOR: Color = Color::Magenta; +const OPERATOR_COLOR: Color = Color::TrueColor { + r: 214, + g: 95, + b: 26, +}; +const UNDEFINED_COLOR: Color = Color::TrueColor { + r: 100, + g: 100, + b: 100, +}; +const NUMBER_COLOR: Color = Color::TrueColor { + r: 26, + g: 214, + b: 175, +}; +const IDENTIFIER_COLOR: Color = Color::TrueColor { + r: 26, + g: 160, + b: 214, +}; + +#[allow(clippy::upper_case_acronyms)] +#[derive(Completer, Helper, Hinter)] +pub(crate) struct RLHelper { + highlighter: LineHighlighter, + validator: MatchingBracketValidator, +} + +impl RLHelper { + pub(crate) fn new() -> Self { + Self { + highlighter: LineHighlighter, + validator: MatchingBracketValidator::new(), + } + } +} + +impl Validator for RLHelper { + fn validate( + &self, + context: &mut ValidationContext<'_>, + ) -> Result { + self.validator.validate(context) + } + + fn validate_while_typing(&self) -> bool { + self.validator.validate_while_typing() + } +} + +impl Highlighter for RLHelper { + fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { + hint.into() + } + + fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> { + self.highlighter.highlight(line, pos) + } + + fn highlight_candidate<'c>( + &self, + candidate: &'c str, + _completion: rustyline::CompletionType, + ) -> Cow<'c, str> { + self.highlighter.highlight(candidate, 0) + } + + fn highlight_char(&self, line: &str, _: usize) -> bool { + !line.is_empty() + } +} + +static KEYWORDS: Set<&'static str> = phf_set! { + "break", + "case", + "catch", + "class", + "const", + "continue", + "default", + "delete", + "do", + "else", + "export", + "extends", + "finally", + "for", + "function", + "if", + "import", + "instanceof", + "new", + "return", + "super", + "switch", + "this", + "throw", + "try", + "typeof", + "var", + "void", + "while", + "with", + "yield", + "await", + "enum", + "let", +}; + +struct LineHighlighter; + +impl Highlighter for LineHighlighter { + fn highlight<'l>(&self, line: &'l str, _: usize) -> Cow<'l, str> { + let mut coloured = line.to_string(); + + let reg = Regex::new( + r#"(?x) + (?P\b[$_\p{ID_Start}][$_\p{ID_Continue}\u{200C}\u{200D}]*\b) | + (?P"([^"\\]|\\.)*") | + (?P'([^'\\]|\\.)*') | + (?P`([^`\\]|\\.)*`) | + (?P[+\-/*%~^!&|=<>;:]) | + (?P0[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?)"#, + ) + .expect("could not compile regular expression"); + + coloured = reg + .replace_all(&coloured, |caps: &Captures<'_>| { + if let Some(cap) = caps.name("identifier") { + match cap.as_str() { + "true" | "false" | "null" | "Infinity" | "globalThis" => { + cap.as_str().color(PROPERTY_COLOR).to_string() + } + "undefined" => cap.as_str().color(UNDEFINED_COLOR).to_string(), + identifier if KEYWORDS.contains(identifier) => { + cap.as_str().color(KEYWORD_COLOR).bold().to_string() + } + _ => cap.as_str().color(IDENTIFIER_COLOR).to_string(), + } + } else if let Some(cap) = caps.name("string_double_quote") { + cap.as_str().color(STRING_COLOR).to_string() + } else if let Some(cap) = caps.name("string_single_quote") { + cap.as_str().color(STRING_COLOR).to_string() + } else if let Some(cap) = caps.name("template_literal") { + cap.as_str().color(STRING_COLOR).to_string() + } else if let Some(cap) = caps.name("op") { + cap.as_str().color(OPERATOR_COLOR).to_string() + } else if let Some(cap) = caps.name("number") { + cap.as_str().color(NUMBER_COLOR).to_string() + } else { + caps[0].to_string() + } + }) + .to_string(); + + coloured.into() + } +} diff --git a/javascript-engine/external/boa/boa_cli/src/main.rs b/javascript-engine/external/boa/boa_cli/src/main.rs new file mode 100644 index 0000000..eb7c25e --- /dev/null +++ b/javascript-engine/external/boa/boa_cli/src/main.rs @@ -0,0 +1,387 @@ +//! A ECMAScript REPL implementation based on boa_engine. + +#![doc( + html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg" +)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![warn(missing_docs, clippy::dbg_macro)] +#![deny( + // rustc lint groups https://doc.rust-lang.org/rustc/lints/groups.html + warnings, + future_incompatible, + let_underscore, + nonstandard_style, + rust_2018_compatibility, + rust_2018_idioms, + rust_2021_compatibility, + unused, + + // rustc allowed-by-default lints https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html + macro_use_extern_crate, + meta_variable_misuse, + missing_abi, + missing_copy_implementations, + missing_debug_implementations, + non_ascii_idents, + noop_method_call, + single_use_lifetimes, + trivial_casts, + trivial_numeric_casts, + unreachable_pub, + unsafe_op_in_unsafe_fn, + unused_crate_dependencies, + unused_import_braces, + unused_lifetimes, + unused_qualifications, + unused_tuple_struct_fields, + variant_size_differences, + + // rustdoc lints https://doc.rust-lang.org/rustdoc/lints.html + rustdoc::broken_intra_doc_links, + rustdoc::private_intra_doc_links, + rustdoc::missing_crate_level_docs, + rustdoc::private_doc_tests, + rustdoc::invalid_codeblock_attributes, + rustdoc::invalid_rust_codeblocks, + rustdoc::bare_urls, + + // clippy categories https://doc.rust-lang.org/clippy/ + clippy::all, + clippy::correctness, + clippy::suspicious, + clippy::style, + clippy::complexity, + clippy::perf, + clippy::pedantic, + clippy::nursery, +)] +#![allow(clippy::option_if_let_else, clippy::redundant_pub_crate)] + +mod helper; + +use boa_ast::StatementList; +use boa_engine::{ + context::ContextBuilder, + job::{JobQueue, NativeJob}, + vm::flowgraph::{Direction, Graph}, + Context, JsResult, +}; +use clap::{Parser, ValueEnum, ValueHint}; +use colored::{Color, Colorize}; +use rustyline::{config::Config, error::ReadlineError, EditMode, Editor}; +use std::{cell::RefCell, collections::VecDeque, fs::read, fs::OpenOptions, io, path::PathBuf}; + +#[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))] +#[cfg_attr( + all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"), + global_allocator +)] +static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; + +/// CLI configuration for Boa. +static CLI_HISTORY: &str = ".boa_history"; + +const READLINE_COLOR: Color = Color::Cyan; + +// Added #[allow(clippy::option_option)] because to StructOpt an Option> +// is an optional argument that optionally takes a value ([--opt=[val]]). +// https://docs.rs/structopt/0.3.11/structopt/#type-magic +#[derive(Debug, Parser)] +#[command(author, version, about, name = "boa")] +struct Opt { + /// The JavaScript file(s) to be evaluated. + #[arg(name = "FILE", value_hint = ValueHint::FilePath)] + files: Vec, + + /// Dump the AST to stdout with the given format. + #[arg( + long, + short = 'a', + value_name = "FORMAT", + ignore_case = true, + value_enum, + conflicts_with = "graph" + )] + #[allow(clippy::option_option)] + dump_ast: Option>, + + /// Dump the AST to stdout with the given format. + #[arg(long, short, conflicts_with = "graph")] + trace: bool, + + /// Use vi mode in the REPL + #[arg(long = "vi")] + vi_mode: bool, + + /// Generate instruction flowgraph. Default is Graphviz. + #[arg( + long, + value_name = "FORMAT", + ignore_case = true, + value_enum, + group = "graph" + )] + #[allow(clippy::option_option)] + flowgraph: Option>, + + /// Specifies the direction of the flowgraph. Default is TopToBottom. + #[arg( + long, + value_name = "FORMAT", + ignore_case = true, + value_enum, + requires = "graph" + )] + flowgraph_direction: Option, +} + +impl Opt { + /// Returns whether a dump flag has been used. + const fn has_dump_flag(&self) -> bool { + self.dump_ast.is_some() + } +} + +#[derive(Debug, Clone, ValueEnum)] +enum DumpFormat { + /// The different types of format available for dumping. + // NOTE: This can easily support other formats just by + // adding a field to this enum and adding the necessary + // implementation. Example: Toml, Html, etc. + // + // NOTE: The fields of this enum are not doc comments because + // arg_enum! macro does not support it. + + // This is the default format that you get from std::fmt::Debug. + Debug, + + // This is a minified json format. + Json, + + // This is a pretty printed json format. + JsonPretty, +} + +/// Represents the format of the instruction flowgraph. +#[derive(Debug, Clone, Copy, ValueEnum)] +enum FlowgraphFormat { + /// Generates in [graphviz][graphviz] format. + /// + /// [graphviz]: https://graphviz.org/ + Graphviz, + /// Generates in [mermaid][mermaid] format. + /// + /// [mermaid]: https://mermaid-js.github.io/mermaid/#/ + Mermaid, +} + +/// Represents the direction of the instruction flowgraph. +#[derive(Debug, Clone, Copy, ValueEnum)] +enum FlowgraphDirection { + TopToBottom, + BottomToTop, + LeftToRight, + RightToLeft, +} + +/// Parses the the token stream into an AST and returns it. +/// +/// Returns a error of type String with a message, +/// if the token stream has a parsing error. +fn parse_tokens(src: S, context: &mut Context<'_>) -> Result +where + S: AsRef<[u8]>, +{ + let src_bytes = src.as_ref(); + boa_parser::Parser::new(src_bytes) + .parse_all(context.interner_mut()) + .map_err(|e| format!("ParsingError: {e}")) +} + +/// Dumps the AST to stdout with format controlled by the given arguments. +/// +/// Returns a error of type String with a error message, +/// if the source has a syntax or parsing error. +fn dump(src: S, args: &Opt, context: &mut Context<'_>) -> Result<(), String> +where + S: AsRef<[u8]>, +{ + if let Some(ref arg) = args.dump_ast { + let ast = parse_tokens(src, context)?; + + match arg { + Some(DumpFormat::Json) => println!( + "{}", + serde_json::to_string(&ast).expect("could not convert AST to a JSON string") + ), + Some(DumpFormat::JsonPretty) => println!( + "{}", + serde_json::to_string_pretty(&ast) + .expect("could not convert AST to a pretty JSON string") + ), + Some(DumpFormat::Debug) | None => println!("{ast:#?}"), + } + } + + Ok(()) +} + +fn generate_flowgraph( + context: &mut Context<'_>, + src: &[u8], + format: FlowgraphFormat, + direction: Option, +) -> JsResult { + let ast = context.parse(src)?; + let code = context.compile(&ast)?; + + let direction = match direction { + Some(FlowgraphDirection::TopToBottom) | None => Direction::TopToBottom, + Some(FlowgraphDirection::BottomToTop) => Direction::BottomToTop, + Some(FlowgraphDirection::LeftToRight) => Direction::LeftToRight, + Some(FlowgraphDirection::RightToLeft) => Direction::RightToLeft, + }; + + let mut graph = Graph::new(direction); + code.to_graph(context.interner(), graph.subgraph(String::default())); + let result = match format { + FlowgraphFormat::Graphviz => graph.to_graphviz_format(), + FlowgraphFormat::Mermaid => graph.to_mermaid_format(), + }; + Ok(result) +} + +fn main() -> Result<(), io::Error> { + let args = Opt::parse(); + + let queue = Jobs::default(); + let mut context = ContextBuilder::new().job_queue(&queue).build(); + + // Trace Output + context.set_trace(args.trace); + + for file in &args.files { + let buffer = read(file)?; + + if args.has_dump_flag() { + if let Err(e) = dump(&buffer, &args, &mut context) { + eprintln!("{e}"); + } + } else if let Some(flowgraph) = args.flowgraph { + match generate_flowgraph( + &mut context, + &buffer, + flowgraph.unwrap_or(FlowgraphFormat::Graphviz), + args.flowgraph_direction, + ) { + Ok(v) => println!("{v}"), + Err(v) => eprintln!("Uncaught {v}"), + } + } else { + match context.eval(&buffer) { + Ok(v) => println!("{}", v.display()), + Err(v) => eprintln!("Uncaught {v}"), + } + context.run_jobs(); + } + } + + if args.files.is_empty() { + let config = Config::builder() + .keyseq_timeout(1) + .edit_mode(if args.vi_mode { + EditMode::Vi + } else { + EditMode::Emacs + }) + .build(); + + let mut editor = + Editor::with_config(config).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + // Check if the history file exists. If it does, create it. + OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(CLI_HISTORY)?; + editor.load_history(CLI_HISTORY).map_err(|err| match err { + ReadlineError::Io(e) => e, + e => io::Error::new(io::ErrorKind::Other, e), + })?; + editor.set_helper(Some(helper::RLHelper::new())); + + let readline = ">> ".color(READLINE_COLOR).bold().to_string(); + + loop { + match editor.readline(&readline) { + Ok(line) if line == ".exit" => break, + Err(ReadlineError::Interrupted | ReadlineError::Eof) => break, + + Ok(line) => { + editor.add_history_entry(&line); + + if args.has_dump_flag() { + if let Err(e) = dump(&line, &args, &mut context) { + eprintln!("{e}"); + } + } else if let Some(flowgraph) = args.flowgraph { + match generate_flowgraph( + &mut context, + line.trim_end().as_bytes(), + flowgraph.unwrap_or(FlowgraphFormat::Graphviz), + args.flowgraph_direction, + ) { + Ok(v) => println!("{v}"), + Err(v) => eprintln!("Uncaught {v}"), + } + } else { + match context.eval(line.trim_end()) { + Ok(v) => { + println!("{}", v.display()); + } + Err(v) => { + eprintln!("{}: {}", "Uncaught".red(), v.to_string().red()); + } + } + context.run_jobs(); + } + } + + Err(err) => { + eprintln!("Unknown error: {err:?}"); + break; + } + } + } + + editor + .save_history(CLI_HISTORY) + .expect("could not save CLI history"); + } + + Ok(()) +} + +#[derive(Default)] +struct Jobs(RefCell>); + +impl JobQueue for Jobs { + fn enqueue_promise_job(&self, job: NativeJob, _: &mut Context<'_>) { + self.0.borrow_mut().push_front(job); + } + + fn run_jobs(&self, context: &mut Context<'_>) { + loop { + let jobs = std::mem::take(&mut *self.0.borrow_mut()); + if jobs.is_empty() { + return; + } + for job in jobs { + if let Err(e) = job.call(context) { + eprintln!("Uncaught {e}"); + } + } + } + } +} diff --git a/javascript-engine/external/boa/boa_engine/Cargo.toml b/javascript-engine/external/boa/boa_engine/Cargo.toml new file mode 100644 index 0000000..158a1f8 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/Cargo.toml @@ -0,0 +1,95 @@ +[package] +name = "boa_engine" +keywords = ["javascript", "js", "compiler", "lexer", "parser"] +categories = ["parser-implementations", "compilers"] +readme = "../README.md" +description.workspace = true +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[features] +profiler = ["boa_profiler/profiler"] +deser = ["boa_interner/serde", "boa_ast/serde"] +intl = [ + "dep:boa_icu_provider", + "dep:icu_locid_transform", + "dep:icu_locid", + "dep:icu_datetime", + "dep:icu_plurals", + "dep:icu_provider", + "dep:icu_calendar", + "dep:icu_collator", + "dep:icu_list", + "dep:writeable", + "dep:sys-locale", +] + +fuzz = ["boa_ast/fuzz", "boa_interner/fuzz"] + +# Enable Boa's VM instruction flowgraph generator. +flowgraph = [] + +# Enable Boa's WHATWG console object implementation. +console = [] + +[dependencies] +boa_interner.workspace = true +boa_gc.workspace = true +boa_profiler.workspace = true +boa_macros.workspace = true +boa_ast.workspace = true +boa_parser.workspace = true +serde = { version = "1.0.152", features = ["derive", "rc"] } +serde_json = "1.0.91" +rand = "0.8.5" +num-traits = "0.2.15" +regress = "0.4.1" +rustc-hash = "1.1.0" +num-bigint = { version = "0.4.3", features = ["serde"] } +num-integer = "0.1.45" +bitflags = "1.3.2" +indexmap = "1.9.2" +ryu-js = "0.2.2" +chrono = "0.4.23" +fast-float = "0.2.0" +unicode-normalization = "0.1.22" +once_cell = "1.17.0" +tap = "1.0.1" +sptr = "0.3.2" +static_assertions = "1.1.0" +thiserror = "1.0.38" +dashmap = "5.4.0" +num_enum = "0.5.7" + +# intl deps +boa_icu_provider = { workspace = true, optional = true } +icu_locid_transform = { version = "1.0.0", features = ["serde"], optional = true } +icu_locid = { version = "1.0.0", features = ["serde"], optional = true } +icu_datetime = { version = "1.0.0", features = ["serde", "experimental"], optional = true } +icu_calendar = { version = "1.0.0", optional = true } +icu_collator = { version = "1.0.1", features = ["serde"], optional = true } +icu_plurals = { version = "1.0.0", features = ["serde"], optional = true } +icu_provider = { version = "1.0.1", optional = true } +icu_list = { version = "1.0.0", features = ["serde"], optional = true } +writeable = { version = "0.5.0", optional = true } +sys-locale = { version = "0.2.3", optional = true } + +[dev-dependencies] +criterion = "0.4.0" +float-cmp = "0.9.0" + +[target.x86_64-unknown-linux-gnu.dev-dependencies] +jemallocator = "0.5.0" + +[lib] +crate-type = ["cdylib", "lib"] +name = "boa_engine" +bench = false + +[[bench]] +name = "full" +harness = false diff --git a/javascript-engine/external/boa/boa_engine/benches/README.md b/javascript-engine/external/boa/boa_engine/benches/README.md new file mode 100644 index 0000000..a84190f --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/README.md @@ -0,0 +1,10 @@ +# Boa Benchmarks + +For each js script in the `bench_scripts` folder, we create three benchmarks: + +- Parser => lexing and parsing of the source code +- Compiler => compilation of the parsed statement list into bytecode +- Execution => execution of the bytecode in the vm + +The idea is to check the performance of Boa in different scenarios. +Different parts of Boa are benchmarked separately to make the impact of local changes visible. diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/arithmetic_operations.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/arithmetic_operations.js new file mode 100644 index 0000000..f711585 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/arithmetic_operations.js @@ -0,0 +1 @@ +((2 + 2) ** 3 / 100 - 5 ** 3 * -1000) ** 2 + 100 - 8; diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/array_access.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/array_access.js new file mode 100644 index 0000000..6da5c86 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/array_access.js @@ -0,0 +1,7 @@ +(function () { + let testArr = [1, 2, 3, 4, 5]; + + let res = testArr[2]; + + return res; +})(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/array_create.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/array_create.js new file mode 100644 index 0000000..fddd4ca --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/array_create.js @@ -0,0 +1,8 @@ +(function () { + let testArr = []; + for (let a = 0; a <= 500; a++) { + testArr[a] = "p" + a; + } + + return testArr; +})(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/array_pop.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/array_pop.js new file mode 100644 index 0000000..7613cfc --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/array_pop.js @@ -0,0 +1,22 @@ +(function () { + let testArray = [ + 83, 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, 32, 45, 93, 17, 28, 83, + 62, 99, 36, 28, 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, 32, 45, 93, + 17, 28, 83, 62, 99, 36, 28, 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, + 32, 45, 93, 17, 28, 83, 62, 99, 36, 28, 93, 27, 29, 2828, 234, 23, 56, 32, + 56, 67, 77, 32, 45, 93, 17, 28, 83, 62, 99, 36, 28, 93, 27, 29, 2828, 234, + 23, 56, 32, 56, 67, 77, 32, 45, 93, 17, 28, 83, 62, 99, 36, 28, 93, 27, 29, + 2828, 234, 23, 56, 32, 56, 67, 77, 32, 45, 93, 17, 28, 83, 62, 99, 36, 28, + 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, 32, 45, 93, 17, 28, 83, 62, + 99, 36, 28, 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, 32, 45, 93, 17, + 28, 83, 62, 99, 36, 28, 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, 32, + 45, 93, 17, 28, 83, 62, 99, 36, 28, 93, 27, 29, 2828, 234, 23, 56, 32, 56, + 67, 77, 32, 45, 93, 17, 28, 83, 62, 99, 36, 28, + ]; + + while (testArray.length > 0) { + testArray.pop(); + } + + return testArray; +})(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/boolean_object_access.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/boolean_object_access.js new file mode 100644 index 0000000..1451300 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/boolean_object_access.js @@ -0,0 +1,7 @@ +new Boolean( + !new Boolean( + new Boolean( + !new Boolean(false).valueOf() && new Boolean(true).valueOf() + ).valueOf() + ).valueOf() +).valueOf(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/clean_js.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/clean_js.js new file mode 100644 index 0000000..848ee4d --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/clean_js.js @@ -0,0 +1,15 @@ +!function () { + var M = new Array(); + for (i = 0; i < 100; i++) { + M.push(Math.floor(Math.random() * 100)); + } + var test = []; + for (i = 0; i < 100; i++) { + if (M[i] > 50) { + test.push(M[i]); + } + } + test.forEach(elem => { + 0 + }); +}(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/fibonacci.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/fibonacci.js new file mode 100644 index 0000000..237f59b --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/fibonacci.js @@ -0,0 +1,10 @@ +(function () { + let num = 12; + + function fib(n) { + if (n <= 1) return 1; + return fib(n - 1) + fib(n - 2); + } + + return fib(num); +})(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/for_loop.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/for_loop.js new file mode 100644 index 0000000..3573e8d --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/for_loop.js @@ -0,0 +1,10 @@ +(function () { + let b = "hello"; + for (let a = 10; a < 100; a += 5) { + if (a < 50) { + b += "world"; + } + } + + return b; +})(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/mini_js.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/mini_js.js new file mode 100644 index 0000000..e7a8279 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/mini_js.js @@ -0,0 +1 @@ +!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})}(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/number_object_access.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/number_object_access.js new file mode 100644 index 0000000..5af0f6b --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/number_object_access.js @@ -0,0 +1,5 @@ +new Number( + new Number( + new Number(new Number(100).valueOf() - 10.5).valueOf() + 100 + ).valueOf() * 1.6 +); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/object_creation.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/object_creation.js new file mode 100644 index 0000000..ecaae42 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/object_creation.js @@ -0,0 +1,8 @@ +(function () { + let test = { + my_prop: "hello", + another: 65, + }; + + return test; +})(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/object_prop_access_const.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/object_prop_access_const.js new file mode 100644 index 0000000..5be57f3 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/object_prop_access_const.js @@ -0,0 +1,8 @@ +(function () { + let test = { + my_prop: "hello", + another: 65, + }; + + return test.my_prop; +})(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/object_prop_access_dyn.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/object_prop_access_dyn.js new file mode 100644 index 0000000..9d62575 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/object_prop_access_dyn.js @@ -0,0 +1,8 @@ +(function () { + let test = { + my_prop: "hello", + another: 65, + }; + + return test["my" + "_prop"]; +})(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/regexp.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/regexp.js new file mode 100644 index 0000000..ce8de85 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/regexp.js @@ -0,0 +1,5 @@ +(function () { + let regExp = new RegExp("hello", "i"); + + return regExp.test("Hello World"); +})(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/regexp_creation.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/regexp_creation.js new file mode 100644 index 0000000..9b06e77 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/regexp_creation.js @@ -0,0 +1,5 @@ +(function () { + let regExp = new RegExp("hello", "i"); + + return regExp; +})(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/regexp_literal.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/regexp_literal.js new file mode 100644 index 0000000..40e09be --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/regexp_literal.js @@ -0,0 +1,5 @@ +(function () { + let regExp = /hello/i; + + return regExp.test("Hello World"); +})(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/regexp_literal_creation.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/regexp_literal_creation.js new file mode 100644 index 0000000..aa0400b --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/regexp_literal_creation.js @@ -0,0 +1,5 @@ +(function () { + let regExp = /hello/i; + + return regExp; +})(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/string_compare.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/string_compare.js new file mode 100644 index 0000000..f7f747c --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/string_compare.js @@ -0,0 +1,9 @@ +(function () { + var a = "hello"; + var b = "world"; + + var c = a == b; + + var d = b; + var e = d == b; +})(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/string_concat.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/string_concat.js new file mode 100644 index 0000000..26ade2f --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/string_concat.js @@ -0,0 +1,6 @@ +(function () { + var a = "hello"; + var b = "world"; + + var c = a + b; +})(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/string_copy.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/string_copy.js new file mode 100644 index 0000000..5b3c5e1 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/string_copy.js @@ -0,0 +1,4 @@ +(function () { + var a = "hello"; + var b = a; +})(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/string_object_access.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/string_object_access.js new file mode 100644 index 0000000..45f2ec9 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/string_object_access.js @@ -0,0 +1,7 @@ +new String( + new String( + new String( + new String("Hello").valueOf() + new String(", world").valueOf() + ).valueOf() + "!" + ).valueOf() +).valueOf(); diff --git a/javascript-engine/external/boa/boa_engine/benches/bench_scripts/symbol_creation.js b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/symbol_creation.js new file mode 100644 index 0000000..65a5248 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/bench_scripts/symbol_creation.js @@ -0,0 +1,3 @@ +(function () { + return Symbol(); +})(); diff --git a/javascript-engine/external/boa/boa_engine/benches/full.rs b/javascript-engine/external/boa/boa_engine/benches/full.rs new file mode 100644 index 0000000..043c215 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/benches/full.rs @@ -0,0 +1,95 @@ +//! Benchmarks of the whole execution engine in Boa. + +use boa_engine::{realm::Realm, Context}; +use criterion::{criterion_group, criterion_main, Criterion}; +use std::hint::black_box; + +#[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))] +#[cfg_attr( + all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"), + global_allocator +)] +static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; + +fn create_realm(c: &mut Criterion) { + c.bench_function("Create Realm", move |b| b.iter(|| Realm::create(None))); +} + +macro_rules! full_benchmarks { + ($({$id:literal, $name:ident}),*) => { + fn bench_parser(c: &mut Criterion) { + $( + { + static CODE: &str = include_str!(concat!("bench_scripts/", stringify!($name), ".js")); + let mut context = Context::default(); + c.bench_function(concat!($id, " (Parser)"), move |b| { + b.iter(|| context.parse(black_box(CODE))) + }); + } + )* + } + fn bench_compile(c: &mut Criterion) { + $( + { + static CODE: &str = include_str!(concat!("bench_scripts/", stringify!($name), ".js")); + let mut context = Context::default(); + let statement_list = context.parse(CODE).expect("parsing failed"); + c.bench_function(concat!($id, " (Compiler)"), move |b| { + b.iter(|| { + context.compile(black_box(&statement_list)) + }) + }); + } + )* + } + fn bench_execution(c: &mut Criterion) { + $( + { + static CODE: &str = include_str!(concat!("bench_scripts/", stringify!($name), ".js")); + let mut context = Context::default(); + let statement_list = context.parse(CODE).expect("parsing failed"); + let code_block = context.compile(&statement_list).unwrap(); + c.bench_function(concat!($id, " (Execution)"), move |b| { + b.iter(|| { + context.execute(black_box(code_block.clone())).unwrap() + }) + }); + } + )* + } + }; +} + +full_benchmarks!( + {"Symbols", symbol_creation}, + {"For loop", for_loop}, + {"Fibonacci", fibonacci}, + {"Object Creation", object_creation}, + {"Static Object Property Access", object_prop_access_const}, + {"Dynamic Object Property Access", object_prop_access_dyn}, + {"RegExp Literal Creation", regexp_literal_creation}, + {"RegExp Creation", regexp_creation}, + {"RegExp Literal", regexp_literal}, + {"RegExp", regexp}, + {"Array access", array_access}, + {"Array creation", array_create}, + {"Array pop", array_pop}, + {"String concatenation", string_concat}, + {"String comparison", string_compare}, + {"String copy", string_copy}, + {"Number Object Access", number_object_access}, + {"Boolean Object Access", boolean_object_access}, + {"String Object Access", string_object_access}, + {"Arithmetic operations", arithmetic_operations}, + {"Clean js", clean_js}, + {"Mini js", mini_js} +); + +criterion_group!( + benches, + create_realm, + bench_parser, + bench_compile, + bench_execution, +); +criterion_main!(benches); diff --git a/javascript-engine/external/boa/boa_engine/src/bigint.rs b/javascript-engine/external/boa/boa_engine/src/bigint.rs new file mode 100644 index 0000000..a4531c3 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/bigint.rs @@ -0,0 +1,465 @@ +//! Boa's implementation of ECMAScript's bigint primitive type. + +use crate::{builtins::Number, error::JsNativeError, JsResult}; +use num_integer::Integer; +use num_traits::{pow::Pow, FromPrimitive, One, ToPrimitive, Zero}; +use std::{ + fmt::{self, Display}, + ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub}, + rc::Rc, +}; + +/// The raw bigint type. +pub type RawBigInt = num_bigint::BigInt; + +#[cfg(feature = "deser")] +use serde::{Deserialize, Serialize}; + +/// JavaScript bigint primitive rust type. +#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct JsBigInt { + inner: Rc, +} + +impl JsBigInt { + /// Create a new [`JsBigInt`]. + #[must_use] + pub fn new>(value: T) -> Self { + value.into() + } + + /// Create a [`JsBigInt`] with value `0`. + #[inline] + #[must_use] + pub fn zero() -> Self { + Self { + inner: Rc::new(RawBigInt::zero()), + } + } + + /// Check if is zero. + #[inline] + #[must_use] + pub fn is_zero(&self) -> bool { + self.inner.is_zero() + } + + /// Create a [`JsBigInt`] with value `1`. + #[inline] + #[must_use] + pub fn one() -> Self { + Self { + inner: Rc::new(RawBigInt::one()), + } + } + + /// Check if is one. + #[inline] + #[must_use] + pub fn is_one(&self) -> bool { + self.inner.is_one() + } + + /// Convert bigint to string with radix. + #[inline] + #[must_use] + pub fn to_string_radix(&self, radix: u32) -> String { + self.inner.to_str_radix(radix) + } + + /// Converts the `BigInt` to a f64 type. + /// + /// Returns `f64::INFINITY` if the `BigInt` is too big. + #[inline] + #[must_use] + pub fn to_f64(&self) -> f64 { + self.inner.to_f64().unwrap_or(f64::INFINITY) + } + + /// Converts a string to a `BigInt` with the specified radix. + #[inline] + #[must_use] + pub fn from_string_radix(buf: &str, radix: u32) -> Option { + Some(Self { + inner: Rc::new(RawBigInt::parse_bytes(buf.as_bytes(), radix)?), + }) + } + + /// This function takes a string and converts it to `BigInt` type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-stringtobigint + #[inline] + #[must_use] + pub fn from_string(mut string: &str) -> Option { + string = string.trim(); + + if string.is_empty() { + return Some(Self::zero()); + } + + let mut radix = 10; + if string.starts_with("0b") || string.starts_with("0B") { + radix = 2; + string = &string[2..]; + } else if string.starts_with("0x") || string.starts_with("0X") { + radix = 16; + string = &string[2..]; + } else if string.starts_with("0o") || string.starts_with("0O") { + radix = 8; + string = &string[2..]; + } + + Self::from_string_radix(string, radix) + } + + /// Checks for `SameValueZero` equality. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-equal + #[inline] + #[must_use] + pub fn same_value_zero(x: &Self, y: &Self) -> bool { + // Return BigInt::equal(x, y) + Self::equal(x, y) + } + + /// Checks for `SameValue` equality. + /// + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-sameValue + #[inline] + #[must_use] + pub fn same_value(x: &Self, y: &Self) -> bool { + // Return BigInt::equal(x, y) + Self::equal(x, y) + } + + /// Checks for mathematical equality. + /// + /// The abstract operation `BigInt::equal` takes arguments x (a `BigInt`) and y (a `BigInt`). + /// It returns `true` if x and y have the same mathematical integer value and false otherwise. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-sameValueZero + #[inline] + #[must_use] + pub fn equal(x: &Self, y: &Self) -> bool { + x == y + } + + /// Returns `x` to the power `y`. + #[inline] + pub fn pow(x: &Self, y: &Self) -> JsResult { + let y = y + .inner + .to_biguint() + .ok_or_else(|| JsNativeError::range().with_message("BigInt negative exponent"))?; + + let num_bits = (x.inner.bits() as f64 + * y.to_f64().expect("Unable to convert from BigUInt to f64")) + .floor() + + 1f64; + + if num_bits > 1_000_000_000f64 { + return Err(JsNativeError::range() + .with_message("Maximum BigInt size exceeded") + .into()); + } + + Ok(Self::new(x.inner.as_ref().clone().pow(y))) + } + + /// Performs the `>>` operation. + #[inline] + pub fn shift_right(x: &Self, y: &Self) -> JsResult { + match y.inner.to_i32() { + Some(n) if n > 0 => Ok(Self::new(x.inner.as_ref().clone().shr(n as usize))), + Some(n) => Ok(Self::new(x.inner.as_ref().clone().shl(n.unsigned_abs()))), + None => Err(JsNativeError::range() + .with_message("Maximum BigInt size exceeded") + .into()), + } + } + + /// Performs the `<<` operation. + #[inline] + pub fn shift_left(x: &Self, y: &Self) -> JsResult { + match y.inner.to_i32() { + Some(n) if n > 0 => Ok(Self::new(x.inner.as_ref().clone().shl(n as usize))), + Some(n) => Ok(Self::new(x.inner.as_ref().clone().shr(n.unsigned_abs()))), + None => Err(JsNativeError::range() + .with_message("Maximum BigInt size exceeded") + .into()), + } + } + + /// Floored integer modulo. + /// + /// # Examples + /// ``` + /// # use num_integer::Integer; + /// assert_eq!((8).mod_floor(&3), 2); + /// assert_eq!((8).mod_floor(&-3), -1); + /// ``` + #[inline] + #[must_use] + pub fn mod_floor(x: &Self, y: &Self) -> Self { + Self::new(x.inner.mod_floor(&y.inner)) + } + + /// Performs the `+` operation. + #[inline] + #[must_use] + pub fn add(x: &Self, y: &Self) -> Self { + Self::new(x.inner.as_ref().clone().add(y.inner.as_ref())) + } + + /// Performs the `-` operation. + #[inline] + #[must_use] + pub fn sub(x: &Self, y: &Self) -> Self { + Self::new(x.inner.as_ref().clone().sub(y.inner.as_ref())) + } + + /// Performs the `*` operation. + #[inline] + #[must_use] + pub fn mul(x: &Self, y: &Self) -> Self { + Self::new(x.inner.as_ref().clone().mul(y.inner.as_ref())) + } + + /// Performs the `/` operation. + #[inline] + #[must_use] + pub fn div(x: &Self, y: &Self) -> Self { + Self::new(x.inner.as_ref().clone().div(y.inner.as_ref())) + } + + /// Performs the `%` operation. + #[inline] + #[must_use] + pub fn rem(x: &Self, y: &Self) -> Self { + Self::new(x.inner.as_ref().clone().rem(y.inner.as_ref())) + } + + /// Performs the `&` operation. + #[inline] + #[must_use] + pub fn bitand(x: &Self, y: &Self) -> Self { + Self::new(x.inner.as_ref().clone().bitand(y.inner.as_ref())) + } + + /// Performs the `|` operation. + #[inline] + #[must_use] + pub fn bitor(x: &Self, y: &Self) -> Self { + Self::new(x.inner.as_ref().clone().bitor(y.inner.as_ref())) + } + + /// Performs the `^` operation. + #[inline] + #[must_use] + pub fn bitxor(x: &Self, y: &Self) -> Self { + Self::new(x.inner.as_ref().clone().bitxor(y.inner.as_ref())) + } + + /// Performs the unary `-` operation. + #[inline] + #[must_use] + pub fn neg(x: &Self) -> Self { + Self::new(x.as_inner().neg()) + } + + /// Performs the unary `!` operation. + #[inline] + #[must_use] + pub fn not(x: &Self) -> Self { + Self::new(!x.as_inner()) + } + + pub(crate) fn as_inner(&self) -> &RawBigInt { + &self.inner + } +} + +impl Display for JsBigInt { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&self.inner, f) + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: RawBigInt) -> Self { + Self { + inner: Rc::new(value), + } + } +} + +impl From> for JsBigInt { + #[inline] + fn from(value: Box) -> Self { + Self { + inner: value.into(), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: i8) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: u8) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: i16) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: u16) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: i32) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: u32) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: i64) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: u64) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: isize) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: usize) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +/// The error indicates that the conversion from [`f64`] to [`JsBigInt`] failed. +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct TryFromF64Error; + +impl Display for TryFromF64Error { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Could not convert f64 value to a BigInt type") + } +} + +impl TryFrom for JsBigInt { + type Error = TryFromF64Error; + + #[inline] + fn try_from(n: f64) -> Result { + // If the truncated version of the number is not the + // same as the non-truncated version then the floating-point + // number conains a fractional part. + if !Number::equal(n.trunc(), n) { + return Err(TryFromF64Error); + } + RawBigInt::from_f64(n).map_or(Err(TryFromF64Error), |bigint| Ok(Self::new(bigint))) + } +} + +impl PartialEq for JsBigInt { + #[inline] + fn eq(&self, other: &i32) -> bool { + self.inner.as_ref() == &RawBigInt::from(*other) + } +} + +impl PartialEq for i32 { + #[inline] + fn eq(&self, other: &JsBigInt) -> bool { + &RawBigInt::from(*self) == other.inner.as_ref() + } +} + +impl PartialEq for JsBigInt { + #[inline] + fn eq(&self, other: &f64) -> bool { + other.fract().is_zero() + && RawBigInt::from_f64(*other).map_or(false, |bigint| self.inner.as_ref() == &bigint) + } +} + +impl PartialEq for f64 { + #[inline] + fn eq(&self, other: &JsBigInt) -> bool { + self.fract().is_zero() + && RawBigInt::from_f64(*self).map_or(false, |bigint| other.inner.as_ref() == &bigint) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/array/array_iterator.rs b/javascript-engine/external/boa/boa_engine/src/builtins/array/array_iterator.rs new file mode 100644 index 0000000..f28045a --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/array/array_iterator.rs @@ -0,0 +1,160 @@ +//! This module implements the `ArrayIterator` object. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-array-iterator-objects + +use crate::{ + builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Array, JsValue}, + error::JsNativeError, + object::{JsObject, ObjectData}, + property::{PropertyDescriptor, PropertyNameKind}, + symbol::JsSymbol, + Context, JsResult, +}; +use boa_gc::{Finalize, Trace}; +use boa_profiler::Profiler; + +/// The Array Iterator object represents an iteration over an array. It implements the iterator protocol. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-array-iterator-objects +#[derive(Debug, Clone, Finalize, Trace)] +pub struct ArrayIterator { + array: JsObject, + next_index: u64, + #[unsafe_ignore_trace] + kind: PropertyNameKind, + done: bool, +} + +impl ArrayIterator { + pub(crate) const NAME: &'static str = "ArrayIterator"; + + fn new(array: JsObject, kind: PropertyNameKind) -> Self { + Self { + array, + kind, + next_index: 0, + done: false, + } + } + + /// `CreateArrayIterator( array, kind )` + /// + /// Creates a new iterator over the given array. + /// + /// More information: + /// - [ECMA reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-createarrayiterator + pub(crate) fn create_array_iterator( + array: JsObject, + kind: PropertyNameKind, + context: &Context<'_>, + ) -> JsValue { + let array_iterator = JsObject::from_proto_and_data( + context + .intrinsics() + .objects() + .iterator_prototypes() + .array_iterator(), + ObjectData::array_iterator(Self::new(array, kind)), + ); + array_iterator.into() + } + + /// %ArrayIteratorPrototype%.next( ) + /// + /// Gets the next result in the array. + /// + /// More information: + /// - [ECMA reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-%arrayiteratorprototype%.next + pub(crate) fn next( + this: &JsValue, + _: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let mut array_iterator = this.as_object().map(JsObject::borrow_mut); + let array_iterator = array_iterator + .as_mut() + .and_then(|obj| obj.as_array_iterator_mut()) + .ok_or_else(|| JsNativeError::typ().with_message("`this` is not an ArrayIterator"))?; + let index = array_iterator.next_index; + if array_iterator.done { + return Ok(create_iter_result_object( + JsValue::undefined(), + true, + context, + )); + } + + let len = if let Some(f) = array_iterator.array.borrow().as_typed_array() { + if f.is_detached() { + return Err(JsNativeError::typ() + .with_message( + "Cannot get value from typed array that has a detached array buffer", + ) + .into()); + } + + f.array_length() + } else { + array_iterator.array.length_of_array_like(context)? + }; + + if index >= len { + array_iterator.done = true; + return Ok(create_iter_result_object( + JsValue::undefined(), + true, + context, + )); + } + array_iterator.next_index = index + 1; + match array_iterator.kind { + PropertyNameKind::Key => Ok(create_iter_result_object(index.into(), false, context)), + PropertyNameKind::Value => { + let element_value = array_iterator.array.get(index, context)?; + Ok(create_iter_result_object(element_value, false, context)) + } + PropertyNameKind::KeyAndValue => { + let element_value = array_iterator.array.get(index, context)?; + let result = Array::create_array_from_list([index.into(), element_value], context); + Ok(create_iter_result_object(result.into(), false, context)) + } + } + } + + /// Create the `%ArrayIteratorPrototype%` object + /// + /// More information: + /// - [ECMA reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-object + pub(crate) fn create_prototype( + iterator_prototype: JsObject, + context: &mut Context<'_>, + ) -> JsObject { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + // Create prototype + let array_iterator = + JsObject::from_proto_and_data(iterator_prototype, ObjectData::ordinary()); + make_builtin_fn(Self::next, "next", &array_iterator, 0, context); + + let to_string_tag = JsSymbol::to_string_tag(); + let to_string_tag_property = PropertyDescriptor::builder() + .value("Array Iterator") + .writable(false) + .enumerable(false) + .configurable(true); + array_iterator.insert(to_string_tag, to_string_tag_property); + array_iterator + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/array/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/array/mod.rs new file mode 100644 index 0000000..6cd68b4 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/array/mod.rs @@ -0,0 +1,3029 @@ +//! Boa's implementation of ECMAScript's global `Array` object. +//! +//! The ECMAScript `Array` class is a global object that is used in the construction of arrays; which are high-level, list-like objects. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! - [MDN documentation][mdn] +//! +//! [spec]: https://tc39.es/ecma262/#sec-array-objects +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array + +pub mod array_iterator; +#[cfg(test)] +mod tests; + +use boa_macros::utf16; +use boa_profiler::Profiler; +use tap::{Conv, Pipe}; + +use super::JsArgs; +use crate::{ + builtins::array::array_iterator::ArrayIterator, + builtins::iterable::{if_abrupt_close_iterator, IteratorHint}, + builtins::BuiltIn, + builtins::Number, + context::intrinsics::StandardConstructors, + error::JsNativeError, + js_string, + native_function::NativeFunction, + object::{ + internal_methods::get_prototype_from_constructor, ConstructorBuilder, + FunctionObjectBuilder, JsFunction, JsObject, ObjectData, + }, + property::{Attribute, PropertyDescriptor, PropertyNameKind}, + symbol::JsSymbol, + value::{IntegerOrInfinity, JsValue}, + Context, JsResult, +}; +use std::cmp::{max, min, Ordering}; + +/// JavaScript `Array` built-in implementation. +#[derive(Debug, Clone, Copy)] +pub(crate) struct Array; + +impl BuiltIn for Array { + const NAME: &'static str = "Array"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let symbol_iterator = JsSymbol::iterator(); + let symbol_unscopables = JsSymbol::unscopables(); + + let get_species = + FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(Self::get_species)) + .name("get [Symbol.species]") + .constructor(false) + .build(); + + let values_function = context.intrinsics().objects().array_prototype_values(); + let unscopables_object = Self::unscopables_intrinsic(context); + + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context.intrinsics().constructors().array().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .static_accessor( + JsSymbol::species(), + Some(get_species), + None, + Attribute::CONFIGURABLE, + ) + .property( + "length", + 0, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ) + .property( + "values", + values_function.clone(), + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ) + .property( + symbol_iterator, + values_function, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ) + .property( + symbol_unscopables, + unscopables_object, + Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ) + .method(Self::at, "at", 1) + .method(Self::concat, "concat", 1) + .method(Self::push, "push", 1) + .method(Self::index_of, "indexOf", 1) + .method(Self::last_index_of, "lastIndexOf", 1) + .method(Self::includes_value, "includes", 1) + .method(Self::map, "map", 1) + .method(Self::fill, "fill", 1) + .method(Self::for_each, "forEach", 1) + .method(Self::filter, "filter", 1) + .method(Self::pop, "pop", 0) + .method(Self::join, "join", 1) + .method(Self::to_string, "toString", 0) + .method(Self::reverse, "reverse", 0) + .method(Self::shift, "shift", 0) + .method(Self::unshift, "unshift", 1) + .method(Self::every, "every", 1) + .method(Self::find, "find", 1) + .method(Self::find_index, "findIndex", 1) + .method(Self::find_last, "findLast", 1) + .method(Self::find_last_index, "findLastIndex", 1) + .method(Self::flat, "flat", 0) + .method(Self::flat_map, "flatMap", 1) + .method(Self::slice, "slice", 2) + .method(Self::some, "some", 1) + .method(Self::sort, "sort", 1) + .method(Self::splice, "splice", 2) + .method(Self::to_locale_string, "toLocaleString", 0) + .method(Self::reduce, "reduce", 1) + .method(Self::reduce_right, "reduceRight", 1) + .method(Self::keys, "keys", 0) + .method(Self::entries, "entries", 0) + .method(Self::copy_within, "copyWithin", 2) + // Static Methods + .static_method(Self::from, "from", 1) + .static_method(Self::is_array, "isArray", 1) + .static_method(Self::of, "of", 0) + .build() + .conv::() + .pipe(Some) + } +} + +impl Array { + const LENGTH: usize = 1; + + fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. + // 2. Let proto be ? GetPrototypeFromConstructor(newTarget, "%Array.prototype%"). + let prototype = + get_prototype_from_constructor(new_target, StandardConstructors::array, context)?; + + // 3. Let numberOfArgs be the number of elements in values. + let number_of_args = args.len(); + + // 4. If numberOfArgs = 0, then + if number_of_args == 0 { + // 4.a. Return ! ArrayCreate(0, proto). + Ok(Self::array_create(0, Some(prototype), context) + .expect("this ArrayCreate call must not fail") + .into()) + // 5. Else if numberOfArgs = 1, then + } else if number_of_args == 1 { + // a. Let len be values[0]. + let len = &args[0]; + // b. Let array be ! ArrayCreate(0, proto). + let array = Self::array_create(0, Some(prototype), context) + .expect("this ArrayCreate call must not fail"); + // c. If Type(len) is not Number, then + #[allow(clippy::if_not_else)] + let int_len = if !len.is_number() { + // i. Perform ! CreateDataPropertyOrThrow(array, "0", len). + array + .create_data_property_or_throw(0, len.clone(), context) + .expect("this CreateDataPropertyOrThrow call must not fail"); + // ii. Let intLen be 1𝔽. + 1 + // d. Else, + } else { + // i. Let intLen be ! ToUint32(len). + let int_len = len + .to_u32(context) + .expect("this ToUint32 call must not fail"); + // ii. If SameValueZero(intLen, len) is false, throw a RangeError exception. + if !JsValue::same_value_zero(&int_len.into(), len) { + return Err(JsNativeError::range() + .with_message("invalid array length") + .into()); + } + int_len + }; + // e. Perform ! Set(array, "length", intLen, true). + array + .set("length", int_len, true, context) + .expect("this Set call must not fail"); + // f. Return array. + Ok(array.into()) + // 6. Else, + } else { + // 6.a. Assert: numberOfArgs ≥ 2. + debug_assert!(number_of_args >= 2); + + // b. Let array be ? ArrayCreate(numberOfArgs, proto). + let array = Self::array_create(number_of_args as u64, Some(prototype), context)?; + // c. Let k be 0. + // d. Repeat, while k < numberOfArgs, + for (i, item) in args.iter().cloned().enumerate() { + // i. Let Pk be ! ToString(𝔽(k)). + // ii. Let itemK be values[k]. + // iii. Perform ! CreateDataPropertyOrThrow(array, Pk, itemK). + array + .create_data_property_or_throw(i, item, context) + .expect("this CreateDataPropertyOrThrow must not fail"); + // iv. Set k to k + 1. + } + // e. Assert: The mathematical value of array's "length" property is numberOfArgs. + // f. Return array. + Ok(array.into()) + } + } + + /// Utility for constructing `Array` objects. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-arraycreate + pub(crate) fn array_create( + length: u64, + prototype: Option, + context: &mut Context<'_>, + ) -> JsResult { + // 1. If length > 2^32 - 1, throw a RangeError exception. + if length > 2u64.pow(32) - 1 { + return Err(JsNativeError::range() + .with_message("array exceeded max size") + .into()); + } + // 7. Return A. + // 2. If proto is not present, set proto to %Array.prototype%. + // 3. Let A be ! MakeBasicObject(« [[Prototype]], [[Extensible]] »). + // 4. Set A.[[Prototype]] to proto. + // 5. Set A.[[DefineOwnProperty]] as specified in 10.4.2.1. + let prototype = + prototype.unwrap_or_else(|| context.intrinsics().constructors().array().prototype()); + let array = JsObject::from_proto_and_data(prototype, ObjectData::array()); + + // 6. Perform ! OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor { [[Value]]: 𝔽(length), [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }). + crate::object::internal_methods::ordinary_define_own_property( + &array, + "length".into(), + PropertyDescriptor::builder() + .value(length) + .writable(true) + .enumerable(false) + .configurable(false) + .build(), + context, + )?; + + Ok(array) + } + + /// Utility for constructing `Array` objects from an iterator of `JsValue`s. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-createarrayfromlist + pub(crate) fn create_array_from_list(elements: I, context: &mut Context<'_>) -> JsObject + where + I: IntoIterator, + { + // 1. Assert: elements is a List whose elements are all ECMAScript language values. + // 2. Let array be ! ArrayCreate(0). + let array = Self::array_create(0, None, context) + .expect("creating an empty array with the default prototype must not fail"); + + // 3. Let n be 0. + // 4. For each element e of elements, do + // a. Perform ! CreateDataPropertyOrThrow(array, ! ToString(𝔽(n)), e). + // b. Set n to n + 1. + // + // NOTE: This deviates from the spec, but it should have the same behaviour. + let elements: Vec<_> = elements.into_iter().collect(); + let length = elements.len(); + array + .borrow_mut() + .properties_mut() + .override_indexed_properties(elements); + array + .set("length", length, true, context) + .expect("Should not fail"); + + // 5. Return array. + array + } + + /// Utility function for concatenating array objects. + /// + /// Returns a Boolean valued property that if `true` indicates that + /// an object should be flattened to its array elements + /// by `Array.prototype.concat`. + fn is_concat_spreadable(o: &JsValue, context: &mut Context<'_>) -> JsResult { + // 1. If Type(O) is not Object, return false. + let Some(o) = o.as_object() else { + return Ok(false); + }; + + // 2. Let spreadable be ? Get(O, @@isConcatSpreadable). + let spreadable = o.get(JsSymbol::is_concat_spreadable(), context)?; + + // 3. If spreadable is not undefined, return ! ToBoolean(spreadable). + if !spreadable.is_undefined() { + return Ok(spreadable.to_boolean()); + } + + // 4. Return ? IsArray(O). + o.is_array_abstract() + } + + /// `get Array [ @@species ]` + /// + /// The `Array [ @@species ]` accessor property returns the Array constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-get-array-@@species + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/@@species + #[allow(clippy::unnecessary_wraps)] + fn get_species(this: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult { + // 1. Return the this value. + Ok(this.clone()) + } + + /// Utility function used to specify the creation of a new Array object using a constructor + /// function that is derived from `original_array`. + /// + /// see: + pub(crate) fn array_species_create( + original_array: &JsObject, + length: u64, + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let isArray be ? IsArray(originalArray). + // 2. If isArray is false, return ? ArrayCreate(length). + if !original_array.is_array_abstract()? { + return Self::array_create(length, None, context); + } + // 3. Let C be ? Get(originalArray, "constructor"). + let c = original_array.get("constructor", context)?; + + // 4. If IsConstructor(C) is true, then + if let Some(c) = c.as_constructor() { + // a. Let thisRealm be the current Realm Record. + // b. Let realmC be ? GetFunctionRealm(C). + // c. If thisRealm and realmC are not the same Realm Record, then + if *c == context.intrinsics().constructors().array().constructor { + // i. If SameValue(C, realmC.[[Intrinsics]].[[%Array%]]) is true, set C to undefined. + // Note: fast path to step 6. + return Self::array_create(length, None, context); + } + } + + // 5. If Type(C) is Object, then + let c = if let Some(c) = c.as_object() { + // 5.a. Set C to ? Get(C, @@species). + let c = c.get(JsSymbol::species(), context)?; + // 5.b. If C is null, set C to undefined. + if c.is_null_or_undefined() { + JsValue::undefined() + } else { + c + } + } else { + c + }; + + // 6. If C is undefined, return ? ArrayCreate(length). + if c.is_undefined() { + return Self::array_create(length, None, context); + } + + if let Some(c) = c.as_constructor() { + // 8. Return ? Construct(C, « 𝔽(length) »). + return c.construct(&[JsValue::new(length)], Some(c), context); + } + + // 7. If IsConstructor(C) is false, throw a TypeError exception. + Err(JsNativeError::typ() + .with_message("Symbol.species must be a constructor") + .into()) + } + + /// `Array.from(arrayLike)` + /// + /// The Array.from() static method creates a new, + /// shallow-copied Array instance from an array-like or iterable object. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.from + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from + pub(crate) fn from( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let items = args.get_or_undefined(0); + let mapfn = args.get_or_undefined(1); + let this_arg = args.get_or_undefined(2); + + // 2. If mapfn is undefined, let mapping be false + // 3. Else, + // a. If IsCallable(mapfn) is false, throw a TypeError exception. + // b. Let mapping be true. + let mapping = match mapfn { + JsValue::Undefined => None, + JsValue::Object(o) if o.is_callable() => Some(o), + _ => { + return Err(JsNativeError::typ() + .with_message(format!("`{}` is not callable", mapfn.type_of())) + .into()) + } + }; + + // 4. Let usingIterator be ? GetMethod(items, @@iterator). + let using_iterator = items.get_method(JsSymbol::iterator(), context)?; + + let Some(using_iterator) = using_iterator else { + // 6. NOTE: items is not an Iterable so assume it is an array-like object. + // 7. Let arrayLike be ! ToObject(items). + let array_like = items + .to_object(context) + .expect("should not fail according to spec"); + + // 8. Let len be ? LengthOfArrayLike(arrayLike). + let len = array_like.length_of_array_like(context)?; + + // 9. If IsConstructor(C) is true, then + // a. Let A be ? Construct(C, « 𝔽(len) »). + // 10. Else, + // a. Let A be ? ArrayCreate(len). + let a = match this.as_constructor() { + Some(constructor) => constructor.construct(&[len.into()], None, context)?, + _ => Self::array_create(len, None, context)?, + }; + + // 11. Let k be 0. + // 12. Repeat, while k < len, + // ... + // f. Set k to k + 1. + for k in 0..len { + // a. Let Pk be ! ToString(𝔽(k)). + // b. Let kValue be ? Get(arrayLike, Pk). + let k_value = array_like.get(k, context)?; + + let mapped_value = if let Some(mapfn) = mapping { + // c. If mapping is true, then + // i. Let mappedValue be ? Call(mapfn, thisArg, « kValue, 𝔽(k) »). + mapfn.call(this_arg, &[k_value, k.into()], context)? + } else { + // d. Else, let mappedValue be kValue. + k_value + }; + + // e. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue). + a.create_data_property_or_throw(k, mapped_value, context)?; + } + + // 13. Perform ? Set(A, "length", 𝔽(len), true). + a.set("length", len, true, context)?; + + // 14. Return A. + return Ok(a.into()); + }; + + // 5. If usingIterator is not undefined, then + + // a. If IsConstructor(C) is true, then + // i. Let A be ? Construct(C). + // b. Else, + // i. Let A be ? ArrayCreate(0en). + let a = match this.as_constructor() { + Some(constructor) => constructor.construct(&[], None, context)?, + _ => Self::array_create(0, None, context)?, + }; + + // c. Let iteratorRecord be ? GetIterator(items, sync, usingIterator). + let iterator_record = + items.get_iterator(context, Some(IteratorHint::Sync), Some(using_iterator))?; + + // d. Let k be 0. + // e. Repeat, + // i. If k ≥ 2^53 - 1 (MAX_SAFE_INTEGER), then + // ... + // x. Set k to k + 1. + for k in 0..9_007_199_254_740_991_u64 { + // iii. Let next be ? IteratorStep(iteratorRecord). + let next = iterator_record.step(context)?; + + // iv. If next is false, then + let Some(next) = next else { + // 1. Perform ? Set(A, "length", 𝔽(k), true). + a.set("length", k, true, context)?; + + // 2. Return A. + return Ok(a.into()); + }; + + // v. Let nextValue be ? IteratorValue(next). + let next_value = next.value(context)?; + + // vi. If mapping is true, then + let mapped_value = if let Some(mapfn) = mapping { + // 1. Let mappedValue be Call(mapfn, thisArg, « nextValue, 𝔽(k) »). + let mapped_value = mapfn.call(this_arg, &[next_value, k.into()], context); + + // 2. IfAbruptCloseIterator(mappedValue, iteratorRecord). + if_abrupt_close_iterator!(mapped_value, iterator_record, context) + } else { + // vii. Else, let mappedValue be nextValue. + next_value + }; + + // viii. Let defineStatus be CreateDataPropertyOrThrow(A, Pk, mappedValue). + let define_status = a.create_data_property_or_throw(k, mapped_value, context); + + // ix. IfAbruptCloseIterator(defineStatus, iteratorRecord). + if_abrupt_close_iterator!(define_status, iterator_record, context); + } + + // NOTE: The loop above has to return before it reaches iteration limit, + // which is why it's safe to have this as the fallback return + // + // 1. Let error be ThrowCompletion(a newly created TypeError object). + let error = Err(JsNativeError::typ() + .with_message("Invalid array length") + .into()); + + // 2. Return ? IteratorClose(iteratorRecord, error). + iterator_record.close(error, context) + } + + /// `Array.isArray( arg )` + /// + /// The isArray function takes one argument arg, and returns the Boolean value true + /// if the argument is an object whose class internal property is "Array"; otherwise it returns false. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.isarray + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray + pub(crate) fn is_array( + _: &JsValue, + args: &[JsValue], + _context: &mut Context<'_>, + ) -> JsResult { + // 1. Return ? IsArray(arg). + args.get_or_undefined(0).is_array().map(Into::into) + } + + /// `Array.of(...items)` + /// + /// The Array.of method creates a new Array instance from a variable number of arguments, + /// regardless of the number or type of arguments. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.of + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of + pub(crate) fn of( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let len be the number of elements in items. + // 2. Let lenNumber be 𝔽(len). + let len = args.len(); + + // 3. Let C be the this value. + // 4. If IsConstructor(C) is true, then + // a. Let A be ? Construct(C, « lenNumber »). + // 5. Else, + // a. Let A be ? ArrayCreate(len). + let a = match this.as_constructor() { + Some(constructor) => constructor.construct(&[len.into()], None, context)?, + _ => Self::array_create(len as u64, None, context)?, + }; + + // 6. Let k be 0. + // 7. Repeat, while k < len, + for (k, value) in args.iter().enumerate() { + // a. Let kValue be items[k]. + // b. Let Pk be ! ToString(𝔽(k)). + // c. Perform ? CreateDataPropertyOrThrow(A, Pk, kValue). + a.create_data_property_or_throw(k, value.clone(), context)?; + // d. Set k to k + 1. + } + + // 8. Perform ? Set(A, "length", lenNumber, true). + a.set("length", len, true, context)?; + + // 9. Return A. + Ok(a.into()) + } + + ///'Array.prototype.at(index)' + /// + /// The at() method takes an integer value and returns the item at that + /// index, allowing for positive and negative integers. Negative integers + /// count back from the last item in the array. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.at + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + pub(crate) fn at( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + //1. let O be ? ToObject(this value) + let obj = this.to_object(context)?; + //2. let len be ? LengthOfArrayLike(O) + let len = obj.length_of_array_like(context)? as i64; + //3. let relativeIndex be ? ToIntegerOrInfinity(index) + let relative_index = args.get_or_undefined(0).to_integer_or_infinity(context)?; + let k = match relative_index { + //4. if relativeIndex >= 0, then let k be relativeIndex + //check if positive and if below length of array + IntegerOrInfinity::Integer(i) if i >= 0 && i < len => i, + //5. Else, let k be len + relativeIndex + //integer should be negative, so abs() and check if less than or equal to length of array + IntegerOrInfinity::Integer(i) if i < 0 && i.abs() <= len => len + i, + //handle most likely impossible case of + //IntegerOrInfinity::NegativeInfinity || IntegerOrInfinity::PositiveInfinity + //by returning undefined + _ => return Ok(JsValue::undefined()), + }; + //6. if k < 0 or k >= len, + //handled by the above match guards + //7. Return ? Get(O, !ToString(𝔽(k))) + obj.get(k, context) + } + + /// `Array.prototype.concat(...arguments)` + /// + /// When the concat method is called with zero or more arguments, it returns an + /// array containing the array elements of the object followed by the array + /// elements of each argument in order. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.concat + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat + pub(crate) fn concat( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let obj = this.to_object(context)?; + // 2. Let A be ? ArraySpeciesCreate(O, 0). + let arr = Self::array_species_create(&obj, 0, context)?; + // 3. Let n be 0. + let mut n = 0; + // 4. Prepend O to items. + // 5. For each element E of items, do + for item in std::iter::once(&JsValue::new(obj)).chain(args.iter()) { + // a. Let spreadable be ? IsConcatSpreadable(E). + let spreadable = Self::is_concat_spreadable(item, context)?; + // b. If spreadable is true, then + if spreadable { + // item is guaranteed to be an object since is_concat_spreadable checks it, + // so we can call `.unwrap()` + let item = item.as_object().expect("guaranteed to be an object"); + // i. Let k be 0. + // ii. Let len be ? LengthOfArrayLike(E). + let len = item.length_of_array_like(context)?; + // iii. If n + len > 2^53 - 1, throw a TypeError exception. + if n + len > Number::MAX_SAFE_INTEGER as u64 { + return Err(JsNativeError::typ() + .with_message( + "length + number of arguments exceeds the max safe integer limit", + ) + .into()); + } + // iv. Repeat, while k < len, + for k in 0..len { + // 1. Let P be ! ToString(𝔽(k)). + // 2. Let exists be ? HasProperty(E, P). + let exists = item.has_property(k, context)?; + // 3. If exists is true, then + if exists { + // a. Let subElement be ? Get(E, P). + let sub_element = item.get(k, context)?; + // b. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), subElement). + arr.create_data_property_or_throw(n, sub_element, context)?; + } + // 4. Set n to n + 1. + n += 1; + // 5. Set k to k + 1. + } + } + // c. Else, + else { + // i. NOTE: E is added as a single item rather than spread. + // ii. If n ≥ 2^53 - 1, throw a TypeError exception. + if n >= Number::MAX_SAFE_INTEGER as u64 { + return Err(JsNativeError::typ() + .with_message("length exceeds the max safe integer limit") + .into()); + } + // iii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), E). + arr.create_data_property_or_throw(n, item.clone(), context)?; + // iv. Set n to n + 1. + n += 1; + } + } + // 6. Perform ? Set(A, "length", 𝔽(n), true). + arr.set("length", n, true, context)?; + + // 7. Return A. + Ok(JsValue::new(arr)) + } + + /// `Array.prototype.push( ...items )` + /// + /// The arguments are appended to the end of the array, in the order in which + /// they appear. The new length of the array is returned as the result of the + /// call. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.push + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push + pub(crate) fn push( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + // 2. Let len be ? LengthOfArrayLike(O). + let mut len = o.length_of_array_like(context)?; + // 3. Let argCount be the number of elements in items. + let arg_count = args.len() as u64; + // 4. If len + argCount > 2^53 - 1, throw a TypeError exception. + if len + arg_count > 2u64.pow(53) - 1 { + return Err(JsNativeError::typ() + .with_message( + "the length + the number of arguments exceed the maximum safe integer limit", + ) + .into()); + } + // 5. For each element E of items, do + for element in args.iter().cloned() { + // a. Perform ? Set(O, ! ToString(𝔽(len)), E, true). + o.set(len, element, true, context)?; + // b. Set len to len + 1. + len += 1; + } + // 6. Perform ? Set(O, "length", 𝔽(len), true). + o.set("length", len, true, context)?; + // 7. Return 𝔽(len). + Ok(len.into()) + } + + /// `Array.prototype.pop()` + /// + /// The last element of the array is removed from the array and returned. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.pop + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop + pub(crate) fn pop( + this: &JsValue, + _: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + // 3. If len = 0, then + if len == 0 { + // a. Perform ? Set(O, "length", +0𝔽, true). + o.set("length", 0, true, context)?; + // b. Return undefined. + Ok(JsValue::undefined()) + // 4. Else, + } else { + // a. Assert: len > 0. + // b. Let newLen be 𝔽(len - 1). + let new_len = len - 1; + // c. Let index be ! ToString(newLen). + let index = new_len; + // d. Let element be ? Get(O, index). + let element = o.get(index, context)?; + // e. Perform ? DeletePropertyOrThrow(O, index). + o.delete_property_or_throw(index, context)?; + // f. Perform ? Set(O, "length", newLen, true). + o.set("length", new_len, true, context)?; + // g. Return element. + Ok(element) + } + } + + /// `Array.prototype.forEach( callbackFn [ , thisArg ] )` + /// + /// This method executes the provided callback function for each element in the array. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.foreach + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach + pub(crate) fn for_each( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. + let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| { + JsNativeError::typ().with_message("Array.prototype.forEach: invalid callback function") + })?; + // 4. Let k be 0. + // 5. Repeat, while k < len, + for k in 0..len { + // a. Let Pk be ! ToString(𝔽(k)). + let pk = k; + // b. Let kPresent be ? HasProperty(O, Pk). + let present = o.has_property(pk, context)?; + // c. If kPresent is true, then + if present { + // i. Let kValue be ? Get(O, Pk). + let k_value = o.get(pk, context)?; + // ii. Perform ? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »). + let this_arg = args.get_or_undefined(1); + callback.call(this_arg, &[k_value, k.into(), o.clone().into()], context)?; + } + // d. Set k to k + 1. + } + // 6. Return undefined. + Ok(JsValue::undefined()) + } + + /// `Array.prototype.join( separator )` + /// + /// The elements of the array are converted to Strings, and these Strings are + /// then concatenated, separated by occurrences of the separator. If no + /// separator is provided, a single comma is used as the separator. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.join + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join + pub(crate) fn join( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + // 3. If separator is undefined, let sep be the single-element String ",". + // 4. Else, let sep be ? ToString(separator). + let separator = args.get_or_undefined(0); + let separator = if separator.is_undefined() { + js_string!(",") + } else { + separator.to_string(context)? + }; + + // 5. Let R be the empty String. + let mut r = Vec::new(); + // 6. Let k be 0. + // 7. Repeat, while k < len, + for k in 0..len { + // a. If k > 0, set R to the string-concatenation of R and sep. + if k > 0 { + r.extend_from_slice(&separator); + } + // b. Let element be ? Get(O, ! ToString(𝔽(k))). + let element = o.get(k, context)?; + // c. If element is undefined or null, let next be the empty String; otherwise, let next be ? ToString(element). + let next = if element.is_null_or_undefined() { + js_string!() + } else { + element.to_string(context)? + }; + // d. Set R to the string-concatenation of R and next. + r.extend_from_slice(&next); + // e. Set k to k + 1. + } + // 8. Return R. + Ok(js_string!(&r[..]).into()) + } + + /// `Array.prototype.toString( separator )` + /// + /// The toString function is intentionally generic; it does not require that + /// its this value be an Array object. Therefore it can be transferred to + /// other kinds of objects for use as a method. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.tostring + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString + #[allow(clippy::wrong_self_convention)] + pub(crate) fn to_string( + this: &JsValue, + _: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let array be ? ToObject(this value). + let array = this.to_object(context)?; + // 2. Let func be ? Get(array, "join"). + let func = array.get("join", context)?; + // 3. If IsCallable(func) is false, set func to the intrinsic function %Object.prototype.toString%. + // 4. Return ? Call(func, array). + if let Some(func) = func.as_callable() { + func.call(&array.into(), &[], context) + } else { + crate::builtins::object::Object::to_string(&array.into(), &[], context) + } + } + + /// `Array.prototype.reverse()` + /// + /// The elements of the array are rearranged so as to reverse their order. + /// The object is returned as the result of the call. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.reverse + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse + #[allow(clippy::else_if_without_else)] + pub(crate) fn reverse( + this: &JsValue, + _: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + // 3. Let middle be floor(len / 2). + let middle = len / 2; + // 4. Let lower be 0. + let mut lower = 0; + // 5. Repeat, while lower ≠ middle, + while lower != middle { + // a. Let upper be len - lower - 1. + let upper = len - lower - 1; + // Skipped: b. Let upperP be ! ToString(𝔽(upper)). + // Skipped: c. Let lowerP be ! ToString(𝔽(lower)). + // d. Let lowerExists be ? HasProperty(O, lowerP). + let lower_exists = o.has_property(lower, context)?; + // e. If lowerExists is true, then + let mut lower_value = JsValue::undefined(); + if lower_exists { + // i. Let lowerValue be ? Get(O, lowerP). + lower_value = o.get(lower, context)?; + } + // f. Let upperExists be ? HasProperty(O, upperP). + let upper_exists = o.has_property(upper, context)?; + // g. If upperExists is true, then + let mut upper_value = JsValue::undefined(); + if upper_exists { + // i. Let upperValue be ? Get(O, upperP). + upper_value = o.get(upper, context)?; + } + match (lower_exists, upper_exists) { + // h. If lowerExists is true and upperExists is true, then + (true, true) => { + // i. Perform ? Set(O, lowerP, upperValue, true). + o.set(lower, upper_value, true, context)?; + // ii. Perform ? Set(O, upperP, lowerValue, true). + o.set(upper, lower_value, true, context)?; + } + // i. Else if lowerExists is false and upperExists is true, then + (false, true) => { + // i. Perform ? Set(O, lowerP, upperValue, true). + o.set(lower, upper_value, true, context)?; + // ii. Perform ? DeletePropertyOrThrow(O, upperP). + o.delete_property_or_throw(upper, context)?; + } + // j. Else if lowerExists is true and upperExists is false, then + (true, false) => { + // i. Perform ? DeletePropertyOrThrow(O, lowerP). + o.delete_property_or_throw(lower, context)?; + // ii. Perform ? Set(O, upperP, lowerValue, true). + o.set(upper, lower_value, true, context)?; + } + // k. Else, + (false, false) => { + // i. Assert: lowerExists and upperExists are both false. + // ii. No action is required. + } + } + + // l. Set lower to lower + 1. + lower += 1; + } + // 6. Return O. + Ok(o.into()) + } + + /// `Array.prototype.shift()` + /// + /// The first element of the array is removed from the array and returned. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.shift + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift + pub(crate) fn shift( + this: &JsValue, + _: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + // 3. If len = 0, then + if len == 0 { + // a. Perform ? Set(O, "length", +0𝔽, true). + o.set("length", 0, true, context)?; + // b. Return undefined. + return Ok(JsValue::undefined()); + } + // 4. Let first be ? Get(O, "0"). + let first = o.get(0, context)?; + // 5. Let k be 1. + // 6. Repeat, while k < len, + for k in 1..len { + // a. Let from be ! ToString(𝔽(k)). + let from = k; + // b. Let to be ! ToString(𝔽(k - 1)). + let to = k - 1; + // c. Let fromPresent be ? HasProperty(O, from). + let from_present = o.has_property(from, context)?; + // d. If fromPresent is true, then + if from_present { + // i. Let fromVal be ? Get(O, from). + let from_val = o.get(from, context)?; + // ii. Perform ? Set(O, to, fromVal, true). + o.set(to, from_val, true, context)?; + // e. Else, + } else { + // i. Assert: fromPresent is false. + // ii. Perform ? DeletePropertyOrThrow(O, to). + o.delete_property_or_throw(to, context)?; + } + // f. Set k to k + 1. + } + // 7. Perform ? DeletePropertyOrThrow(O, ! ToString(𝔽(len - 1))). + o.delete_property_or_throw(len - 1, context)?; + // 8. Perform ? Set(O, "length", 𝔽(len - 1), true). + o.set("length", len - 1, true, context)?; + // 9. Return first. + Ok(first) + } + + /// `Array.prototype.unshift( ...items )` + /// + /// The arguments are prepended to the start of the array, such that their order + /// within the array is the same as the order in which they appear in the + /// argument list. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.unshift + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift + pub(crate) fn unshift( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + // 3. Let argCount be the number of elements in items. + let arg_count = args.len() as u64; + // 4. If argCount > 0, then + if arg_count > 0 { + // a. If len + argCount > 2^53 - 1, throw a TypeError exception. + if len + arg_count > 2u64.pow(53) - 1 { + return Err(JsNativeError::typ() + .with_message("length + number of arguments exceeds the max safe integer limit") + .into()); + } + // b. Let k be len. + let mut k = len; + // c. Repeat, while k > 0, + while k > 0 { + // i. Let from be ! ToString(𝔽(k - 1)). + let from = k - 1; + // ii. Let to be ! ToString(𝔽(k + argCount - 1)). + let to = k + arg_count - 1; + // iii. Let fromPresent be ? HasProperty(O, from). + let from_present = o.has_property(from, context)?; + // iv. If fromPresent is true, then + if from_present { + // 1. Let fromValue be ? Get(O, from). + let from_value = o.get(from, context)?; + // 2. Perform ? Set(O, to, fromValue, true). + o.set(to, from_value, true, context)?; + // v. Else, + } else { + // 1. Assert: fromPresent is false. + // 2. Perform ? DeletePropertyOrThrow(O, to). + o.delete_property_or_throw(to, context)?; + } + // vi. Set k to k - 1. + k -= 1; + } + // d. Let j be +0𝔽. + // e. For each element E of items, do + for (j, e) in args.iter().enumerate() { + // i. Perform ? Set(O, ! ToString(j), E, true). + o.set(j, e.clone(), true, context)?; + // ii. Set j to j + 1𝔽. + } + } + // 5. Perform ? Set(O, "length", 𝔽(len + argCount), true). + o.set("length", len + arg_count, true, context)?; + // 6. Return 𝔽(len + argCount). + Ok((len + arg_count).into()) + } + + /// `Array.prototype.every( callback, [ thisArg ] )` + /// + /// The every method executes the provided callback function once for each + /// element present in the array until it finds the one where callback returns + /// a falsy value. It returns `false` if it finds such element, otherwise it + /// returns `true`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.every + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every + pub(crate) fn every( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. + let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| { + JsNativeError::typ().with_message("Array.prototype.every: callback is not callable") + })?; + + let this_arg = args.get_or_undefined(1); + + // 4. Let k be 0. + // 5. Repeat, while k < len, + for k in 0..len { + // a. Let Pk be ! ToString(𝔽(k)). + // b. Let kPresent be ? HasProperty(O, Pk). + let k_present = o.has_property(k, context)?; + // c. If kPresent is true, then + if k_present { + // i. Let kValue be ? Get(O, Pk). + let k_value = o.get(k, context)?; + // ii. Let testResult be ! ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)). + let test_result = callback + .call(this_arg, &[k_value, k.into(), o.clone().into()], context)? + .to_boolean(); + // iii. If testResult is false, return false. + if !test_result { + return Ok(JsValue::new(false)); + } + } + // d. Set k to k + 1. + } + // 6. Return true. + Ok(JsValue::new(true)) + } + + /// `Array.prototype.map( callback, [ thisArg ] )` + /// + /// For each element in the array the callback function is called, and a new + /// array is constructed from the return values of these calls. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.map + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map + pub(crate) fn map( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. + let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| { + JsNativeError::typ().with_message("Array.prototype.map: Callbackfn is not callable") + })?; + + // 4. Let A be ? ArraySpeciesCreate(O, len). + let a = Self::array_species_create(&o, len, context)?; + + let this_arg = args.get_or_undefined(1); + + // 5. Let k be 0. + // 6. Repeat, while k < len, + for k in 0..len { + // a. Let Pk be ! ToString(𝔽(k)). + // b. Let k_present be ? HasProperty(O, Pk). + let k_present = o.has_property(k, context)?; + // c. If k_present is true, then + if k_present { + // i. Let kValue be ? Get(O, Pk). + let k_value = o.get(k, context)?; + // ii. Let mappedValue be ? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »). + let mapped_value = + callback.call(this_arg, &[k_value, k.into(), o.clone().into()], context)?; + // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue). + a.create_data_property_or_throw(k, mapped_value, context)?; + } + // d. Set k to k + 1. + } + // 7. Return A. + Ok(a.into()) + } + + /// `Array.prototype.indexOf( searchElement[, fromIndex ] )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.indexof + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf + pub(crate) fn index_of( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)? as i64; + + // 3. If len is 0, return -1𝔽. + if len == 0 { + return Ok(JsValue::new(-1)); + } + + // 4. Let n be ? ToIntegerOrInfinity(fromIndex). + let n = args + .get(1) + .cloned() + .unwrap_or_default() + .to_integer_or_infinity(context)?; + // 5. Assert: If fromIndex is undefined, then n is 0. + let n = match n { + // 6. If n is +∞, return -1𝔽. + IntegerOrInfinity::PositiveInfinity => return Ok(JsValue::new(-1)), + // 7. Else if n is -∞, set n to 0. + IntegerOrInfinity::NegativeInfinity => 0, + IntegerOrInfinity::Integer(value) => value, + }; + + // 8. If n ≥ 0, then + let mut k; + if n >= 0 { + // a. Let k be n. + k = n; + // 9. Else, + } else { + // a. Let k be len + n. + k = len + n; + // b. If k < 0, set k to 0. + if k < 0 { + k = 0; + } + }; + + let search_element = args.get_or_undefined(0); + + // 10. Repeat, while k < len, + while k < len { + // a. Let kPresent be ? HasProperty(O, ! ToString(𝔽(k))). + let k_present = o.has_property(k, context)?; + // b. If kPresent is true, then + if k_present { + // i. Let elementK be ? Get(O, ! ToString(𝔽(k))). + let element_k = o.get(k, context)?; + // ii. Let same be IsStrictlyEqual(searchElement, elementK). + // iii. If same is true, return 𝔽(k). + if search_element.strict_equals(&element_k) { + return Ok(JsValue::new(k)); + } + } + // c. Set k to k + 1. + k += 1; + } + // 11. Return -1𝔽. + Ok(JsValue::new(-1)) + } + + /// `Array.prototype.lastIndexOf( searchElement[, fromIndex ] )` + /// + /// + /// `lastIndexOf` compares searchElement to the elements of the array in descending order + /// using the Strict Equality Comparison algorithm, and if found at one or more indices, + /// returns the largest such index; otherwise, -1 is returned. + /// + /// The optional second argument fromIndex defaults to the array's length minus one + /// (i.e. the whole array is searched). If it is greater than or equal to the length of the array, + /// the whole array will be searched. If it is negative, it is used as the offset from the end + /// of the array to compute fromIndex. If the computed index is less than 0, -1 is returned. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.lastindexof + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf + pub(crate) fn last_index_of( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)? as i64; + + // 3. If len is 0, return -1𝔽. + if len == 0 { + return Ok(JsValue::new(-1)); + } + + // 4. If fromIndex is present, let n be ? ToIntegerOrInfinity(fromIndex); else let n be len - 1. + let n = if let Some(from_index) = args.get(1) { + from_index.to_integer_or_infinity(context)? + } else { + IntegerOrInfinity::Integer(len - 1) + }; + + let mut k = match n { + // 5. If n is -∞, return -1𝔽. + IntegerOrInfinity::NegativeInfinity => return Ok(JsValue::new(-1)), + // 6. If n ≥ 0, then + // a. Let k be min(n, len - 1). + IntegerOrInfinity::Integer(n) if n >= 0 => min(n, len - 1), + IntegerOrInfinity::PositiveInfinity => len - 1, + // 7. Else, + // a. Let k be len + n. + IntegerOrInfinity::Integer(n) => len + n, + }; + + let search_element = args.get_or_undefined(0); + + // 8. Repeat, while k ≥ 0, + while k >= 0 { + // a. Let kPresent be ? HasProperty(O, ! ToString(𝔽(k))). + let k_present = o.has_property(k, context)?; + // b. If kPresent is true, then + if k_present { + // i. Let elementK be ? Get(O, ! ToString(𝔽(k))). + let element_k = o.get(k, context)?; + // ii. Let same be IsStrictlyEqual(searchElement, elementK). + // iii. If same is true, return 𝔽(k). + if JsValue::strict_equals(search_element, &element_k) { + return Ok(JsValue::new(k)); + } + } + // c. Set k to k - 1. + k -= 1; + } + // 9. Return -1𝔽. + Ok(JsValue::new(-1)) + } + + /// `Array.prototype.find( callback, [thisArg] )` + /// + /// The find method executes the callback function once for each index of the array + /// until the callback returns a truthy value. If so, find immediately returns the value + /// of that element. Otherwise, find returns undefined. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.find + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find + pub(crate) fn find( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + + // 3. If IsCallable(predicate) is false, throw a TypeError exception. + let predicate = args.get_or_undefined(0).as_callable().ok_or_else(|| { + JsNativeError::typ().with_message("Array.prototype.find: predicate is not callable") + })?; + + let this_arg = args.get_or_undefined(1); + + // 4. Let k be 0. + let mut k = 0; + // 5. Repeat, while k < len, + while k < len { + // a. Let Pk be ! ToString(𝔽(k)). + let pk = k; + // b. Let kValue be ? Get(O, Pk). + let k_value = o.get(pk, context)?; + // c. Let testResult be ! ToBoolean(? Call(predicate, thisArg, « kValue, 𝔽(k), O »)). + let test_result = predicate + .call( + this_arg, + &[k_value.clone(), k.into(), o.clone().into()], + context, + )? + .to_boolean(); + // d. If testResult is true, return kValue. + if test_result { + return Ok(k_value); + } + // e. Set k to k + 1. + k += 1; + } + // 6. Return undefined. + Ok(JsValue::undefined()) + } + + /// `Array.prototype.findIndex( predicate [ , thisArg ] )` + /// + /// This method executes the provided predicate function for each element of the array. + /// If the predicate function returns `true` for an element, this method returns the index of the element. + /// If all elements return `false`, the value `-1` is returned. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.findindex + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex + pub(crate) fn find_index( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + + // 3. If IsCallable(predicate) is false, throw a TypeError exception. + let predicate = args.get_or_undefined(0).as_callable().ok_or_else(|| { + JsNativeError::typ() + .with_message("Array.prototype.findIndex: predicate is not callable") + })?; + + let this_arg = args.get_or_undefined(1); + + // 4. Let k be 0. + let mut k = 0; + // 5. Repeat, while k < len, + while k < len { + // a. Let Pk be ! ToString(𝔽(k)). + let pk = k; + // b. Let kValue be ? Get(O, Pk). + let k_value = o.get(pk, context)?; + // c. Let testResult be ! ToBoolean(? Call(predicate, thisArg, « kValue, 𝔽(k), O »)). + let test_result = predicate + .call(this_arg, &[k_value, k.into(), o.clone().into()], context)? + .to_boolean(); + // d. If testResult is true, return 𝔽(k). + if test_result { + return Ok(JsValue::new(k)); + } + // e. Set k to k + 1. + k += 1; + } + // 6. Return -1𝔽. + Ok(JsValue::new(-1)) + } + + /// `Array.prototype.findLast( predicate, [thisArg] )` + /// + /// findLast calls predicate once for each element of the array, in descending order, + /// until it finds one where predicate returns true. If such an element is found, findLast + /// immediately returns that element value. Otherwise, findLast returns undefined. + /// + /// More information: + /// - [ECMAScript proposal][spec] + /// + /// [spec]: https://tc39.es/proposal-array-find-from-last/#sec-array.prototype.findlast + pub(crate) fn find_last( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + + // 3. If IsCallable(predicate) is false, throw a TypeError exception. + let predicate = args.get_or_undefined(0).as_callable().ok_or_else(|| { + JsNativeError::typ().with_message("Array.prototype.findLast: predicate is not callable") + })?; + + let this_arg = args.get_or_undefined(1); + + // 4. Let k be len - 1. (implementation differs slightly from spec because k is unsigned) + // 5. Repeat, while k >= 0, (implementation differs slightly from spec because k is unsigned) + for k in (0..len).rev() { + // a. Let Pk be ! ToString(𝔽(k)). + // b. Let kValue be ? Get(O, Pk). + let k_value = o.get(k, context)?; + // c. Let testResult be ! ToBoolean(? Call(predicate, thisArg, « kValue, 𝔽(k), O »)). + let test_result = predicate + .call( + this_arg, + &[k_value.clone(), k.into(), this.clone()], + context, + )? + .to_boolean(); + // d. If testResult is true, return kValue. + if test_result { + return Ok(k_value); + } + // e. Set k to k - 1. + } + // 6. Return undefined. + Ok(JsValue::undefined()) + } + + /// `Array.prototype.findLastIndex( predicate [ , thisArg ] )` + /// + /// `findLastIndex` calls predicate once for each element of the array, in descending order, + /// until it finds one where predicate returns true. If such an element is found, `findLastIndex` + /// immediately returns the index of that element value. Otherwise, `findLastIndex` returns -1. + /// + /// More information: + /// - [ECMAScript proposal][spec] + /// + /// [spec]: https://tc39.es/proposal-array-find-from-last/#sec-array.prototype.findlastindex + pub(crate) fn find_last_index( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + + // 3. If IsCallable(predicate) is false, throw a TypeError exception. + let predicate = args.get_or_undefined(0).as_callable().ok_or_else(|| { + JsNativeError::typ() + .with_message("Array.prototype.findLastIndex: predicate is not callable") + })?; + + let this_arg = args.get_or_undefined(1); + + // 4. Let k be len - 1. (implementation differs slightly from spec because k is unsigned) + // 5. Repeat, while k >= 0, (implementation differs slightly from spec because k is unsigned) + for k in (0..len).rev() { + // a. Let Pk be ! ToString(𝔽(k)). + // b. Let kValue be ? Get(O, Pk). + let k_value = o.get(k, context)?; + // c. Let testResult be ! ToBoolean(? Call(predicate, thisArg, « kValue, 𝔽(k), O »)). + let test_result = predicate + .call(this_arg, &[k_value, k.into(), this.clone()], context)? + .to_boolean(); + // d. If testResult is true, return 𝔽(k). + if test_result { + return Ok(JsValue::new(k)); + } + // e. Set k to k - 1. + } + // 6. Return -1𝔽. + Ok(JsValue::new(-1)) + } + + /// `Array.prototype.flat( [depth] )` + /// + /// This method creates a new array with all sub-array elements concatenated into it + /// recursively up to the specified depth. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.flat + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat + pub(crate) fn flat( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ToObject(this value) + let o = this.to_object(context)?; + + // 2. Let sourceLen be LengthOfArrayLike(O) + let source_len = o.length_of_array_like(context)?; + + // 3. Let depthNum be 1 + let mut depth_num = 1; + + // 4. If depth is not undefined, then set depthNum to IntegerOrInfinity(depth) + if let Some(depth) = args.get(0) { + // a. Set depthNum to ? ToIntegerOrInfinity(depth). + // b. If depthNum < 0, set depthNum to 0. + match depth.to_integer_or_infinity(context)? { + IntegerOrInfinity::Integer(value) if value >= 0 => depth_num = value as u64, + IntegerOrInfinity::PositiveInfinity => depth_num = u64::MAX, + _ => depth_num = 0, + } + }; + + // 5. Let A be ArraySpeciesCreate(O, 0) + let a = Self::array_species_create(&o, 0, context)?; + + // 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, depthNum) + Self::flatten_into_array( + &a, + &o, + source_len, + 0, + depth_num, + None, + &JsValue::undefined(), + context, + )?; + + Ok(a.into()) + } + + /// `Array.prototype.flatMap( callback, [ thisArg ] )` + /// + /// This method returns a new array formed by applying a given callback function to + /// each element of the array, and then flattening the result by one level. It is + /// identical to a `map()` followed by a `flat()` of depth 1, but slightly more + /// efficient than calling those two methods separately. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.flatMap + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap + pub(crate) fn flat_map( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ToObject(this value) + let o = this.to_object(context)?; + + // 2. Let sourceLen be LengthOfArrayLike(O) + let source_len = o.length_of_array_like(context)?; + + // 3. If ! IsCallable(mapperFunction) is false, throw a TypeError exception. + let mapper_function = args.get_or_undefined(0).as_callable().ok_or_else(|| { + JsNativeError::typ().with_message("flatMap mapper function is not callable") + })?; + + // 4. Let A be ? ArraySpeciesCreate(O, 0). + let a = Self::array_species_create(&o, 0, context)?; + + // 5. Perform ? FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, thisArg). + Self::flatten_into_array( + &a, + &o, + source_len, + 0, + 1, + Some(mapper_function), + args.get_or_undefined(1), + context, + )?; + + // 6. Return A + Ok(a.into()) + } + + /// Abstract method `FlattenIntoArray`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-flattenintoarray + #[allow(clippy::too_many_arguments)] + fn flatten_into_array( + target: &JsObject, + source: &JsObject, + source_len: u64, + start: u64, + depth: u64, + mapper_function: Option<&JsObject>, + this_arg: &JsValue, + context: &mut Context<'_>, + ) -> JsResult { + // 1. Assert target is Object + // 2. Assert source is Object + + // 3. Assert if mapper_function is present, then: + // - IsCallable(mapper_function) is true + // - thisArg is present + // - depth is 1 + + // 4. Let targetIndex be start + let mut target_index = start; + + // 5. Let sourceIndex be 0 + let mut source_index = 0; + + // 6. Repeat, while R(sourceIndex) < sourceLen + while source_index < source_len { + // a. Let P be ToString(sourceIndex) + let p = source_index; + + // b. Let exists be ? HasProperty(source, P). + let exists = source.has_property(p, context)?; + // c. If exists is true, then + if exists { + // i. Let element be Get(source, P) + let mut element = source.get(p, context)?; + + // ii. If mapperFunction is present, then + if let Some(mapper_function) = mapper_function { + // 1. Set element to ? Call(mapperFunction, thisArg, <>) + element = mapper_function.call( + this_arg, + &[element, source_index.into(), source.clone().into()], + context, + )?; + } + + // iii. Let shouldFlatten be false + // iv. If depth > 0, then + let should_flatten = if depth > 0 { + // 1. Set shouldFlatten to ? IsArray(element). + element.is_array()? + } else { + false + }; + + // v. If shouldFlatten is true + if should_flatten { + // For `should_flatten` to be true, element must be an object. + let element = element.as_object().expect("must be an object"); + + // 1. If depth is +Infinity let newDepth be +Infinity + let new_depth = if depth == u64::MAX { + u64::MAX + // 2. Else, let newDepth be depth - 1 + } else { + depth - 1 + }; + + // 3. Let elementLen be ? LengthOfArrayLike(element) + let element_len = element.length_of_array_like(context)?; + + // 4. Set targetIndex to ? FlattenIntoArray(target, element, elementLen, targetIndex, newDepth) + target_index = Self::flatten_into_array( + target, + element, + element_len, + target_index, + new_depth, + None, + &JsValue::undefined(), + context, + )?; + + // vi. Else + } else { + // 1. If targetIndex >= 2^53 - 1, throw a TypeError exception + if target_index >= Number::MAX_SAFE_INTEGER as u64 { + return Err(JsNativeError::typ() + .with_message("Target index exceeded max safe integer value") + .into()); + } + + // 2. Perform ? CreateDataPropertyOrThrow(target, targetIndex, element) + target.create_data_property_or_throw(target_index, element, context)?; + + // 3. Set targetIndex to targetIndex + 1 + target_index += 1; + } + } + // d. Set sourceIndex to sourceIndex + 1 + source_index += 1; + } + + // 7. Return targetIndex + Ok(target_index) + } + + /// `Array.prototype.fill( value[, start[, end]] )` + /// + /// The method fills (modifies) all the elements of an array from start index (default 0) + /// to an end index (default array length) with a static value. It returns the modified array. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.fill + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill + pub(crate) fn fill( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + + // 3. Let relativeStart be ? ToIntegerOrInfinity(start). + // 4. If relativeStart is -∞, let k be 0. + // 5. Else if relativeStart < 0, let k be max(len + relativeStart, 0). + // 6. Else, let k be min(relativeStart, len). + let mut k = Self::get_relative_start(context, args.get(1), len)?; + + // 7. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end). + // 8. If relativeEnd is -∞, let final be 0. + // 9. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0). + // 10. Else, let final be min(relativeEnd, len). + let final_ = Self::get_relative_end(context, args.get(2), len)?; + + let value = args.get_or_undefined(0); + + // 11. Repeat, while k < final, + while k < final_ { + // a. Let Pk be ! ToString(𝔽(k)). + let pk = k; + // b. Perform ? Set(O, Pk, value, true). + o.set(pk, value.clone(), true, context)?; + // c. Set k to k + 1. + k += 1; + } + // 12. Return O. + Ok(o.into()) + } + + /// `Array.prototype.includes( valueToFind [, fromIndex] )` + /// + /// Determines whether an array includes a certain value among its entries, returning `true` or `false` as appropriate. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.includes + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes + pub(crate) fn includes_value( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)? as i64; + + // 3. If len is 0, return false. + if len == 0 { + return Ok(JsValue::new(false)); + } + + // 4. Let n be ? ToIntegerOrInfinity(fromIndex). + let n = args + .get(1) + .cloned() + .unwrap_or_default() + .to_integer_or_infinity(context)?; + // 5. Assert: If fromIndex is undefined, then n is 0. + // 6. If n is +∞, return false. + // 7. Else if n is -∞, set n to 0. + let n = match n { + IntegerOrInfinity::PositiveInfinity => return Ok(JsValue::new(false)), + IntegerOrInfinity::NegativeInfinity => 0, + IntegerOrInfinity::Integer(value) => value, + }; + + // 8. If n ≥ 0, then + let mut k; + if n >= 0 { + // a. Let k be n. + k = n; + // 9. Else, + } else { + // a. Let k be len + n. + k = len + n; + // b. If k < 0, set k to 0. + if k < 0 { + k = 0; + } + } + + let search_element = args.get_or_undefined(0); + + // 10. Repeat, while k < len, + while k < len { + // a. Let elementK be ? Get(O, ! ToString(𝔽(k))). + let element_k = o.get(k, context)?; + // b. If SameValueZero(searchElement, elementK) is true, return true. + if JsValue::same_value_zero(search_element, &element_k) { + return Ok(JsValue::new(true)); + } + // c. Set k to k + 1. + k += 1; + } + // 11. Return false. + Ok(JsValue::new(false)) + } + + /// `Array.prototype.slice( [begin[, end]] )` + /// + /// The slice method takes two arguments, start and end, and returns an array containing the + /// elements of the array from element start up to, but not including, element end (or through the + /// end of the array if end is undefined). If start is negative, it is treated as length + start + /// where length is the length of the array. If end is negative, it is treated as length + end where + /// length is the length of the array. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.slice + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice + pub(crate) fn slice( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + + // 3. Let relativeStart be ? ToIntegerOrInfinity(start). + // 4. If relativeStart is -∞, let k be 0. + // 5. Else if relativeStart < 0, let k be max(len + relativeStart, 0). + // 6. Else, let k be min(relativeStart, len). + let mut k = Self::get_relative_start(context, args.get(0), len)?; + + // 7. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end). + // 8. If relativeEnd is -∞, let final be 0. + // 9. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0). + // 10. Else, let final be min(relativeEnd, len). + let final_ = Self::get_relative_end(context, args.get(1), len)?; + + // 11. Let count be max(final - k, 0). + let count = final_.saturating_sub(k); + + // 12. Let A be ? ArraySpeciesCreate(O, count). + let a = Self::array_species_create(&o, count, context)?; + + // 13. Let n be 0. + let mut n: u64 = 0; + // 14. Repeat, while k < final, + while k < final_ { + // a. Let Pk be ! ToString(𝔽(k)). + let pk = k; + // b. Let kPresent be ? HasProperty(O, Pk). + let k_present = o.has_property(pk, context)?; + // c. If kPresent is true, then + if k_present { + // i. Let kValue be ? Get(O, Pk). + let k_value = o.get(pk, context)?; + // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), kValue). + a.create_data_property_or_throw(n, k_value, context)?; + } + // d. Set k to k + 1. + k += 1; + // e. Set n to n + 1. + n += 1; + } + + // 15. Perform ? Set(A, "length", 𝔽(n), true). + a.set("length", n, true, context)?; + + // 16. Return A. + Ok(a.into()) + } + + /// [`Array.prototype.toLocaleString ( [ locales [ , options ] ] )`][spec]. + /// + /// Returns a string representing the elements of the array. The elements are converted to + /// strings using their `toLocaleString` methods and these strings are separated by a + /// locale-specific string (such as a comma ","). + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma402/#sup-array.prototype.tolocalestring + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toLocaleString + pub(crate) fn to_locale_string( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let array be ? ToObject(this value). + let array = this.to_object(context)?; + // 2. Let len be ? ToLength(? Get(array, "length")). + let len = array.length_of_array_like(context)?; + + // 3. Let separator be the implementation-defined list-separator String value appropriate for the host environment's current locale (such as ", "). + let separator = { + #[cfg(feature = "intl")] + { + // TODO: this should eventually return a locale-sensitive separator. + utf16!(", ") + } + + #[cfg(not(feature = "intl"))] + { + utf16!(", ") + } + }; + + // 4. Let R be the empty String. + let mut r = Vec::new(); + + // 5. Let k be 0. + // 6. Repeat, while k < len, + for k in 0..len { + // a. If k > 0, then + if k > 0 { + // i. Set R to the string-concatenation of R and separator. + r.extend_from_slice(separator); + } + + // b. Let nextElement be ? Get(array, ! ToString(k)). + let next = array.get(k, context)?; + + // c. If nextElement is not undefined or null, then + if !next.is_null_or_undefined() { + // i. Let S be ? ToString(? Invoke(nextElement, "toLocaleString", « locales, options »)). + let s = next + .invoke("toLocaleString", args, context)? + .to_string(context)?; + + // ii. Set R to the string-concatenation of R and S. + r.extend_from_slice(&s); + } + // d. Increase k by 1. + } + // 7. Return R. + Ok(js_string!(r).into()) + } + + /// `Array.prototype.splice ( start, [deleteCount[, ...items]] )` + /// + /// Splices an array by following + /// The deleteCount elements of the array starting at integer index start are replaced by the elements of items. + /// An Array object containing the deleted elements (if any) is returned. + pub(crate) fn splice( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + + let start = args.get(0); + let delete_count = args.get(1); + let items = args.get(2..).unwrap_or(&[]); + // 3. Let relativeStart be ? ToIntegerOrInfinity(start). + // 4. If relativeStart is -∞, let actualStart be 0. + // 5. Else if relativeStart < 0, let actualStart be max(len + relativeStart, 0). + // 6. Else, let actualStart be min(relativeStart, len). + let actual_start = Self::get_relative_start(context, start, len)?; + // 7. If start is not present, then + let insert_count = if start.is_none() || delete_count.is_none() { + // 7a. Let insertCount be 0. + // 8. Else if deleteCount is not present, then + // a. Let insertCount be 0. + 0 + // 9. Else, + } else { + // 9a. Let insertCount be the number of elements in items. + items.len() as u64 + }; + let actual_delete_count = if start.is_none() { + // 7b. Let actualDeleteCount be 0. + 0 + // 8. Else if deleteCount is not present, then + } else if delete_count.is_none() { + // 8b. Let actualDeleteCount be len - actualStart. + len - actual_start + // 9. Else, + } else { + // b. Let dc be ? ToIntegerOrInfinity(deleteCount). + let dc = delete_count + .cloned() + .unwrap_or_default() + .to_integer_or_infinity(context)?; + // c. Let actualDeleteCount be the result of clamping dc between 0 and len - actualStart. + let max = len - actual_start; + match dc { + IntegerOrInfinity::Integer(i) => u64::try_from(i).unwrap_or_default().clamp(0, max), + IntegerOrInfinity::PositiveInfinity => max, + IntegerOrInfinity::NegativeInfinity => 0, + } + }; + + // 10. If len + insertCount - actualDeleteCount > 2^53 - 1, throw a TypeError exception. + if len + insert_count - actual_delete_count > Number::MAX_SAFE_INTEGER as u64 { + return Err(JsNativeError::typ() + .with_message("Target splice exceeded max safe integer value") + .into()); + } + + // 11. Let A be ? ArraySpeciesCreate(O, actualDeleteCount). + let arr = Self::array_species_create(&o, actual_delete_count, context)?; + // 12. Let k be 0. + // 13. Repeat, while k < actualDeleteCount, + for k in 0..actual_delete_count { + // a. Let from be ! ToString(𝔽(actualStart + k)). + // b. Let fromPresent be ? HasProperty(O, from). + let from_present = o.has_property(actual_start + k, context)?; + // c. If fromPresent is true, then + if from_present { + // i. Let fromValue be ? Get(O, from). + let from_value = o.get(actual_start + k, context)?; + // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(k)), fromValue). + arr.create_data_property_or_throw(k, from_value, context)?; + } + // d. Set k to k + 1. + } + + // 14. Perform ? Set(A, "length", 𝔽(actualDeleteCount), true). + arr.set("length", actual_delete_count, true, context)?; + + // 15. Let itemCount be the number of elements in items. + let item_count = items.len() as u64; + + match item_count.cmp(&actual_delete_count) { + // 16. If itemCount < actualDeleteCount, then + Ordering::Less => { + // a. Set k to actualStart. + // b. Repeat, while k < (len - actualDeleteCount), + for k in actual_start..(len - actual_delete_count) { + // i. Let from be ! ToString(𝔽(k + actualDeleteCount)). + let from = k + actual_delete_count; + // ii. Let to be ! ToString(𝔽(k + itemCount)). + let to = k + item_count; + // iii. Let fromPresent be ? HasProperty(O, from). + let from_present = o.has_property(from, context)?; + // iv. If fromPresent is true, then + if from_present { + // 1. Let fromValue be ? Get(O, from). + let from_value = o.get(from, context)?; + // 2. Perform ? Set(O, to, fromValue, true). + o.set(to, from_value, true, context)?; + // v. Else, + } else { + // 1. Assert: fromPresent is false. + debug_assert!(!from_present); + // 2. Perform ? DeletePropertyOrThrow(O, to). + o.delete_property_or_throw(to, context)?; + } + // vi. Set k to k + 1. + } + // c. Set k to len. + // d. Repeat, while k > (len - actualDeleteCount + itemCount), + for k in ((len - actual_delete_count + item_count)..len).rev() { + // i. Perform ? DeletePropertyOrThrow(O, ! ToString(𝔽(k - 1))). + o.delete_property_or_throw(k, context)?; + // ii. Set k to k - 1. + } + } + // 17. Else if itemCount > actualDeleteCount, then + Ordering::Greater => { + // a. Set k to (len - actualDeleteCount). + // b. Repeat, while k > actualStart, + for k in (actual_start..len - actual_delete_count).rev() { + // i. Let from be ! ToString(𝔽(k + actualDeleteCount - 1)). + let from = k + actual_delete_count; + // ii. Let to be ! ToString(𝔽(k + itemCount - 1)). + let to = k + item_count; + // iii. Let fromPresent be ? HasProperty(O, from). + let from_present = o.has_property(from, context)?; + // iv. If fromPresent is true, then + if from_present { + // 1. Let fromValue be ? Get(O, from). + let from_value = o.get(from, context)?; + // 2. Perform ? Set(O, to, fromValue, true). + o.set(to, from_value, true, context)?; + // v. Else, + } else { + // 1. Assert: fromPresent is false. + debug_assert!(!from_present); + // 2. Perform ? DeletePropertyOrThrow(O, to). + o.delete_property_or_throw(to, context)?; + } + // vi. Set k to k - 1. + } + } + Ordering::Equal => {} + }; + + // 18. Set k to actualStart. + // 19. For each element E of items, do + if item_count > 0 { + for (k, item) in items + .iter() + .enumerate() + .map(|(i, val)| (i as u64 + actual_start, val)) + { + // a. Perform ? Set(O, ! ToString(𝔽(k)), E, true). + o.set(k, item.clone(), true, context)?; + // b. Set k to k + 1. + } + } + + // 20. Perform ? Set(O, "length", 𝔽(len - actualDeleteCount + itemCount), true). + o.set( + "length", + len - actual_delete_count + item_count, + true, + context, + )?; + + // 21. Return A. + Ok(JsValue::from(arr)) + } + + /// `Array.prototype.filter( callback, [ thisArg ] )` + /// + /// For each element in the array the callback function is called, and a new + /// array is constructed for every value whose callback returned a truthy value. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.filter + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter + pub(crate) fn filter( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + + // 2. Let len be ? LengthOfArrayLike(O). + let length = o.length_of_array_like(context)?; + + // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. + let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| { + JsNativeError::typ().with_message("Array.prototype.filter: `callback` must be callable") + })?; + let this_arg = args.get_or_undefined(1); + + // 4. Let A be ? ArraySpeciesCreate(O, 0). + let a = Self::array_species_create(&o, 0, context)?; + + // 5. Let k be 0. + // 6. Let to be 0. + let mut to = 0u32; + // 7. Repeat, while k < len, + for idx in 0..length { + // a. Let Pk be ! ToString(𝔽(k)). + // b. Let kPresent be ? HasProperty(O, Pk). + // c. If kPresent is true, then + if o.has_property(idx, context)? { + // i. Let kValue be ? Get(O, Pk). + let element = o.get(idx, context)?; + + let args = [element.clone(), JsValue::new(idx), JsValue::new(o.clone())]; + + // ii. Let selected be ! ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)). + let selected = callback.call(this_arg, &args, context)?.to_boolean(); + + // iii. If selected is true, then + if selected { + // 1. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(to)), kValue). + a.create_data_property_or_throw(to, element, context)?; + // 2. Set to to to + 1. + to += 1; + } + } + } + + // 8. Return A. + Ok(a.into()) + } + + /// Array.prototype.some ( callbackfn [ , thisArg ] ) + /// + /// The some method tests whether at least one element in the array passes + /// the test implemented by the provided callback function. It returns a Boolean value, + /// true if the callback function returns a truthy value for at least one element + /// in the array. Otherwise, false. + /// + /// Caution: Calling this method on an empty array returns false for any condition! + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.some + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some + pub(crate) fn some( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. + let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| { + JsNativeError::typ().with_message("Array.prototype.some: callback is not callable") + })?; + + // 4. Let k be 0. + // 5. Repeat, while k < len, + for k in 0..len { + // a. Let Pk be ! ToString(𝔽(k)). + // b. Let kPresent be ? HasProperty(O, Pk). + let k_present = o.has_property(k, context)?; + // c. If kPresent is true, then + if k_present { + // i. Let kValue be ? Get(O, Pk). + let k_value = o.get(k, context)?; + // ii. Let testResult be ! ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)). + let this_arg = args.get_or_undefined(1); + let test_result = callback + .call(this_arg, &[k_value, k.into(), o.clone().into()], context)? + .to_boolean(); + // iii. If testResult is true, return true. + if test_result { + return Ok(JsValue::new(true)); + } + } + // d. Set k to k + 1. + } + // 6. Return false. + Ok(JsValue::new(false)) + } + + /// Array.prototype.sort ( comparefn ) + /// + /// The sort method sorts the elements of an array in place and returns the sorted array. + /// The default sort order is ascending, built upon converting the elements into strings, + /// then comparing their sequences of UTF-16 code units values. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.sort + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort + pub(crate) fn sort( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception. + let comparefn = match args.get_or_undefined(0) { + JsValue::Object(ref obj) if obj.is_callable() => Some(obj), + JsValue::Undefined => None, + _ => { + return Err(JsNativeError::typ() + .with_message("The comparison function must be either a function or undefined") + .into()) + } + }; + + // Abstract method `SortCompare`. + // + // More information: + // - [ECMAScript reference][spec] + // + // [spec]: https://tc39.es/ecma262/#sec-sortcompare + let sort_compare = + |x: &JsValue, y: &JsValue, context: &mut Context<'_>| -> JsResult { + match (x.is_undefined(), y.is_undefined()) { + // 1. If x and y are both undefined, return +0𝔽. + (true, true) => return Ok(Ordering::Equal), + // 2. If x is undefined, return 1𝔽. + (true, false) => return Ok(Ordering::Greater), + // 3. If y is undefined, return -1𝔽. + (false, true) => return Ok(Ordering::Less), + _ => {} + } + + // 4. If comparefn is not undefined, then + if let Some(cmp) = comparefn { + let args = [x.clone(), y.clone()]; + // a. Let v be ? ToNumber(? Call(comparefn, undefined, « x, y »)). + let v = cmp + .call(&JsValue::Undefined, &args, context)? + .to_number(context)?; + // b. If v is NaN, return +0𝔽. + // c. Return v. + return Ok(v.partial_cmp(&0.0).unwrap_or(Ordering::Equal)); + } + // 5. Let xString be ? ToString(x). + // 6. Let yString be ? ToString(y). + let x_str = x.to_string(context)?; + let y_str = y.to_string(context)?; + + // 7. Let xSmaller be IsLessThan(xString, yString, true). + // 8. If xSmaller is true, return -1𝔽. + // 9. Let ySmaller be IsLessThan(yString, xString, true). + // 10. If ySmaller is true, return 1𝔽. + // 11. Return +0𝔽. + + // NOTE: skipped IsLessThan because it just makes a lexicographic comparison + // when x and y are strings + Ok(x_str.cmp(&y_str)) + }; + + // 2. Let obj be ? ToObject(this value). + let obj = this.to_object(context)?; + + // 3. Let len be ? LengthOfArrayLike(obj). + let length = obj.length_of_array_like(context)?; + + // 4. Let items be a new empty List. + let mut items = Vec::with_capacity(length as usize); + + // 5. Let k be 0. + // 6. Repeat, while k < len, + for k in 0..length { + // a. Let Pk be ! ToString(𝔽(k)). + // b. Let kPresent be ? HasProperty(obj, Pk). + // c. If kPresent is true, then + if obj.has_property(k, context)? { + // i. Let kValue be ? Get(obj, Pk). + let kval = obj.get(k, context)?; + // ii. Append kValue to items. + items.push(kval); + } + // d. Set k to k + 1. + } + + // 7. Let itemCount be the number of elements in items. + let item_count = items.len() as u64; + + // 8. 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 or steps in this algorithm and return that completion. + let mut sort_err = Ok(()); + items.sort_by(|x, y| { + if sort_err.is_ok() { + sort_compare(x, y, context).unwrap_or_else(|err| { + sort_err = Err(err); + Ordering::Equal + }) + } else { + Ordering::Equal + } + }); + sort_err?; + + // 9. Let j be 0. + // 10. Repeat, while j < itemCount, + for (j, item) in items.into_iter().enumerate() { + // a. Perform ? Set(obj, ! ToString(𝔽(j)), items[j], true). + obj.set(j, item, true, context)?; + // b. Set j to j + 1. + } + + // 11. Repeat, while j < len, + for j in item_count..length { + // a. Perform ? DeletePropertyOrThrow(obj, ! ToString(𝔽(j))). + obj.delete_property_or_throw(j, context)?; + // b. Set j to j + 1. + } + + // 12. Return obj. + Ok(obj.into()) + } + + /// `Array.prototype.reduce( callbackFn [ , initialValue ] )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.reduce + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce + pub(crate) fn reduce( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + + // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. + let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| { + JsNativeError::typ() + .with_message("Array.prototype.reduce: callback function is not callable") + })?; + + // 4. If len = 0 and initialValue is not present, throw a TypeError exception. + if len == 0 && args.get(1).is_none() { + return Err(JsNativeError::typ() + .with_message( + "Array.prototype.reduce: called on an empty array and with no initial value", + ) + .into()); + } + + // 5. Let k be 0. + let mut k = 0; + // 6. Let accumulator be undefined. + let mut accumulator = JsValue::undefined(); + + // 7. If initialValue is present, then + if let Some(initial_value) = args.get(1) { + // a. Set accumulator to initialValue. + accumulator = initial_value.clone(); + // 8. Else, + } else { + // a. Let kPresent be false. + let mut k_present = false; + // b. Repeat, while kPresent is false and k < len, + while !k_present && k < len { + // i. Let Pk be ! ToString(𝔽(k)). + let pk = k; + // ii. Set kPresent to ? HasProperty(O, Pk). + k_present = o.has_property(pk, context)?; + // iii. If kPresent is true, then + if k_present { + // 1. Set accumulator to ? Get(O, Pk). + accumulator = o.get(pk, context)?; + } + // iv. Set k to k + 1. + k += 1; + } + // c. If kPresent is false, throw a TypeError exception. + if !k_present { + return Err(JsNativeError::typ().with_message( + "Array.prototype.reduce: called on an empty array and with no initial value", + ).into()); + } + } + + // 9. Repeat, while k < len, + while k < len { + // a. Let Pk be ! ToString(𝔽(k)). + let pk = k; + // b. Let kPresent be ? HasProperty(O, Pk). + let k_present = o.has_property(pk, context)?; + // c. If kPresent is true, then + if k_present { + // i. Let kValue be ? Get(O, Pk). + let k_value = o.get(pk, context)?; + // ii. Set accumulator to ? Call(callbackfn, undefined, « accumulator, kValue, 𝔽(k), O »). + accumulator = callback.call( + &JsValue::undefined(), + &[accumulator, k_value, k.into(), o.clone().into()], + context, + )?; + } + // d. Set k to k + 1. + k += 1; + } + + // 10. Return accumulator. + Ok(accumulator) + } + + /// `Array.prototype.reduceRight( callbackFn [ , initialValue ] )` + /// + /// The reduceRight method traverses right to left starting from the last defined value in the array, + /// accumulating a value using a given callback function. It returns the accumulated value. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.reduceright + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduceRight + pub(crate) fn reduce_right( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + + // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. + let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| { + JsNativeError::typ() + .with_message("Array.prototype.reduceRight: callback function is not callable") + })?; + + // 4. If len is 0 and initialValue is not present, throw a TypeError exception. + if len == 0 && args.get(1).is_none() { + return Err(JsNativeError::typ().with_message( + "Array.prototype.reduceRight: called on an empty array and with no initial value", + ).into()); + } + + // 5. Let k be len - 1. + let mut k = len as i64 - 1; + // 6. Let accumulator be undefined. + let mut accumulator = JsValue::undefined(); + // 7. If initialValue is present, then + if let Some(initial_value) = args.get(1) { + // a. Set accumulator to initialValue. + accumulator = initial_value.clone(); + // 8. Else, + } else { + // a. Let kPresent be false. + let mut k_present = false; + // b. Repeat, while kPresent is false and k ≥ 0, + while !k_present && k >= 0 { + // i. Let Pk be ! ToString(𝔽(k)). + let pk = k; + // ii. Set kPresent to ? HasProperty(O, Pk). + k_present = o.has_property(pk, context)?; + // iii. If kPresent is true, then + if k_present { + // 1. Set accumulator to ? Get(O, Pk). + accumulator = o.get(pk, context)?; + } + // iv. Set k to k - 1. + k -= 1; + } + // c. If kPresent is false, throw a TypeError exception. + if !k_present { + return Err(JsNativeError::typ().with_message( + "Array.prototype.reduceRight: called on an empty array and with no initial value", + ).into()); + } + } + + // 9. Repeat, while k ≥ 0, + while k >= 0 { + // a. Let Pk be ! ToString(𝔽(k)). + let pk = k; + // b. Let kPresent be ? HasProperty(O, Pk). + let k_present = o.has_property(pk, context)?; + // c. If kPresent is true, then + if k_present { + // i. Let kValue be ? Get(O, Pk). + let k_value = o.get(pk, context)?; + // ii. Set accumulator to ? Call(callbackfn, undefined, « accumulator, kValue, 𝔽(k), O »). + accumulator = callback.call( + &JsValue::undefined(), + &[accumulator.clone(), k_value, k.into(), o.clone().into()], + context, + )?; + } + // d. Set k to k - 1. + k -= 1; + } + + // 10. Return accumulator. + Ok(accumulator) + } + + /// `Array.prototype.copyWithin ( target, start [ , end ] )` + /// + /// The copyWithin() method shallow copies part of an array to another location + /// in the same array and returns it without modifying its length. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.copywithin + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin + pub(crate) fn copy_within( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + + // 2. Let len be ? LengthOfArrayLike(O). + let len = o.length_of_array_like(context)?; + + // 3. Let relativeTarget be ? ToIntegerOrInfinity(target). + // 4. If relativeTarget is -∞, let to be 0. + // 5. Else if relativeTarget < 0, let to be max(len + relativeTarget, 0). + // 6. Else, let to be min(relativeTarget, len). + let mut to = Self::get_relative_start(context, args.get(0), len)? as i64; + + // 7. Let relativeStart be ? ToIntegerOrInfinity(start). + // 8. If relativeStart is -∞, let from be 0. + // 9. Else if relativeStart < 0, let from be max(len + relativeStart, 0). + // 10. Else, let from be min(relativeStart, len). + let mut from = Self::get_relative_start(context, args.get(1), len)? as i64; + + // 11. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end). + // 12. If relativeEnd is -∞, let final be 0. + // 13. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0). + // 14. Else, let final be min(relativeEnd, len). + let final_ = Self::get_relative_end(context, args.get(2), len)? as i64; + + // 15. Let count be min(final - from, len - to). + let mut count = min(final_ - from, len as i64 - to); + + // 16. If from < to and to < from + count, then + let direction = if from < to && to < from + count { + // b. Set from to from + count - 1. + from = from + count - 1; + // c. Set to to to + count - 1. + to = to + count - 1; + + // a. Let direction be -1. + -1 + // 17. Else, + } else { + // a. Let direction be 1. + 1 + }; + + // 18. Repeat, while count > 0, + while count > 0 { + // a. Let fromKey be ! ToString(𝔽(from)). + let from_key = from; + + // b. Let toKey be ! ToString(𝔽(to)). + let to_key = to; + + // c. Let fromPresent be ? HasProperty(O, fromKey). + let from_present = o.has_property(from_key, context)?; + // d. If fromPresent is true, then + if from_present { + // i. Let fromVal be ? Get(O, fromKey). + let from_val = o.get(from_key, context)?; + // ii. Perform ? Set(O, toKey, fromVal, true). + o.set(to_key, from_val, true, context)?; + // e. Else, + } else { + // i. Assert: fromPresent is false. + // ii. Perform ? DeletePropertyOrThrow(O, toKey). + o.delete_property_or_throw(to_key, context)?; + } + // f. Set from to from + direction. + from += direction; + // g. Set to to to + direction. + to += direction; + // h. Set count to count - 1. + count -= 1; + } + // 19. Return O. + Ok(o.into()) + } + + /// `Array.prototype.values( )` + /// + /// The values method returns an iterable that iterates over the values in the array. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.values + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values + pub(crate) fn values( + this: &JsValue, + _: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + + // 2. Return CreateArrayIterator(O, value). + Ok(ArrayIterator::create_array_iterator( + o, + PropertyNameKind::Value, + context, + )) + } + + /// Creates an `Array.prototype.values( )` function object. + pub(crate) fn create_array_prototype_values(context: &mut Context<'_>) -> JsFunction { + FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(Self::values)) + .name("values") + .length(0) + .constructor(false) + .build() + } + + /// `Array.prototype.keys( )` + /// + /// The keys method returns an iterable that iterates over the indexes in the array. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.keys + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values + pub(crate) fn keys( + this: &JsValue, + _: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + + // 2. Return CreateArrayIterator(O, key). + Ok(ArrayIterator::create_array_iterator( + o, + PropertyNameKind::Key, + context, + )) + } + + /// `Array.prototype.entries( )` + /// + /// The entries method returns an iterable that iterates over the key-value pairs in the array. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.entries + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values + pub(crate) fn entries( + this: &JsValue, + _: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + + // 2. Return CreateArrayIterator(O, key+value). + Ok(ArrayIterator::create_array_iterator( + o, + PropertyNameKind::KeyAndValue, + context, + )) + } + + /// Represents the algorithm to calculate `relativeStart` (or `k`) in array functions. + pub(super) fn get_relative_start( + context: &mut Context<'_>, + arg: Option<&JsValue>, + len: u64, + ) -> JsResult { + // 1. Let relativeStart be ? ToIntegerOrInfinity(start). + let relative_start = arg + .cloned() + .unwrap_or_default() + .to_integer_or_infinity(context)?; + match relative_start { + // 2. If relativeStart is -∞, let k be 0. + IntegerOrInfinity::NegativeInfinity => Ok(0), + // 3. Else if relativeStart < 0, let k be max(len + relativeStart, 0). + IntegerOrInfinity::Integer(i) if i < 0 => Ok(max(len as i64 + i, 0) as u64), + // Both `as` casts are safe as both variables are non-negative + // 4. Else, let k be min(relativeStart, len). + IntegerOrInfinity::Integer(i) => Ok(min(i, len as i64) as u64), + + // Special case - positive infinity. `len` is always smaller than +inf, thus from (4) + IntegerOrInfinity::PositiveInfinity => Ok(len), + } + } + + /// Represents the algorithm to calculate `relativeEnd` (or `final`) in array functions. + pub(super) fn get_relative_end( + context: &mut Context<'_>, + arg: Option<&JsValue>, + len: u64, + ) -> JsResult { + let default_value = JsValue::undefined(); + let value = arg.unwrap_or(&default_value); + // 1. If end is undefined, let relativeEnd be len [and return it] + if value.is_undefined() { + Ok(len) + } else { + // 1. cont, else let relativeEnd be ? ToIntegerOrInfinity(end). + let relative_end = value.to_integer_or_infinity(context)?; + match relative_end { + // 2. If relativeEnd is -∞, let final be 0. + IntegerOrInfinity::NegativeInfinity => Ok(0), + // 3. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0). + IntegerOrInfinity::Integer(i) if i < 0 => Ok(max(len as i64 + i, 0) as u64), + // 4. Else, let final be min(relativeEnd, len). + // Both `as` casts are safe as both variables are non-negative + IntegerOrInfinity::Integer(i) => Ok(min(i, len as i64) as u64), + + // Special case - positive infinity. `len` is always smaller than +inf, thus from (4) + IntegerOrInfinity::PositiveInfinity => Ok(len), + } + } + } + + /// `Array.prototype [ @@unscopables ]` + /// + /// The initial value of the 'unscopables' data property is an ordinary object + /// with the following boolean properties set to true: + /// 'at', 'copyWithin', 'entries', 'fill', 'find', 'findIndex', 'flat', + /// 'flatMap', 'includes', 'keys', 'values' + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype-@@unscopables + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/@@unscopables + pub(crate) fn unscopables_intrinsic(context: &mut Context<'_>) -> JsObject { + // 1. Let unscopableList be OrdinaryObjectCreate(null). + let unscopable_list = JsObject::with_null_proto(); + // 2. Perform ! CreateDataPropertyOrThrow(unscopableList, "at", true). + unscopable_list + .create_data_property_or_throw("at", true, context) + .expect("CreateDataPropertyOrThrow for 'at' must not fail"); + // 3. Perform ! CreateDataPropertyOrThrow(unscopableList, "copyWithin", true). + unscopable_list + .create_data_property_or_throw("copyWithin", true, context) + .expect("CreateDataPropertyOrThrow for 'copyWithin' must not fail"); + // 4. Perform ! CreateDataPropertyOrThrow(unscopableList, "entries", true). + unscopable_list + .create_data_property_or_throw("entries", true, context) + .expect("CreateDataPropertyOrThrow for 'entries' must not fail"); + // 5. Perform ! CreateDataPropertyOrThrow(unscopableList, "fill", true). + unscopable_list + .create_data_property_or_throw("fill", true, context) + .expect("CreateDataPropertyOrThrow for 'fill' must not fail"); + // 6. Perform ! CreateDataPropertyOrThrow(unscopableList, "find", true). + unscopable_list + .create_data_property_or_throw("find", true, context) + .expect("CreateDataPropertyOrThrow for 'find' must not fail"); + // 7. Perform ! CreateDataPropertyOrThrow(unscopableList, "findIndex", true). + unscopable_list + .create_data_property_or_throw("findIndex", true, context) + .expect("CreateDataPropertyOrThrow for 'findIndex' must not fail"); + // 8. Perform ! CreateDataPropertyOrThrow(unscopableList, "flat", true). + unscopable_list + .create_data_property_or_throw("flat", true, context) + .expect("CreateDataPropertyOrThrow for 'flat' must not fail"); + // 9. Perform ! CreateDataPropertyOrThrow(unscopableList, "flatMap", true). + unscopable_list + .create_data_property_or_throw("flatMap", true, context) + .expect("CreateDataPropertyOrThrow for 'flatMap' must not fail"); + // 10. Perform ! CreateDataPropertyOrThrow(unscopableList, "includes", true). + unscopable_list + .create_data_property_or_throw("includes", true, context) + .expect("CreateDataPropertyOrThrow for 'includes' must not fail"); + // 11. Perform ! CreateDataPropertyOrThrow(unscopableList, "keys", true). + unscopable_list + .create_data_property_or_throw("keys", true, context) + .expect("CreateDataPropertyOrThrow for 'keys' must not fail"); + // 12. Perform ! CreateDataPropertyOrThrow(unscopableList, "values", true). + unscopable_list + .create_data_property_or_throw("values", true, context) + .expect("CreateDataPropertyOrThrow for 'values' must not fail"); + + // 13. Return unscopableList. + unscopable_list + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/array/tests.rs b/javascript-engine/external/boa/boa_engine/src/builtins/array/tests.rs new file mode 100644 index 0000000..3944623 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/array/tests.rs @@ -0,0 +1,1582 @@ +use super::Array; +use crate::builtins::Number; +use crate::{forward, Context, JsValue}; + +#[test] +fn is_array() { + let mut context = Context::default(); + let init = r#" + var empty = []; + var new_arr = new Array(); + var many = ["a", "b", "c"]; + "#; + context.eval(init).unwrap(); + assert_eq!( + context.eval("Array.isArray(empty)").unwrap(), + JsValue::new(true) + ); + assert_eq!( + context.eval("Array.isArray(new_arr)").unwrap(), + JsValue::new(true) + ); + assert_eq!( + context.eval("Array.isArray(many)").unwrap(), + JsValue::new(true) + ); + assert_eq!( + context.eval("Array.isArray([1, 2, 3])").unwrap(), + JsValue::new(true) + ); + assert_eq!( + context.eval("Array.isArray([])").unwrap(), + JsValue::new(true) + ); + assert_eq!( + context.eval("Array.isArray({})").unwrap(), + JsValue::new(false) + ); + // assert_eq!(context.eval("Array.isArray(new Array)"), "true"); + assert_eq!( + context.eval("Array.isArray()").unwrap(), + JsValue::new(false) + ); + assert_eq!( + context + .eval("Array.isArray({ constructor: Array })") + .unwrap(), + JsValue::new(false) + ); + assert_eq!( + context + .eval("Array.isArray({ push: Array.prototype.push, concat: Array.prototype.concat })") + .unwrap(), + JsValue::new(false) + ); + assert_eq!( + context.eval("Array.isArray(17)").unwrap(), + JsValue::new(false) + ); + assert_eq!( + context + .eval("Array.isArray({ __proto__: Array.prototype })") + .unwrap(), + JsValue::new(false) + ); + assert_eq!( + context.eval("Array.isArray({ length: 0 })").unwrap(), + JsValue::new(false) + ); +} + +#[test] +fn of() { + let mut context = Context::default(); + assert_eq!( + context + .eval("Array.of(1, 2, 3)") + .unwrap() + .to_string(&mut context) + .unwrap(), + context + .eval("[1, 2, 3]") + .unwrap() + .to_string(&mut context) + .unwrap() + ); + assert_eq!( + context + .eval("Array.of(1, 'a', [], undefined, null)") + .unwrap() + .to_string(&mut context) + .unwrap(), + context + .eval("[1, 'a', [], undefined, null]") + .unwrap() + .to_string(&mut context) + .unwrap() + ); + assert_eq!( + context + .eval("Array.of()") + .unwrap() + .to_string(&mut context) + .unwrap(), + context.eval("[]").unwrap().to_string(&mut context).unwrap() + ); + + context + .eval(r#"let a = Array.of.call(Date, "a", undefined, 3);"#) + .unwrap(); + assert_eq!( + context.eval("a instanceof Date").unwrap(), + JsValue::new(true) + ); + assert_eq!(context.eval("a[0]").unwrap(), JsValue::new("a")); + assert_eq!(context.eval("a[1]").unwrap(), JsValue::undefined()); + assert_eq!(context.eval("a[2]").unwrap(), JsValue::new(3)); + assert_eq!(context.eval("a.length").unwrap(), JsValue::new(3)); +} + +#[test] +fn concat() { + let mut context = Context::default(); + let init = r#" + var empty = []; + var one = [1]; + "#; + context.eval(init).unwrap(); + // Empty ++ Empty + let ee = context + .eval("empty.concat(empty)") + .unwrap() + .display() + .to_string(); + assert_eq!(ee, "[]"); + // Empty ++ NonEmpty + let en = context + .eval("empty.concat(one)") + .unwrap() + .display() + .to_string(); + assert_eq!(en, "[ 1 ]"); + // NonEmpty ++ Empty + let ne = context + .eval("one.concat(empty)") + .unwrap() + .display() + .to_string(); + assert_eq!(ne, "[ 1 ]"); + // NonEmpty ++ NonEmpty + let nn = context + .eval("one.concat(one)") + .unwrap() + .display() + .to_string(); + assert_eq!(nn, "[ 1, 1 ]"); +} + +#[test] +fn copy_within() { + let mut context = Context::default(); + + let target = forward(&mut context, "[1,2,3,4,5].copyWithin(-2).join('.')"); + assert_eq!(target, String::from("\"1.2.3.1.2\"")); + + let start = forward(&mut context, "[1,2,3,4,5].copyWithin(0, 3).join('.')"); + assert_eq!(start, String::from("\"4.5.3.4.5\"")); + + let end = forward(&mut context, "[1,2,3,4,5].copyWithin(0, 3, 4).join('.')"); + assert_eq!(end, String::from("\"4.2.3.4.5\"")); + + let negatives = forward(&mut context, "[1,2,3,4,5].copyWithin(-2, -3, -1).join('.')"); + assert_eq!(negatives, String::from("\"1.2.3.3.4\"")); +} + +#[test] +fn join() { + let mut context = Context::default(); + let init = r#" + var empty = [ ]; + var one = ["a"]; + var many = ["a", "b", "c"]; + "#; + eprintln!("{}", forward(&mut context, init)); + // Empty + let empty = forward(&mut context, "empty.join('.')"); + assert_eq!(empty, String::from("\"\"")); + // One + let one = forward(&mut context, "one.join('.')"); + assert_eq!(one, String::from("\"a\"")); + // Many + let many = forward(&mut context, "many.join('.')"); + assert_eq!(many, String::from("\"a.b.c\"")); +} + +#[test] +fn to_string() { + let mut context = Context::default(); + let init = r#" + var empty = [ ]; + var one = ["a"]; + var many = ["a", "b", "c"]; + "#; + eprintln!("{}", forward(&mut context, init)); + // Empty + let empty = forward(&mut context, "empty.toString()"); + assert_eq!(empty, String::from("\"\"")); + // One + let one = forward(&mut context, "one.toString()"); + assert_eq!(one, String::from("\"a\"")); + // Many + let many = forward(&mut context, "many.toString()"); + assert_eq!(many, String::from("\"a,b,c\"")); +} + +#[test] +fn every() { + let mut context = Context::default(); + // taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every + let init = r#" + var empty = []; + + var array = [11, 23, 45]; + function callback(element) { + return element > 10; + } + function callback2(element) { + return element < 10; + } + + var appendArray = [1,2,3,4]; + function appendingCallback(elem,index,arr) { + arr.push('new'); + return elem !== "new"; + } + + var delArray = [1,2,3,4]; + function deletingCallback(elem,index,arr) { + arr.pop() + return elem < 3; + } + "#; + eprintln!("{}", forward(&mut context, init)); + let result = forward(&mut context, "array.every(callback);"); + assert_eq!(result, "true"); + + let result = forward(&mut context, "empty.every(callback);"); + assert_eq!(result, "true"); + + let result = forward(&mut context, "array.every(callback2);"); + assert_eq!(result, "false"); + + let result = forward(&mut context, "appendArray.every(appendingCallback);"); + assert_eq!(result, "true"); + + let result = forward(&mut context, "delArray.every(deletingCallback);"); + assert_eq!(result, "true"); +} + +#[test] +fn find() { + let mut context = Context::default(); + let init = r#" + function comp(a) { + return a == "a"; + } + var many = ["a", "b", "c"]; + "#; + eprintln!("{}", forward(&mut context, init)); + let found = forward(&mut context, "many.find(comp)"); + assert_eq!(found, String::from("\"a\"")); +} + +#[test] +fn find_index() { + let mut context = Context::default(); + + let code = r#" + function comp(item) { + return item == 2; + } + var many = [1, 2, 3]; + var empty = []; + var missing = [4, 5, 6]; + "#; + + forward(&mut context, code); + + let many = forward(&mut context, "many.findIndex(comp)"); + assert_eq!(many, String::from("1")); + + let empty = forward(&mut context, "empty.findIndex(comp)"); + assert_eq!(empty, String::from("-1")); + + let missing = forward(&mut context, "missing.findIndex(comp)"); + assert_eq!(missing, String::from("-1")); +} + +#[test] +fn flat() { + let mut context = Context::default(); + + let code = r#" + var depth1 = ['a', ['b', 'c']]; + var flat_depth1 = depth1.flat(); + + var depth2 = ['a', ['b', ['c'], 'd']]; + var flat_depth2 = depth2.flat(2); + "#; + forward(&mut context, code); + + assert_eq!(forward(&mut context, "flat_depth1[0]"), "\"a\""); + assert_eq!(forward(&mut context, "flat_depth1[1]"), "\"b\""); + assert_eq!(forward(&mut context, "flat_depth1[2]"), "\"c\""); + assert_eq!(forward(&mut context, "flat_depth1.length"), "3"); + + assert_eq!(forward(&mut context, "flat_depth2[0]"), "\"a\""); + assert_eq!(forward(&mut context, "flat_depth2[1]"), "\"b\""); + assert_eq!(forward(&mut context, "flat_depth2[2]"), "\"c\""); + assert_eq!(forward(&mut context, "flat_depth2[3]"), "\"d\""); + assert_eq!(forward(&mut context, "flat_depth2.length"), "4"); +} + +#[test] +fn flat_empty() { + let mut context = Context::default(); + + let code = r#" + var empty = [[]]; + var flat_empty = empty.flat(); + "#; + forward(&mut context, code); + + assert_eq!(forward(&mut context, "flat_empty.length"), "0"); +} + +#[test] +fn flat_infinity() { + let mut context = Context::default(); + + let code = r#" + var arr = [[[[[['a']]]]]]; + var flat_arr = arr.flat(Infinity) + "#; + forward(&mut context, code); + + assert_eq!(forward(&mut context, "flat_arr[0]"), "\"a\""); + assert_eq!(forward(&mut context, "flat_arr.length"), "1"); +} + +#[test] +fn flat_map() { + let mut context = Context::default(); + + let code = r#" + var double = [1, 2, 3]; + var double_flatmap = double.flatMap(i => [i * 2]); + + var sentence = ["it's Sunny", "in Cali"]; + var flat_split_sentence = sentence.flatMap(x => x.split(" ")); + "#; + forward(&mut context, code); + + assert_eq!(forward(&mut context, "double_flatmap[0]"), "2"); + assert_eq!(forward(&mut context, "double_flatmap[1]"), "4"); + assert_eq!(forward(&mut context, "double_flatmap[2]"), "6"); + assert_eq!(forward(&mut context, "double_flatmap.length"), "3"); + + assert_eq!(forward(&mut context, "flat_split_sentence[0]"), "\"it's\""); + assert_eq!(forward(&mut context, "flat_split_sentence[1]"), "\"Sunny\""); + assert_eq!(forward(&mut context, "flat_split_sentence[2]"), "\"in\""); + assert_eq!(forward(&mut context, "flat_split_sentence[3]"), "\"Cali\""); + assert_eq!(forward(&mut context, "flat_split_sentence.length"), "4"); +} + +#[test] +fn flat_map_with_hole() { + let mut context = Context::default(); + + let code = r#" + var arr = [0, 1, 2]; + delete arr[1]; + var arr_flattened = arr.flatMap(i => [i * 2]); + "#; + forward(&mut context, code); + + assert_eq!(forward(&mut context, "arr_flattened[0]"), "0"); + assert_eq!(forward(&mut context, "arr_flattened[1]"), "4"); + assert_eq!(forward(&mut context, "arr_flattened.length"), "2"); +} + +#[test] +fn flat_map_not_callable() { + let mut context = Context::default(); + + let code = r#" + try { + var array = [1,2,3]; + array.flatMap("not a function"); + } catch (err) { + err.name === "TypeError" + } + "#; + + assert_eq!(forward(&mut context, code), "true"); +} + +#[test] +fn push() { + let mut context = Context::default(); + let init = r#" + var arr = [1, 2]; + "#; + eprintln!("{}", forward(&mut context, init)); + + assert_eq!(forward(&mut context, "arr.push()"), "2"); + assert_eq!(forward(&mut context, "arr.push(3, 4)"), "4"); + assert_eq!(forward(&mut context, "arr[2]"), "3"); + assert_eq!(forward(&mut context, "arr[3]"), "4"); +} + +#[test] +fn pop() { + let mut context = Context::default(); + let init = r#" + var empty = [ ]; + var one = [1]; + var many = [1, 2, 3, 4]; + "#; + eprintln!("{}", forward(&mut context, init)); + + assert_eq!( + forward(&mut context, "empty.pop()"), + String::from("undefined") + ); + assert_eq!(forward(&mut context, "one.pop()"), "1"); + assert_eq!(forward(&mut context, "one.length"), "0"); + assert_eq!(forward(&mut context, "many.pop()"), "4"); + assert_eq!(forward(&mut context, "many[0]"), "1"); + assert_eq!(forward(&mut context, "many.length"), "3"); +} + +#[test] +fn shift() { + let mut context = Context::default(); + let init = r#" + var empty = [ ]; + var one = [1]; + var many = [1, 2, 3, 4]; + "#; + eprintln!("{}", forward(&mut context, init)); + + assert_eq!( + forward(&mut context, "empty.shift()"), + String::from("undefined") + ); + assert_eq!(forward(&mut context, "one.shift()"), "1"); + assert_eq!(forward(&mut context, "one.length"), "0"); + assert_eq!(forward(&mut context, "many.shift()"), "1"); + assert_eq!(forward(&mut context, "many[0]"), "2"); + assert_eq!(forward(&mut context, "many.length"), "3"); +} + +#[test] +fn unshift() { + let mut context = Context::default(); + let init = r#" + var arr = [3, 4]; + "#; + eprintln!("{}", forward(&mut context, init)); + + assert_eq!(forward(&mut context, "arr.unshift()"), "2"); + assert_eq!(forward(&mut context, "arr.unshift(1, 2)"), "4"); + assert_eq!(forward(&mut context, "arr[0]"), "1"); + assert_eq!(forward(&mut context, "arr[1]"), "2"); +} + +#[test] +fn reverse() { + let mut context = Context::default(); + let init = r#" + var arr = [1, 2]; + var reversed = arr.reverse(); + "#; + eprintln!("{}", forward(&mut context, init)); + assert_eq!(forward(&mut context, "reversed[0]"), "2"); + assert_eq!(forward(&mut context, "reversed[1]"), "1"); + assert_eq!(forward(&mut context, "arr[0]"), "2"); + assert_eq!(forward(&mut context, "arr[1]"), "1"); +} + +#[test] +fn index_of() { + let mut context = Context::default(); + let init = r#" + var empty = [ ]; + var one = ["a"]; + var many = ["a", "b", "c"]; + var duplicates = ["a", "b", "c", "a", "b"]; + "#; + eprintln!("{}", forward(&mut context, init)); + + // Empty + let empty = forward(&mut context, "empty.indexOf('a')"); + assert_eq!(empty, String::from("-1")); + + // One + let one = forward(&mut context, "one.indexOf('a')"); + assert_eq!(one, String::from("0")); + // Missing from one + let missing_from_one = forward(&mut context, "one.indexOf('b')"); + assert_eq!(missing_from_one, String::from("-1")); + + // First in many + let first_in_many = forward(&mut context, "many.indexOf('a')"); + assert_eq!(first_in_many, String::from("0")); + // Second in many + let second_in_many = forward(&mut context, "many.indexOf('b')"); + assert_eq!(second_in_many, String::from("1")); + + // First in duplicates + let first_in_many = forward(&mut context, "duplicates.indexOf('a')"); + assert_eq!(first_in_many, String::from("0")); + // Second in duplicates + let second_in_many = forward(&mut context, "duplicates.indexOf('b')"); + assert_eq!(second_in_many, String::from("1")); + + // Positive fromIndex greater than array length + let fromindex_greater_than_length = forward(&mut context, "one.indexOf('a', 2)"); + assert_eq!(fromindex_greater_than_length, String::from("-1")); + // Positive fromIndex missed match + let fromindex_misses_match = forward(&mut context, "many.indexOf('a', 1)"); + assert_eq!(fromindex_misses_match, String::from("-1")); + // Positive fromIndex matched + let fromindex_matches = forward(&mut context, "many.indexOf('b', 1)"); + assert_eq!(fromindex_matches, String::from("1")); + // Positive fromIndex with duplicates + let first_in_many = forward(&mut context, "duplicates.indexOf('a', 1)"); + assert_eq!(first_in_many, String::from("3")); + + // Negative fromIndex greater than array length + let fromindex_greater_than_length = forward(&mut context, "one.indexOf('a', -2)"); + assert_eq!(fromindex_greater_than_length, String::from("0")); + // Negative fromIndex missed match + let fromindex_misses_match = forward(&mut context, "many.indexOf('b', -1)"); + assert_eq!(fromindex_misses_match, String::from("-1")); + // Negative fromIndex matched + let fromindex_matches = forward(&mut context, "many.indexOf('c', -1)"); + assert_eq!(fromindex_matches, String::from("2")); + // Negative fromIndex with duplicates + let second_in_many = forward(&mut context, "duplicates.indexOf('b', -2)"); + assert_eq!(second_in_many, String::from("4")); +} + +#[test] +fn last_index_of() { + let mut context = Context::default(); + let init = r#" + var empty = [ ]; + var one = ["a"]; + var many = ["a", "b", "c"]; + var duplicates = ["a", "b", "c", "a", "b"]; + "#; + eprintln!("{}", forward(&mut context, init)); + + // Empty + let empty = forward(&mut context, "empty.lastIndexOf('a')"); + assert_eq!(empty, String::from("-1")); + + // One + let one = forward(&mut context, "one.lastIndexOf('a')"); + assert_eq!(one, String::from("0")); + // Missing from one + let missing_from_one = forward(&mut context, "one.lastIndexOf('b')"); + assert_eq!(missing_from_one, String::from("-1")); + + // First in many + let first_in_many = forward(&mut context, "many.lastIndexOf('a')"); + assert_eq!(first_in_many, String::from("0")); + // Second in many + let second_in_many = forward(&mut context, "many.lastIndexOf('b')"); + assert_eq!(second_in_many, String::from("1")); + + // 4th in duplicates + let first_in_many = forward(&mut context, "duplicates.lastIndexOf('a')"); + assert_eq!(first_in_many, String::from("3")); + // 5th in duplicates + let second_in_many = forward(&mut context, "duplicates.lastIndexOf('b')"); + assert_eq!(second_in_many, String::from("4")); + + // Positive fromIndex greater than array length + let fromindex_greater_than_length = forward(&mut context, "one.lastIndexOf('a', 2)"); + assert_eq!(fromindex_greater_than_length, String::from("0")); + // Positive fromIndex missed match + let fromindex_misses_match = forward(&mut context, "many.lastIndexOf('c', 1)"); + assert_eq!(fromindex_misses_match, String::from("-1")); + // Positive fromIndex matched + let fromindex_matches = forward(&mut context, "many.lastIndexOf('b', 1)"); + assert_eq!(fromindex_matches, String::from("1")); + // Positive fromIndex with duplicates + let first_in_many = forward(&mut context, "duplicates.lastIndexOf('a', 1)"); + assert_eq!(first_in_many, String::from("0")); + + // Negative fromIndex greater than array length + let fromindex_greater_than_length = forward(&mut context, "one.lastIndexOf('a', -2)"); + assert_eq!(fromindex_greater_than_length, String::from("-1")); + // Negative fromIndex missed match + let fromindex_misses_match = forward(&mut context, "many.lastIndexOf('c', -2)"); + assert_eq!(fromindex_misses_match, String::from("-1")); + // Negative fromIndex matched + let fromindex_matches = forward(&mut context, "many.lastIndexOf('c', -1)"); + assert_eq!(fromindex_matches, String::from("2")); + // Negative fromIndex with duplicates + let second_in_many = forward(&mut context, "duplicates.lastIndexOf('b', -2)"); + assert_eq!(second_in_many, String::from("1")); +} + +#[test] +fn fill_obj_ref() { + let mut context = Context::default(); + + // test object reference + forward(&mut context, "a = (new Array(3)).fill({});"); + forward(&mut context, "a[0].hi = 'hi';"); + assert_eq!(forward(&mut context, "a[0].hi"), "\"hi\""); +} + +#[test] +fn fill() { + let mut context = Context::default(); + + forward(&mut context, "var a = [1, 2, 3];"); + assert_eq!( + forward(&mut context, "a.fill(4).join()"), + String::from("\"4,4,4\"") + ); + // make sure the array is modified + assert_eq!(forward(&mut context, "a.join()"), String::from("\"4,4,4\"")); + + forward(&mut context, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut context, "a.fill(4, '1').join()"), + String::from("\"1,4,4\"") + ); + + forward(&mut context, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut context, "a.fill(4, 1, 2).join()"), + String::from("\"1,4,3\"") + ); + + forward(&mut context, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut context, "a.fill(4, 1, 1).join()"), + String::from("\"1,2,3\"") + ); + + forward(&mut context, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut context, "a.fill(4, 3, 3).join()"), + String::from("\"1,2,3\"") + ); + + forward(&mut context, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut context, "a.fill(4, -3, -2).join()"), + String::from("\"4,2,3\"") + ); + + forward(&mut context, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut context, "a.fill(4, NaN, NaN).join()"), + String::from("\"1,2,3\"") + ); + + forward(&mut context, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut context, "a.fill(4, 3, 5).join()"), + String::from("\"1,2,3\"") + ); + + forward(&mut context, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut context, "a.fill(4, '1.2', '2.5').join()"), + String::from("\"1,4,3\"") + ); + + forward(&mut context, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut context, "a.fill(4, 'str').join()"), + String::from("\"4,4,4\"") + ); + + forward(&mut context, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut context, "a.fill(4, 'str', 'str').join()"), + String::from("\"1,2,3\"") + ); + + forward(&mut context, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut context, "a.fill(4, undefined, null).join()"), + String::from("\"1,2,3\"") + ); + + forward(&mut context, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut context, "a.fill(4, undefined, undefined).join()"), + String::from("\"4,4,4\"") + ); + + assert_eq!( + forward(&mut context, "a.fill().join()"), + String::from("\",,\"") + ); + + // test object reference + forward(&mut context, "a = (new Array(3)).fill({});"); + forward(&mut context, "a[0].hi = 'hi';"); + assert_eq!(forward(&mut context, "a[0].hi"), String::from("\"hi\"")); +} + +#[test] +fn includes_value() { + let mut context = Context::default(); + let init = r#" + var empty = [ ]; + var one = ["a"]; + var many = ["a", "b", "c"]; + var duplicates = ["a", "b", "c", "a", "b"]; + var undefined = [undefined]; + "#; + eprintln!("{}", forward(&mut context, init)); + + // Empty + let empty = forward(&mut context, "empty.includes('a')"); + assert_eq!(empty, String::from("false")); + + // One + let one = forward(&mut context, "one.includes('a')"); + assert_eq!(one, String::from("true")); + // Missing from one + let missing_from_one = forward(&mut context, "one.includes('b')"); + assert_eq!(missing_from_one, String::from("false")); + + // In many + let first_in_many = forward(&mut context, "many.includes('c')"); + assert_eq!(first_in_many, String::from("true")); + // Missing from many + let second_in_many = forward(&mut context, "many.includes('d')"); + assert_eq!(second_in_many, String::from("false")); + + // In duplicates + let first_in_many = forward(&mut context, "duplicates.includes('a')"); + assert_eq!(first_in_many, String::from("true")); + // Missing from duplicates + let second_in_many = forward(&mut context, "duplicates.includes('d')"); + assert_eq!(second_in_many, String::from("false")); +} + +#[test] +fn map() { + let mut context = Context::default(); + + let js = r#" + var empty = []; + var one = ["x"]; + var many = ["x", "y", "z"]; + + var _this = { answer: 42 }; + + function callbackThatUsesThis() { + return 'The answer to life is: ' + this.answer; + } + + var empty_mapped = empty.map(v => v + '_'); + var one_mapped = one.map(v => '_' + v); + var many_mapped = many.map(v => '_' + v + '_'); + "#; + + forward(&mut context, js); + + // assert the old arrays have not been modified + assert_eq!(forward(&mut context, "one[0]"), String::from("\"x\"")); + assert_eq!( + forward(&mut context, "many[2] + many[1] + many[0]"), + String::from("\"zyx\"") + ); + + // NB: These tests need to be rewritten once `Display` has been implemented for `Array` + // Empty + assert_eq!( + forward(&mut context, "empty_mapped.length"), + String::from("0") + ); + + // One + assert_eq!( + forward(&mut context, "one_mapped.length"), + String::from("1") + ); + assert_eq!( + forward(&mut context, "one_mapped[0]"), + String::from("\"_x\"") + ); + + // Many + assert_eq!( + forward(&mut context, "many_mapped.length"), + String::from("3") + ); + assert_eq!( + forward( + &mut context, + "many_mapped[0] + many_mapped[1] + many_mapped[2]" + ), + String::from("\"_x__y__z_\"") + ); + + // One but it uses `this` inside the callback + let one_with_this = forward(&mut context, "one.map(callbackThatUsesThis, _this)[0];"); + assert_eq!(one_with_this, String::from("\"The answer to life is: 42\"")); +} + +#[test] +fn slice() { + let mut context = Context::default(); + let init = r#" + var empty = [ ].slice(); + var one = ["a"].slice(); + var many1 = ["a", "b", "c", "d"].slice(1); + var many2 = ["a", "b", "c", "d"].slice(2, 3); + var many3 = ["a", "b", "c", "d"].slice(7); + "#; + eprintln!("{}", forward(&mut context, init)); + + assert_eq!(forward(&mut context, "empty.length"), "0"); + assert_eq!(forward(&mut context, "one[0]"), "\"a\""); + assert_eq!(forward(&mut context, "many1[0]"), "\"b\""); + assert_eq!(forward(&mut context, "many1[1]"), "\"c\""); + assert_eq!(forward(&mut context, "many1[2]"), "\"d\""); + assert_eq!(forward(&mut context, "many1.length"), "3"); + assert_eq!(forward(&mut context, "many2[0]"), "\"c\""); + assert_eq!(forward(&mut context, "many2.length"), "1"); + assert_eq!(forward(&mut context, "many3.length"), "0"); +} + +#[test] +fn for_each() { + let mut context = Context::default(); + let init = r#" + var a = [2, 3, 4, 5]; + var sum = 0; + var indexSum = 0; + var listLengthSum = 0; + function callingCallback(item, index, list) { + sum += item; + indexSum += index; + listLengthSum += list.length; + } + a.forEach(callingCallback); + "#; + eprintln!("{}", forward(&mut context, init)); + + assert_eq!(forward(&mut context, "sum"), "14"); + assert_eq!(forward(&mut context, "indexSum"), "6"); + assert_eq!(forward(&mut context, "listLengthSum"), "16"); +} + +#[test] +fn for_each_push_value() { + let mut context = Context::default(); + let init = r#" + var a = [1, 2, 3, 4]; + function callingCallback(item, index, list) { + list.push(item * 2); + } + a.forEach(callingCallback); + "#; + eprintln!("{}", forward(&mut context, init)); + + // [ 1, 2, 3, 4, 2, 4, 6, 8 ] + assert_eq!(forward(&mut context, "a.length"), "8"); + assert_eq!(forward(&mut context, "a[4]"), "2"); + assert_eq!(forward(&mut context, "a[5]"), "4"); + assert_eq!(forward(&mut context, "a[6]"), "6"); + assert_eq!(forward(&mut context, "a[7]"), "8"); +} + +#[test] +fn filter() { + let mut context = Context::default(); + + let js = r#" + var empty = []; + var one = ["1"]; + var many = ["1", "0", "1"]; + + var empty_filtered = empty.filter(v => v === "1"); + var one_filtered = one.filter(v => v === "1"); + var zero_filtered = one.filter(v => v === "0"); + var many_one_filtered = many.filter(v => v === "1"); + var many_zero_filtered = many.filter(v => v === "0"); + "#; + + forward(&mut context, js); + + // assert the old arrays have not been modified + assert_eq!(forward(&mut context, "one[0]"), String::from("\"1\"")); + assert_eq!( + forward(&mut context, "many[2] + many[1] + many[0]"), + String::from("\"101\"") + ); + + // NB: These tests need to be rewritten once `Display` has been implemented for `Array` + // Empty + assert_eq!( + forward(&mut context, "empty_filtered.length"), + String::from("0") + ); + + // One filtered on "1" + assert_eq!( + forward(&mut context, "one_filtered.length"), + String::from("1") + ); + assert_eq!( + forward(&mut context, "one_filtered[0]"), + String::from("\"1\"") + ); + + // One filtered on "0" + assert_eq!( + forward(&mut context, "zero_filtered.length"), + String::from("0") + ); + + // Many filtered on "1" + assert_eq!( + forward(&mut context, "many_one_filtered.length"), + String::from("2") + ); + assert_eq!( + forward(&mut context, "many_one_filtered[0] + many_one_filtered[1]"), + String::from("\"11\"") + ); + + // Many filtered on "0" + assert_eq!( + forward(&mut context, "many_zero_filtered.length"), + String::from("1") + ); + assert_eq!( + forward(&mut context, "many_zero_filtered[0]"), + String::from("\"0\"") + ); +} + +#[test] +fn some() { + let mut context = Context::default(); + let init = r#" + var empty = []; + + var array = [11, 23, 45]; + function lessThan10(element) { + return element > 10; + } + function greaterThan10(element) { + return element < 10; + } + + // Cases where callback mutates the array. + var appendArray = [1,2,3,4]; + function appendingCallback(elem,index,arr) { + arr.push('new'); + return elem !== "new"; + } + + var delArray = [1,2,3,4]; + function deletingCallback(elem,index,arr) { + arr.pop() + return elem < 3; + } + "#; + forward(&mut context, init); + let result = forward(&mut context, "array.some(lessThan10);"); + assert_eq!(result, "true"); + + let result = forward(&mut context, "empty.some(lessThan10);"); + assert_eq!(result, "false"); + + let result = forward(&mut context, "array.some(greaterThan10);"); + assert_eq!(result, "false"); + + let result = forward(&mut context, "appendArray.some(appendingCallback);"); + let append_array_length = forward(&mut context, "appendArray.length"); + assert_eq!(append_array_length, "5"); + assert_eq!(result, "true"); + + let result = forward(&mut context, "delArray.some(deletingCallback);"); + let del_array_length = forward(&mut context, "delArray.length"); + assert_eq!(del_array_length, "3"); + assert_eq!(result, "true"); +} + +#[test] +fn reduce() { + let mut context = Context::default(); + + let init = r#" + var arr = [1, 2, 3, 4]; + function add(acc, x) { + return acc + x; + } + + function addIdx(acc, _, idx) { + return acc + idx; + } + + function addLen(acc, _x, _idx, arr) { + return acc + arr.length; + } + + function addResize(acc, x, idx, arr) { + if(idx == 0) { + arr.length = 3; + } + return acc + x; + } + var delArray = [1, 2, 3, 4, 5]; + delete delArray[0]; + delete delArray[1]; + delete delArray[3]; + + "#; + forward(&mut context, init); + + // empty array + let result = forward(&mut context, "[].reduce(add, 0)"); + assert_eq!(result, "0"); + + // simple with initial value + let result = forward(&mut context, "arr.reduce(add, 0)"); + assert_eq!(result, "10"); + + // without initial value + let result = forward(&mut context, "arr.reduce(add)"); + assert_eq!(result, "10"); + + // with some items missing + let result = forward(&mut context, "delArray.reduce(add, 0)"); + assert_eq!(result, "8"); + + // with index + let result = forward(&mut context, "arr.reduce(addIdx, 0)"); + assert_eq!(result, "6"); + + // with array + let result = forward(&mut context, "arr.reduce(addLen, 0)"); + assert_eq!(result, "16"); + + // resizing the array as reduce progresses + let result = forward(&mut context, "arr.reduce(addResize, 0)"); + assert_eq!(result, "6"); + + // Empty array + let result = forward( + &mut context, + r#" + try { + [].reduce((acc, x) => acc + x); + } catch(e) { + e.message + } + "#, + ); + assert_eq!( + result, + "\"Array.prototype.reduce: called on an empty array and with no initial value\"" + ); + + // Array with no defined elements + let result = forward( + &mut context, + r#" + try { + var arr = [0, 1]; + delete arr[0]; + delete arr[1]; + arr.reduce((acc, x) => acc + x); + } catch(e) { + e.message + } + "#, + ); + assert_eq!( + result, + "\"Array.prototype.reduce: called on an empty array and with no initial value\"" + ); + + // No callback + let result = forward( + &mut context, + r#" + try { + arr.reduce(""); + } catch(e) { + e.message + } + "#, + ); + assert_eq!( + result, + "\"Array.prototype.reduce: callback function is not callable\"" + ); +} + +#[test] +fn reduce_right() { + let mut context = Context::default(); + + let init = r#" + var arr = [1, 2, 3, 4]; + function sub(acc, x) { + return acc - x; + } + + function subIdx(acc, _, idx) { + return acc - idx; + } + + function subLen(acc, _x, _idx, arr) { + return acc - arr.length; + } + + function subResize(acc, x, idx, arr) { + if(idx == arr.length - 1) { + arr.length = 1; + } + return acc - x; + } + function subResize0(acc, x, idx, arr) { + if(idx == arr.length - 2) { + arr.length = 0; + } + return acc - x; + } + var delArray = [1, 2, 3, 4, 5]; + delete delArray[0]; + delete delArray[1]; + delete delArray[3]; + + "#; + forward(&mut context, init); + + // empty array + let result = forward(&mut context, "[].reduceRight(sub, 0)"); + assert_eq!(result, "0"); + + // simple with initial value + let result = forward(&mut context, "arr.reduceRight(sub, 0)"); + assert_eq!(result, "-10"); + + // without initial value + let result = forward(&mut context, "arr.reduceRight(sub)"); + assert_eq!(result, "-2"); + + // with some items missing + let result = forward(&mut context, "delArray.reduceRight(sub, 0)"); + assert_eq!(result, "-8"); + + // with index + let result = forward(&mut context, "arr.reduceRight(subIdx)"); + assert_eq!(result, "1"); + + // with array + let result = forward(&mut context, "arr.reduceRight(subLen)"); + assert_eq!(result, "-8"); + + // resizing the array as reduce progresses + let result = forward(&mut context, "arr.reduceRight(subResize, 0)"); + assert_eq!(result, "-5"); + + // reset array + forward(&mut context, "arr = [1, 2, 3, 4];"); + + // resizing the array to 0 as reduce progresses + let result = forward(&mut context, "arr.reduceRight(subResize0, 0)"); + assert_eq!(result, "-7"); + + // Empty array + let result = forward( + &mut context, + r#" + try { + [].reduceRight((acc, x) => acc + x); + } catch(e) { + e.message + } + "#, + ); + assert_eq!( + result, + "\"Array.prototype.reduceRight: called on an empty array and with no initial value\"" + ); + + // Array with no defined elements + let result = forward( + &mut context, + r#" + try { + var arr = [0, 1]; + delete arr[0]; + delete arr[1]; + arr.reduceRight((acc, x) => acc + x); + } catch(e) { + e.message + } + "#, + ); + assert_eq!( + result, + "\"Array.prototype.reduceRight: called on an empty array and with no initial value\"" + ); + + // No callback + let result = forward( + &mut context, + r#" + try { + arr.reduceRight(""); + } catch(e) { + e.message + } + "#, + ); + assert_eq!( + result, + "\"Array.prototype.reduceRight: callback function is not callable\"" + ); +} + +#[test] +fn call_array_constructor_with_one_argument() { + let mut context = Context::default(); + let init = r#" + var empty = new Array(0); + + var five = new Array(5); + + var one = new Array("Hello, world!"); + "#; + forward(&mut context, init); + // let result = forward(&mut context, "empty.length"); + // assert_eq!(result, "0"); + + // let result = forward(&mut context, "five.length"); + // assert_eq!(result, "5"); + + // let result = forward(&mut context, "one.length"); + // assert_eq!(result, "1"); +} + +#[test] +fn array_values_simple() { + let mut context = Context::default(); + let init = r#" + var iterator = [1, 2, 3].values(); + var next = iterator.next(); + "#; + forward(&mut context, init); + assert_eq!(forward(&mut context, "next.value"), "1"); + assert_eq!(forward(&mut context, "next.done"), "false"); + forward(&mut context, "next = iterator.next()"); + assert_eq!(forward(&mut context, "next.value"), "2"); + assert_eq!(forward(&mut context, "next.done"), "false"); + forward(&mut context, "next = iterator.next()"); + assert_eq!(forward(&mut context, "next.value"), "3"); + assert_eq!(forward(&mut context, "next.done"), "false"); + forward(&mut context, "next = iterator.next()"); + assert_eq!(forward(&mut context, "next.value"), "undefined"); + assert_eq!(forward(&mut context, "next.done"), "true"); +} + +#[test] +fn array_keys_simple() { + let mut context = Context::default(); + let init = r#" + var iterator = [1, 2, 3].keys(); + var next = iterator.next(); + "#; + forward(&mut context, init); + assert_eq!(forward(&mut context, "next.value"), "0"); + assert_eq!(forward(&mut context, "next.done"), "false"); + forward(&mut context, "next = iterator.next()"); + assert_eq!(forward(&mut context, "next.value"), "1"); + assert_eq!(forward(&mut context, "next.done"), "false"); + forward(&mut context, "next = iterator.next()"); + assert_eq!(forward(&mut context, "next.value"), "2"); + assert_eq!(forward(&mut context, "next.done"), "false"); + forward(&mut context, "next = iterator.next()"); + assert_eq!(forward(&mut context, "next.value"), "undefined"); + assert_eq!(forward(&mut context, "next.done"), "true"); +} + +#[test] +fn array_entries_simple() { + let mut context = Context::default(); + let init = r#" + var iterator = [1, 2, 3].entries(); + var next = iterator.next(); + "#; + forward(&mut context, init); + assert_eq!(forward(&mut context, "next.value"), "[ 0, 1 ]"); + assert_eq!(forward(&mut context, "next.done"), "false"); + forward(&mut context, "next = iterator.next()"); + assert_eq!(forward(&mut context, "next.value"), "[ 1, 2 ]"); + assert_eq!(forward(&mut context, "next.done"), "false"); + forward(&mut context, "next = iterator.next()"); + assert_eq!(forward(&mut context, "next.value"), "[ 2, 3 ]"); + assert_eq!(forward(&mut context, "next.done"), "false"); + forward(&mut context, "next = iterator.next()"); + assert_eq!(forward(&mut context, "next.value"), "undefined"); + assert_eq!(forward(&mut context, "next.done"), "true"); +} + +#[test] +fn array_values_empty() { + let mut context = Context::default(); + let init = r#" + var iterator = [].values(); + var next = iterator.next(); + "#; + forward(&mut context, init); + assert_eq!(forward(&mut context, "next.value"), "undefined"); + assert_eq!(forward(&mut context, "next.done"), "true"); +} + +#[test] +fn array_values_sparse() { + let mut context = Context::default(); + let init = r#" + var array = Array(); + array[3] = 5; + var iterator = array.values(); + var next = iterator.next(); + "#; + forward(&mut context, init); + assert_eq!(forward(&mut context, "next.value"), "undefined"); + assert_eq!(forward(&mut context, "next.done"), "false"); + forward(&mut context, "next = iterator.next()"); + assert_eq!(forward(&mut context, "next.value"), "undefined"); + assert_eq!(forward(&mut context, "next.done"), "false"); + forward(&mut context, "next = iterator.next()"); + assert_eq!(forward(&mut context, "next.value"), "undefined"); + assert_eq!(forward(&mut context, "next.done"), "false"); + forward(&mut context, "next = iterator.next()"); + assert_eq!(forward(&mut context, "next.value"), "5"); + assert_eq!(forward(&mut context, "next.done"), "false"); + forward(&mut context, "next = iterator.next()"); + assert_eq!(forward(&mut context, "next.value"), "undefined"); + assert_eq!(forward(&mut context, "next.done"), "true"); +} + +#[test] +fn array_symbol_iterator() { + let mut context = Context::default(); + let init = r#" + var iterator = [1, 2, 3][Symbol.iterator](); + var next = iterator.next(); + "#; + forward(&mut context, init); + assert_eq!(forward(&mut context, "next.value"), "1"); + assert_eq!(forward(&mut context, "next.done"), "false"); + forward(&mut context, "next = iterator.next()"); + assert_eq!(forward(&mut context, "next.value"), "2"); + assert_eq!(forward(&mut context, "next.done"), "false"); + forward(&mut context, "next = iterator.next()"); + assert_eq!(forward(&mut context, "next.value"), "3"); + assert_eq!(forward(&mut context, "next.done"), "false"); + forward(&mut context, "next = iterator.next()"); + assert_eq!(forward(&mut context, "next.value"), "undefined"); + assert_eq!(forward(&mut context, "next.done"), "true"); +} + +#[test] +fn array_values_symbol_iterator() { + let mut context = Context::default(); + let init = r#" + var iterator = [1, 2, 3].values(); + iterator === iterator[Symbol.iterator](); + "#; + assert_eq!(forward(&mut context, init), "true"); +} + +#[test] +fn array_spread_arrays() { + let mut context = Context::default(); + let init = r#" + const array1 = [2, 3]; + const array2 = [1, ...array1]; + array2[0] === 1 && array2[1] === 2 && array2[2] === 3; + "#; + assert_eq!(forward(&mut context, init), "true"); +} + +#[test] +fn array_spread_non_iterable() { + let mut context = Context::default(); + let init = r#" + try { + const array2 = [...5]; + } catch (err) { + err.name === "TypeError" && err.message === "value with type `number` is not iterable" + } + "#; + assert_eq!(forward(&mut context, init), "true"); +} + +#[test] +fn get_relative_start() { + let mut context = Context::default(); + + assert_eq!( + Array::get_relative_start(&mut context, None, 10).unwrap(), + 0 + ); + assert_eq!( + Array::get_relative_start(&mut context, Some(&JsValue::undefined()), 10).unwrap(), + 0 + ); + assert_eq!( + Array::get_relative_start(&mut context, Some(&JsValue::new(f64::NEG_INFINITY)), 10) + .unwrap(), + 0 + ); + assert_eq!( + Array::get_relative_start(&mut context, Some(&JsValue::new(f64::INFINITY)), 10).unwrap(), + 10 + ); + assert_eq!( + Array::get_relative_start(&mut context, Some(&JsValue::new(-1)), 10).unwrap(), + 9 + ); + assert_eq!( + Array::get_relative_start(&mut context, Some(&JsValue::new(1)), 10).unwrap(), + 1 + ); + assert_eq!( + Array::get_relative_start(&mut context, Some(&JsValue::new(-11)), 10).unwrap(), + 0 + ); + assert_eq!( + Array::get_relative_start(&mut context, Some(&JsValue::new(11)), 10).unwrap(), + 10 + ); + assert_eq!( + Array::get_relative_start(&mut context, Some(&JsValue::new(f64::MIN)), 10).unwrap(), + 0 + ); + assert_eq!( + Array::get_relative_start( + &mut context, + Some(&JsValue::new(Number::MIN_SAFE_INTEGER)), + 10 + ) + .unwrap(), + 0 + ); + assert_eq!( + Array::get_relative_start(&mut context, Some(&JsValue::new(f64::MAX)), 10).unwrap(), + 10 + ); + + // This test is relevant only on 32-bit archs (where usize == u32 thus `len` is u32) + assert_eq!( + Array::get_relative_start( + &mut context, + Some(&JsValue::new(Number::MAX_SAFE_INTEGER)), + 10 + ) + .unwrap(), + 10 + ); +} + +#[test] +fn get_relative_end() { + let mut context = Context::default(); + + assert_eq!(Array::get_relative_end(&mut context, None, 10).unwrap(), 10); + assert_eq!( + Array::get_relative_end(&mut context, Some(&JsValue::undefined()), 10).unwrap(), + 10 + ); + assert_eq!( + Array::get_relative_end(&mut context, Some(&JsValue::new(f64::NEG_INFINITY)), 10).unwrap(), + 0 + ); + assert_eq!( + Array::get_relative_end(&mut context, Some(&JsValue::new(f64::INFINITY)), 10).unwrap(), + 10 + ); + assert_eq!( + Array::get_relative_end(&mut context, Some(&JsValue::new(-1)), 10).unwrap(), + 9 + ); + assert_eq!( + Array::get_relative_end(&mut context, Some(&JsValue::new(1)), 10).unwrap(), + 1 + ); + assert_eq!( + Array::get_relative_end(&mut context, Some(&JsValue::new(-11)), 10).unwrap(), + 0 + ); + assert_eq!( + Array::get_relative_end(&mut context, Some(&JsValue::new(11)), 10).unwrap(), + 10 + ); + assert_eq!( + Array::get_relative_end(&mut context, Some(&JsValue::new(f64::MIN)), 10).unwrap(), + 0 + ); + assert_eq!( + Array::get_relative_end( + &mut context, + Some(&JsValue::new(Number::MIN_SAFE_INTEGER)), + 10 + ) + .unwrap(), + 0 + ); + assert_eq!( + Array::get_relative_end(&mut context, Some(&JsValue::new(f64::MAX)), 10).unwrap(), + 10 + ); + + // This test is relevant only on 32-bit archs (where usize == u32 thus `len` is u32) + assert_eq!( + Array::get_relative_end( + &mut context, + Some(&JsValue::new(Number::MAX_SAFE_INTEGER)), + 10 + ) + .unwrap(), + 10 + ); +} + +#[test] +fn array_length_is_not_enumerable() { + let mut context = Context::default(); + + let array = + Array::array_create(0, None, &mut context).expect("could not create an empty array"); + let desc = array + .__get_own_property__(&"length".into(), &mut context) + .expect("accessing length property on array should not throw") + .expect("there should always be a length property on arrays"); + assert!(!desc.expect_enumerable()); +} + +#[test] +fn array_sort() { + let mut context = Context::default(); + let init = r#" + let arr = ['80', '9', '700', 40, 1, 5, 200]; + + function compareNumbers(a, b) { + return a - b; + } + "#; + forward(&mut context, init); + assert_eq!( + forward(&mut context, "arr.sort().join()"), + "\"1,200,40,5,700,80,9\"" + ); + assert_eq!( + forward(&mut context, "arr.sort(compareNumbers).join()"), + "\"1,5,9,40,80,200,700\"" + ); +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/array_buffer/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/array_buffer/mod.rs new file mode 100644 index 0000000..d054642 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/array_buffer/mod.rs @@ -0,0 +1,842 @@ +//! Boa's implementation of ECMAScript's global `ArrayBuffer` object. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! - [MDN documentation][mdn] +//! +//! [spec]: https://tc39.es/ecma262/#sec-arraybuffer-objects +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer + +#[cfg(test)] +mod tests; + +use crate::{ + builtins::{typed_array::TypedArrayKind, BuiltIn, JsArgs}, + context::intrinsics::StandardConstructors, + error::JsNativeError, + native_function::NativeFunction, + object::{ + internal_methods::get_prototype_from_constructor, ConstructorBuilder, + FunctionObjectBuilder, JsObject, ObjectData, + }, + property::Attribute, + symbol::JsSymbol, + value::{IntegerOrInfinity, Numeric}, + Context, JsResult, JsValue, +}; +use boa_gc::{Finalize, Trace}; +use boa_profiler::Profiler; +use num_traits::{Signed, ToPrimitive}; +use tap::{Conv, Pipe}; + +/// The internal representation of an `ArrayBuffer` object. +#[derive(Debug, Clone, Trace, Finalize)] +pub struct ArrayBuffer { + /// The `[[ArrayBufferData]]` internal slot. + pub array_buffer_data: Option>, + + /// The `[[ArrayBufferByteLength]]` internal slot. + pub array_buffer_byte_length: u64, + + /// The `[[ArrayBufferDetachKey]]` internal slot. + pub array_buffer_detach_key: JsValue, +} + +impl ArrayBuffer { + pub(crate) const fn array_buffer_byte_length(&self) -> u64 { + self.array_buffer_byte_length + } +} + +impl BuiltIn for ArrayBuffer { + const NAME: &'static str = "ArrayBuffer"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE; + + let get_species = + FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(Self::get_species)) + .name("get [Symbol.species]") + .constructor(false) + .build(); + + let get_byte_length = + FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(Self::get_byte_length)) + .name("get byteLength") + .build(); + + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context.intrinsics().constructors().array_buffer().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .accessor("byteLength", Some(get_byte_length), None, flag_attributes) + .static_accessor( + JsSymbol::species(), + Some(get_species), + None, + Attribute::CONFIGURABLE, + ) + .static_method(Self::is_view, "isView", 1) + .method(Self::slice, "slice", 2) + .property( + JsSymbol::to_string_tag(), + Self::NAME, + Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ) + .build() + .conv::() + .pipe(Some) + } +} + +impl ArrayBuffer { + const LENGTH: usize = 1; + + /// `25.1.3.1 ArrayBuffer ( length )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-arraybuffer-length + fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. If NewTarget is undefined, throw a TypeError exception. + if new_target.is_undefined() { + return Err(JsNativeError::typ() + .with_message("ArrayBuffer.constructor called with undefined new target") + .into()); + } + + // 2. Let byteLength be ? ToIndex(length). + let byte_length = args.get_or_undefined(0).to_index(context)?; + + // 3. Return ? AllocateArrayBuffer(NewTarget, byteLength). + Ok(Self::allocate(new_target, byte_length, context)?.into()) + } + + /// `25.1.4.3 get ArrayBuffer [ @@species ]` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-get-arraybuffer-@@species + #[allow(clippy::unnecessary_wraps)] + fn get_species(this: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult { + // 1. Return the this value. + Ok(this.clone()) + } + + /// `25.1.4.1 ArrayBuffer.isView ( arg )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-arraybuffer.isview + #[allow(clippy::unnecessary_wraps)] + fn is_view(_: &JsValue, args: &[JsValue], _context: &mut Context<'_>) -> JsResult { + // 1. If Type(arg) is not Object, return false. + // 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true. + // 3. Return false. + Ok(args + .get_or_undefined(0) + .as_object() + .map(|obj| obj.borrow().has_viewed_array_buffer()) + .unwrap_or_default() + .into()) + } + + /// `25.1.5.1 get ArrayBuffer.prototype.byteLength` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.bytelength + pub(crate) fn get_byte_length( + this: &JsValue, + _args: &[JsValue], + _: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be the this value. + // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]). + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("ArrayBuffer.byteLength called with non-object value") + })?; + let obj = obj.borrow(); + let buf = obj.as_array_buffer().ok_or_else(|| { + JsNativeError::typ().with_message("ArrayBuffer.byteLength called with invalid object") + })?; + + // TODO: Shared Array Buffer + // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception. + + // 4. If IsDetachedBuffer(O) is true, return +0𝔽. + if Self::is_detached_buffer(buf) { + return Ok(0.into()); + } + + // 5. Let length be O.[[ArrayBufferByteLength]]. + // 6. Return 𝔽(length). + Ok(buf.array_buffer_byte_length.into()) + } + + /// `25.1.5.3 ArrayBuffer.prototype.slice ( start, end )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-arraybuffer.prototype.slice + fn slice(this: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResult { + // 1. Let O be the this value. + // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]). + let obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("ArrayBuffer.slice called with non-object value") + })?; + let obj_borrow = obj.borrow(); + let buf = obj_borrow.as_array_buffer().ok_or_else(|| { + JsNativeError::typ().with_message("ArrayBuffer.slice called with invalid object") + })?; + + // TODO: Shared Array Buffer + // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception. + + // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception. + if Self::is_detached_buffer(buf) { + return Err(JsNativeError::typ() + .with_message("ArrayBuffer.slice called with detached buffer") + .into()); + } + + // 5. Let len be O.[[ArrayBufferByteLength]]. + let len = buf.array_buffer_byte_length as i64; + + // 6. Let relativeStart be ? ToIntegerOrInfinity(start). + let relative_start = args.get_or_undefined(0).to_integer_or_infinity(context)?; + + let first = match relative_start { + // 7. If relativeStart is -∞, let first be 0. + IntegerOrInfinity::NegativeInfinity => 0, + // 8. Else if relativeStart < 0, let first be max(len + relativeStart, 0). + IntegerOrInfinity::Integer(i) if i < 0 => std::cmp::max(len + i, 0), + // 9. Else, let first be min(relativeStart, len). + IntegerOrInfinity::Integer(i) => std::cmp::min(i, len), + IntegerOrInfinity::PositiveInfinity => len, + }; + + // 10. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end). + let end = args.get_or_undefined(1); + let relative_end = if end.is_undefined() { + IntegerOrInfinity::Integer(len) + } else { + end.to_integer_or_infinity(context)? + }; + + let r#final = match relative_end { + // 11. If relativeEnd is -∞, let final be 0. + IntegerOrInfinity::NegativeInfinity => 0, + // 12. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0). + IntegerOrInfinity::Integer(i) if i < 0 => std::cmp::max(len + i, 0), + // 13. Else, let final be min(relativeEnd, len). + IntegerOrInfinity::Integer(i) => std::cmp::min(i, len), + IntegerOrInfinity::PositiveInfinity => len, + }; + + // 14. Let newLen be max(final - first, 0). + let new_len = std::cmp::max(r#final - first, 0) as u64; + + // 15. Let ctor be ? SpeciesConstructor(O, %ArrayBuffer%). + let ctor = obj.species_constructor(StandardConstructors::array_buffer, context)?; + + // 16. Let new be ? Construct(ctor, « 𝔽(newLen) »). + let new = ctor.construct(&[new_len.into()], Some(&ctor), context)?; + + { + let new_obj = new.borrow(); + // 17. Perform ? RequireInternalSlot(new, [[ArrayBufferData]]). + let new_array_buffer = new_obj.as_array_buffer().ok_or_else(|| { + JsNativeError::typ().with_message("ArrayBuffer constructor returned invalid object") + })?; + + // TODO: Shared Array Buffer + // 18. If IsSharedArrayBuffer(new) is true, throw a TypeError exception. + + // 19. If IsDetachedBuffer(new) is true, throw a TypeError exception. + if new_array_buffer.is_detached_buffer() { + return Err(JsNativeError::typ() + .with_message("ArrayBuffer constructor returned detached ArrayBuffer") + .into()); + } + } + // 20. If SameValue(new, O) is true, throw a TypeError exception. + if this + .as_object() + .map(|obj| JsObject::equals(obj, &new)) + .unwrap_or_default() + { + return Err(JsNativeError::typ() + .with_message("New ArrayBuffer is the same as this ArrayBuffer") + .into()); + } + + { + let mut new_obj_borrow = new.borrow_mut(); + let new_array_buffer = new_obj_borrow + .as_array_buffer_mut() + .expect("Already checked that `new_obj` was an `ArrayBuffer`"); + + // 21. If new.[[ArrayBufferByteLength]] < newLen, throw a TypeError exception. + if new_array_buffer.array_buffer_byte_length < new_len { + return Err(JsNativeError::typ() + .with_message("New ArrayBuffer length too small") + .into()); + } + + // 22. NOTE: Side-effects of the above steps may have detached O. + // 23. If IsDetachedBuffer(O) is true, throw a TypeError exception. + if Self::is_detached_buffer(buf) { + return Err(JsNativeError::typ() + .with_message("ArrayBuffer detached while ArrayBuffer.slice was running") + .into()); + } + + // 24. Let fromBuf be O.[[ArrayBufferData]]. + let from_buf = buf + .array_buffer_data + .as_ref() + .expect("ArrayBuffer cannot be detached here"); + + // 25. Let toBuf be new.[[ArrayBufferData]]. + let to_buf = new_array_buffer + .array_buffer_data + .as_mut() + .expect("ArrayBuffer cannot be detached here"); + + // 26. Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen). + copy_data_block_bytes(to_buf, 0, from_buf, first as usize, new_len as usize); + } + + // 27. Return new. + Ok(new.into()) + } + + /// `25.1.2.1 AllocateArrayBuffer ( constructor, byteLength )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-allocatearraybuffer + pub(crate) fn allocate( + constructor: &JsValue, + byte_length: u64, + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%ArrayBuffer.prototype%", « [[ArrayBufferData]], [[ArrayBufferByteLength]], [[ArrayBufferDetachKey]] »). + let prototype = get_prototype_from_constructor( + constructor, + StandardConstructors::array_buffer, + context, + )?; + + // 2. Let block be ? CreateByteDataBlock(byteLength). + let block = create_byte_data_block(byte_length)?; + + // 3. Set obj.[[ArrayBufferData]] to block. + // 4. Set obj.[[ArrayBufferByteLength]] to byteLength. + let obj = JsObject::from_proto_and_data( + prototype, + ObjectData::array_buffer(Self { + array_buffer_data: Some(block), + array_buffer_byte_length: byte_length, + array_buffer_detach_key: JsValue::Undefined, + }), + ); + + // 5. Return obj. + Ok(obj) + } + + /// `25.1.2.2 IsDetachedBuffer ( arrayBuffer )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-isdetachedbuffer + pub(crate) const fn is_detached_buffer(&self) -> bool { + // 1. If arrayBuffer.[[ArrayBufferData]] is null, return true. + // 2. Return false. + self.array_buffer_data.is_none() + } + + /// `25.1.2.4 CloneArrayBuffer ( srcBuffer, srcByteOffset, srcLength, cloneConstructor )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-clonearraybuffer + pub(crate) fn clone_array_buffer( + &self, + src_byte_offset: u64, + src_length: u64, + clone_constructor: &JsValue, + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let targetBuffer be ? AllocateArrayBuffer(cloneConstructor, srcLength). + let target_buffer = Self::allocate(clone_constructor, src_length, context)?; + + // 2. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. + // 3. Let srcBlock be srcBuffer.[[ArrayBufferData]]. + let src_block = self.array_buffer_data.as_deref().ok_or_else(|| { + JsNativeError::syntax().with_message("Cannot clone detached array buffer") + })?; + + { + // 4. Let targetBlock be targetBuffer.[[ArrayBufferData]]. + let mut target_buffer_mut = target_buffer.borrow_mut(); + let target_block = target_buffer_mut + .as_array_buffer_mut() + .expect("This must be an ArrayBuffer"); + + // 5. Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, srcLength). + copy_data_block_bytes( + target_block + .array_buffer_data + .as_mut() + .expect("ArrayBuffer cannot me detached here"), + 0, + src_block, + src_byte_offset as usize, + src_length as usize, + ); + } + + // 6. Return targetBuffer. + Ok(target_buffer) + } + + /// `25.1.2.6 IsUnclampedIntegerElementType ( type )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-isunclampedintegerelementtype + const fn is_unclamped_integer_element_type(t: TypedArrayKind) -> bool { + // 1. If type is Int8, Uint8, Int16, Uint16, Int32, or Uint32, return true. + // 2. Return false. + matches!( + t, + TypedArrayKind::Int8 + | TypedArrayKind::Uint8 + | TypedArrayKind::Int16 + | TypedArrayKind::Uint16 + | TypedArrayKind::Int32 + | TypedArrayKind::Uint32 + ) + } + + /// `25.1.2.7 IsBigIntElementType ( type )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-isbigintelementtype + const fn is_big_int_element_type(t: TypedArrayKind) -> bool { + // 1. If type is BigUint64 or BigInt64, return true. + // 2. Return false. + matches!(t, TypedArrayKind::BigUint64 | TypedArrayKind::BigInt64) + } + + /// `25.1.2.8 IsNoTearConfiguration ( type, order )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-isnotearconfiguration + // TODO: Allow unused function until shared array buffers are implemented. + #[allow(dead_code)] + const fn is_no_tear_configuration(t: TypedArrayKind, order: SharedMemoryOrder) -> bool { + // 1. If ! IsUnclampedIntegerElementType(type) is true, return true. + if Self::is_unclamped_integer_element_type(t) { + return true; + } + + // 2. If ! IsBigIntElementType(type) is true and order is not Init or Unordered, return true. + if Self::is_big_int_element_type(t) + && !matches!( + order, + SharedMemoryOrder::Init | SharedMemoryOrder::Unordered + ) + { + return true; + } + + // 3. Return false. + false + } + + /// `25.1.2.9 RawBytesToNumeric ( type, rawBytes, isLittleEndian )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-rawbytestonumeric + fn raw_bytes_to_numeric(t: TypedArrayKind, bytes: &[u8], is_little_endian: bool) -> JsValue { + let n: Numeric = match t { + TypedArrayKind::Int8 => { + if is_little_endian { + i8::from_le_bytes(bytes.try_into().expect("slice with incorrect length")).into() + } else { + i8::from_be_bytes(bytes.try_into().expect("slice with incorrect length")).into() + } + } + TypedArrayKind::Uint8 | TypedArrayKind::Uint8Clamped => { + if is_little_endian { + u8::from_le_bytes(bytes.try_into().expect("slice with incorrect length")).into() + } else { + u8::from_be_bytes(bytes.try_into().expect("slice with incorrect length")).into() + } + } + TypedArrayKind::Int16 => { + if is_little_endian { + i16::from_le_bytes(bytes.try_into().expect("slice with incorrect length")) + .into() + } else { + i16::from_be_bytes(bytes.try_into().expect("slice with incorrect length")) + .into() + } + } + TypedArrayKind::Uint16 => { + if is_little_endian { + u16::from_le_bytes(bytes.try_into().expect("slice with incorrect length")) + .into() + } else { + u16::from_be_bytes(bytes.try_into().expect("slice with incorrect length")) + .into() + } + } + TypedArrayKind::Int32 => { + if is_little_endian { + i32::from_le_bytes(bytes.try_into().expect("slice with incorrect length")) + .into() + } else { + i32::from_be_bytes(bytes.try_into().expect("slice with incorrect length")) + .into() + } + } + TypedArrayKind::Uint32 => { + if is_little_endian { + u32::from_le_bytes(bytes.try_into().expect("slice with incorrect length")) + .into() + } else { + u32::from_be_bytes(bytes.try_into().expect("slice with incorrect length")) + .into() + } + } + TypedArrayKind::BigInt64 => { + if is_little_endian { + i64::from_le_bytes(bytes.try_into().expect("slice with incorrect length")) + .into() + } else { + i64::from_be_bytes(bytes.try_into().expect("slice with incorrect length")) + .into() + } + } + TypedArrayKind::BigUint64 => { + if is_little_endian { + u64::from_le_bytes(bytes.try_into().expect("slice with incorrect length")) + .into() + } else { + u64::from_be_bytes(bytes.try_into().expect("slice with incorrect length")) + .into() + } + } + TypedArrayKind::Float32 => { + if is_little_endian { + f32::from_le_bytes(bytes.try_into().expect("slice with incorrect length")) + .into() + } else { + f32::from_be_bytes(bytes.try_into().expect("slice with incorrect length")) + .into() + } + } + TypedArrayKind::Float64 => { + if is_little_endian { + f64::from_le_bytes(bytes.try_into().expect("slice with incorrect length")) + .into() + } else { + f64::from_be_bytes(bytes.try_into().expect("slice with incorrect length")) + .into() + } + } + }; + + n.into() + } + + /// `25.1.2.10 GetValueFromBuffer ( arrayBuffer, byteIndex, type, isTypedArray, order [ , isLittleEndian ] )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-getvaluefrombuffer + pub(crate) fn get_value_from_buffer( + &self, + byte_index: u64, + t: TypedArrayKind, + _is_typed_array: bool, + _order: SharedMemoryOrder, + is_little_endian: Option, + ) -> JsValue { + // 1. Assert: IsDetachedBuffer(arrayBuffer) is false. + // 2. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type. + // 3. Let block be arrayBuffer.[[ArrayBufferData]]. + let block = self + .array_buffer_data + .as_ref() + .expect("ArrayBuffer cannot be detached here"); + + // 4. Let elementSize be the Element Size value specified in Table 73 for Element Type type. + let element_size = t.element_size() as usize; + + // TODO: Shared Array Buffer + // 5. If IsSharedArrayBuffer(arrayBuffer) is true, then + + // 6. Else, let rawValue be a List whose elements are bytes from block at indices byteIndex (inclusive) through byteIndex + elementSize (exclusive). + // 7. Assert: The number of elements in rawValue is elementSize. + let byte_index = byte_index as usize; + let raw_value = &block[byte_index..byte_index + element_size]; + + // TODO: Agent Record [[LittleEndian]] filed + // 8. If isLittleEndian is not present, set isLittleEndian to the value of the [[LittleEndian]] field of the surrounding agent's Agent Record. + let is_little_endian = is_little_endian.unwrap_or(true); + + // 9. Return RawBytesToNumeric(type, rawValue, isLittleEndian). + Self::raw_bytes_to_numeric(t, raw_value, is_little_endian) + } + + /// `25.1.2.11 NumericToRawBytes ( type, value, isLittleEndian )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-numerictorawbytes + fn numeric_to_raw_bytes( + t: TypedArrayKind, + value: &JsValue, + is_little_endian: bool, + context: &mut Context<'_>, + ) -> JsResult> { + Ok(match t { + TypedArrayKind::Int8 if is_little_endian => { + value.to_int8(context)?.to_le_bytes().to_vec() + } + TypedArrayKind::Int8 => value.to_int8(context)?.to_be_bytes().to_vec(), + TypedArrayKind::Uint8 if is_little_endian => { + value.to_uint8(context)?.to_le_bytes().to_vec() + } + TypedArrayKind::Uint8 => value.to_uint8(context)?.to_be_bytes().to_vec(), + TypedArrayKind::Uint8Clamped if is_little_endian => { + value.to_uint8_clamp(context)?.to_le_bytes().to_vec() + } + TypedArrayKind::Uint8Clamped => value.to_uint8_clamp(context)?.to_be_bytes().to_vec(), + TypedArrayKind::Int16 if is_little_endian => { + value.to_int16(context)?.to_le_bytes().to_vec() + } + TypedArrayKind::Int16 => value.to_int16(context)?.to_be_bytes().to_vec(), + TypedArrayKind::Uint16 if is_little_endian => { + value.to_uint16(context)?.to_le_bytes().to_vec() + } + TypedArrayKind::Uint16 => value.to_uint16(context)?.to_be_bytes().to_vec(), + TypedArrayKind::Int32 if is_little_endian => { + value.to_i32(context)?.to_le_bytes().to_vec() + } + TypedArrayKind::Int32 => value.to_i32(context)?.to_be_bytes().to_vec(), + TypedArrayKind::Uint32 if is_little_endian => { + value.to_u32(context)?.to_le_bytes().to_vec() + } + TypedArrayKind::Uint32 => value.to_u32(context)?.to_be_bytes().to_vec(), + TypedArrayKind::BigInt64 if is_little_endian => { + let big_int = value.to_big_int64(context)?; + big_int + .to_i64() + .unwrap_or_else(|| { + if big_int.is_positive() { + i64::MAX + } else { + i64::MIN + } + }) + .to_le_bytes() + .to_vec() + } + TypedArrayKind::BigInt64 => { + let big_int = value.to_big_int64(context)?; + big_int + .to_i64() + .unwrap_or_else(|| { + if big_int.is_positive() { + i64::MAX + } else { + i64::MIN + } + }) + .to_be_bytes() + .to_vec() + } + TypedArrayKind::BigUint64 if is_little_endian => value + .to_big_uint64(context)? + .to_u64() + .unwrap_or(u64::MAX) + .to_le_bytes() + .to_vec(), + TypedArrayKind::BigUint64 => value + .to_big_uint64(context)? + .to_u64() + .unwrap_or(u64::MAX) + .to_be_bytes() + .to_vec(), + TypedArrayKind::Float32 => match value.to_number(context)? { + f if is_little_endian => (f as f32).to_le_bytes().to_vec(), + f => (f as f32).to_be_bytes().to_vec(), + }, + TypedArrayKind::Float64 => match value.to_number(context)? { + f if is_little_endian => f.to_le_bytes().to_vec(), + f => f.to_be_bytes().to_vec(), + }, + }) + } + + /// `25.1.2.12 SetValueInBuffer ( arrayBuffer, byteIndex, type, value, isTypedArray, order [ , isLittleEndian ] )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-setvalueinbuffer + pub(crate) fn set_value_in_buffer( + &mut self, + byte_index: u64, + t: TypedArrayKind, + value: &JsValue, + _order: SharedMemoryOrder, + is_little_endian: Option, + context: &mut Context<'_>, + ) -> JsResult { + // 1. Assert: IsDetachedBuffer(arrayBuffer) is false. + // 2. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type. + // 3. Assert: Type(value) is BigInt if ! IsBigIntElementType(type) is true; otherwise, Type(value) is Number. + // 4. Let block be arrayBuffer.[[ArrayBufferData]]. + let block = self + .array_buffer_data + .as_mut() + .expect("ArrayBuffer cannot be detached here"); + + // 5. Let elementSize be the Element Size value specified in Table 73 for Element Type type. + + // TODO: Agent Record [[LittleEndian]] filed + // 6. If isLittleEndian is not present, set isLittleEndian to the value of the [[LittleEndian]] field of the surrounding agent's Agent Record. + let is_little_endian = is_little_endian.unwrap_or(true); + + // 7. Let rawBytes be NumericToRawBytes(type, value, isLittleEndian). + let raw_bytes = Self::numeric_to_raw_bytes(t, value, is_little_endian, context)?; + + // TODO: Shared Array Buffer + // 8. If IsSharedArrayBuffer(arrayBuffer) is true, then + + // 9. Else, store the individual bytes of rawBytes into block, starting at block[byteIndex]. + for (i, raw_byte) in raw_bytes.iter().enumerate() { + block[byte_index as usize + i] = *raw_byte; + } + + // 10. Return NormalCompletion(undefined). + Ok(JsValue::undefined()) + } +} + +/// `CreateByteDataBlock ( size )` abstract operation. +/// +/// The abstract operation `CreateByteDataBlock` takes argument `size` (a non-negative +/// integer). For more information, check the [spec][spec]. +/// +/// [spec]: https://tc39.es/ecma262/#sec-createbytedatablock +pub fn create_byte_data_block(size: u64) -> JsResult> { + // 1. Let db be a new Data Block value consisting of size bytes. If it is impossible to + // create such a Data Block, throw a RangeError exception. + let size = size.try_into().map_err(|e| { + JsNativeError::range().with_message(format!("couldn't allocate the data block: {e}")) + })?; + + let mut data_block = Vec::new(); + data_block.try_reserve(size).map_err(|e| { + JsNativeError::range().with_message(format!("couldn't allocate the data block: {e}")) + })?; + + // 2. Set all of the bytes of db to 0. + data_block.resize(size, 0); + + // 3. Return db. + Ok(data_block) +} + +/// `6.2.8.3 CopyDataBlockBytes ( toBlock, toIndex, fromBlock, fromIndex, count )` +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-copydatablockbytes +fn copy_data_block_bytes( + to_block: &mut [u8], + mut to_index: usize, + from_block: &[u8], + mut from_index: usize, + mut count: usize, +) { + // 1. Assert: fromBlock and toBlock are distinct values. + // 2. Let fromSize be the number of bytes in fromBlock. + let from_size = from_block.len(); + + // 3. Assert: fromIndex + count ≤ fromSize. + assert!(from_index + count <= from_size); + + // 4. Let toSize be the number of bytes in toBlock. + let to_size = to_block.len(); + + // 5. Assert: toIndex + count ≤ toSize. + assert!(to_index + count <= to_size); + + // 6. Repeat, while count > 0, + while count > 0 { + // a. If fromBlock is a Shared Data Block, then + // TODO: Shared Data Block + + // b. Else, + // i. Assert: toBlock is not a Shared Data Block. + // ii. Set toBlock[toIndex] to fromBlock[fromIndex]. + to_block[to_index] = from_block[from_index]; + + // c. Set toIndex to toIndex + 1. + to_index += 1; + + // d. Set fromIndex to fromIndex + 1. + from_index += 1; + + // e. Set count to count - 1. + count -= 1; + } + + // 7. Return NormalCompletion(empty). +} + +// TODO: Allow unused variants until shared array buffers are implemented. +#[allow(dead_code)] +#[derive(Debug, PartialEq, Clone, Copy)] +pub(crate) enum SharedMemoryOrder { + Init, + SeqCst, + Unordered, +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/array_buffer/tests.rs b/javascript-engine/external/boa/boa_engine/src/builtins/array_buffer/tests.rs new file mode 100644 index 0000000..ae4bcd7 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/array_buffer/tests.rs @@ -0,0 +1,11 @@ +use super::*; + +#[test] +fn ut_sunny_day_create_byte_data_block() { + assert!(create_byte_data_block(100).is_ok()); +} + +#[test] +fn ut_rainy_day_create_byte_data_block() { + assert!(create_byte_data_block(u64::MAX).is_err()); +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/async_function/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/async_function/mod.rs new file mode 100644 index 0000000..e2d0fae --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/async_function/mod.rs @@ -0,0 +1,109 @@ +//! Boa's implementation of ECMAScript's global `AsyncFunction` object. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! - [MDN documentation][mdn] +//! +//! [spec]: https://tc39.es/ecma262/#sec-async-function-objects +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncFunction + +use crate::{ + builtins::{ + function::{ConstructorKind, Function}, + BuiltIn, + }, + native_function::NativeFunction, + object::ObjectData, + property::PropertyDescriptor, + symbol::JsSymbol, + Context, JsResult, JsValue, +}; +use boa_profiler::Profiler; + +/// The internal representation of an `AsyncFunction` object. +#[derive(Debug, Clone, Copy)] +pub struct AsyncFunction; + +impl BuiltIn for AsyncFunction { + const NAME: &'static str = "AsyncFunction"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let prototype = &context + .intrinsics() + .constructors() + .async_function() + .prototype; + let constructor = &context + .intrinsics() + .constructors() + .async_function() + .constructor; + + constructor.set_prototype(Some( + context.intrinsics().constructors().function().constructor(), + )); + let property = PropertyDescriptor::builder() + .value(1) + .writable(false) + .enumerable(false) + .configurable(true); + constructor.borrow_mut().insert("length", property); + let property = PropertyDescriptor::builder() + .value(Self::NAME) + .writable(false) + .enumerable(false) + .configurable(true); + constructor.borrow_mut().insert("name", property); + let property = PropertyDescriptor::builder() + .value(prototype.clone()) + .writable(false) + .enumerable(false) + .configurable(false); + constructor.borrow_mut().insert("prototype", property); + constructor.borrow_mut().data = ObjectData::function(Function::Native { + function: NativeFunction::from_fn_ptr(Self::constructor), + constructor: Some(ConstructorKind::Base), + }); + + prototype.set_prototype(Some( + context.intrinsics().constructors().function().prototype(), + )); + let property = PropertyDescriptor::builder() + .value(constructor.clone()) + .writable(false) + .enumerable(false) + .configurable(true); + prototype.borrow_mut().insert("constructor", property); + let property = PropertyDescriptor::builder() + .value(Self::NAME) + .writable(false) + .enumerable(false) + .configurable(true); + prototype + .borrow_mut() + .insert(JsSymbol::to_string_tag(), property); + + None + } +} + +impl AsyncFunction { + /// `AsyncFunction ( p1, p2, … , pn, body )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-async-function-constructor-arguments + fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + crate::builtins::function::BuiltInFunctionObject::create_dynamic_function( + new_target, args, true, false, context, + ) + .map(Into::into) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/async_generator/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/async_generator/mod.rs new file mode 100644 index 0000000..612fe83 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/async_generator/mod.rs @@ -0,0 +1,777 @@ +//! Boa's implementation of ECMAScript's global `AsyncGenerator` object. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-asyncgenerator-objects + +use crate::{ + builtins::{ + generator::GeneratorContext, iterable::create_iter_result_object, + promise::if_abrupt_reject_promise, promise::PromiseCapability, BuiltIn, JsArgs, Promise, + }, + error::JsNativeError, + native_function::NativeFunction, + object::{ConstructorBuilder, FunctionObjectBuilder, JsObject, ObjectData}, + property::{Attribute, PropertyDescriptor}, + symbol::JsSymbol, + value::JsValue, + vm::GeneratorResumeKind, + Context, JsError, JsResult, +}; +use boa_gc::{Finalize, Gc, GcCell, Trace}; +use boa_profiler::Profiler; +use std::collections::VecDeque; + +/// Indicates the state of an async generator. +#[derive(Debug, Clone, Copy, PartialEq)] +pub(crate) enum AsyncGeneratorState { + Undefined, + SuspendedStart, + SuspendedYield, + Executing, + AwaitingReturn, + Completed, +} + +/// `AsyncGeneratorRequest Records` +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorrequest-records +#[derive(Debug, Clone, Finalize, Trace)] +pub(crate) struct AsyncGeneratorRequest { + /// The `[[Completion]]` slot. + pub(crate) completion: (JsResult, bool), + + /// The `[[Capability]]` slot. + capability: PromiseCapability, +} + +/// The internal representation of an `AsyncGenerator` object. +#[derive(Debug, Clone, Finalize, Trace)] +pub struct AsyncGenerator { + /// The `[[AsyncGeneratorState]]` internal slot. + #[unsafe_ignore_trace] + pub(crate) state: AsyncGeneratorState, + + /// The `[[AsyncGeneratorContext]]` internal slot. + pub(crate) context: Option>>, + + /// The `[[AsyncGeneratorQueue]]` internal slot. + pub(crate) queue: VecDeque, +} + +impl BuiltIn for AsyncGenerator { + const NAME: &'static str = "AsyncGenerator"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let iterator_prototype = context + .intrinsics() + .objects() + .iterator_prototypes() + .async_iterator_prototype(); + + let generator_function_prototype = context + .intrinsics() + .constructors() + .async_generator_function() + .prototype(); + + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context + .intrinsics() + .constructors() + .async_generator() + .clone(), + ) + .name(Self::NAME) + .length(0) + .property( + JsSymbol::to_string_tag(), + Self::NAME, + Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ) + .method(Self::next, "next", 1) + .method(Self::r#return, "return", 1) + .method(Self::throw, "throw", 1) + .inherit(iterator_prototype) + .build(); + + context + .intrinsics() + .constructors() + .async_generator() + .prototype + .insert_property( + "constructor", + PropertyDescriptor::builder() + .value(generator_function_prototype) + .writable(false) + .enumerable(false) + .configurable(true), + ); + + None + } +} + +impl AsyncGenerator { + #[allow(clippy::unnecessary_wraps)] + pub(crate) fn constructor( + _: &JsValue, + _: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let prototype = context + .intrinsics() + .constructors() + .async_generator() + .prototype(); + + let this = JsObject::from_proto_and_data( + prototype, + ObjectData::async_generator(Self { + state: AsyncGeneratorState::Undefined, + context: None, + queue: VecDeque::new(), + }), + ); + + Ok(this.into()) + } + + /// `AsyncGenerator.prototype.next ( value )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-asyncgenerator-prototype-next + pub(crate) fn next( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let generator be the this value. + let generator = this; + + // 2. Let promiseCapability be ! NewPromiseCapability(%Promise%). + let promise_capability = PromiseCapability::new( + &context + .intrinsics() + .constructors() + .promise() + .constructor() + .into(), + context, + ) + .expect("cannot fail with promise constructor"); + + // 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)). + // 4. IfAbruptRejectPromise(result, promiseCapability). + let generator_object: JsResult<_> = generator.as_object().ok_or_else(|| { + JsNativeError::typ() + .with_message("generator resumed on non generator object") + .into() + }); + if_abrupt_reject_promise!(generator_object, promise_capability, context); + let mut generator_obj_mut = generator_object.borrow_mut(); + let generator: JsResult<_> = generator_obj_mut.as_async_generator_mut().ok_or_else(|| { + JsNativeError::typ() + .with_message("generator resumed on non generator object") + .into() + }); + if_abrupt_reject_promise!(generator, promise_capability, context); + + // 5. Let state be generator.[[AsyncGeneratorState]]. + let state = generator.state; + + // 6. If state is completed, then + if state == AsyncGeneratorState::Completed { + drop(generator_obj_mut); + + // a. Let iteratorResult be CreateIterResultObject(undefined, true). + let iterator_result = create_iter_result_object(JsValue::undefined(), true, context); + + // b. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iteratorResult »). + promise_capability + .resolve() + .call(&JsValue::undefined(), &[iterator_result], context) + .expect("cannot fail per spec"); + + // c. Return promiseCapability.[[Promise]]. + return Ok(promise_capability.promise().clone().into()); + } + + // 7. Let completion be NormalCompletion(value). + let completion = (Ok(args.get_or_undefined(0).clone()), false); + + // 8. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability). + generator.enqueue(completion.clone(), promise_capability.clone()); + + // 9. If state is either suspendedStart or suspendedYield, then + if state == AsyncGeneratorState::SuspendedStart + || state == AsyncGeneratorState::SuspendedYield + { + // a. Perform AsyncGeneratorResume(generator, completion). + let generator_context = generator + .context + .clone() + .expect("generator context cannot be empty here"); + + drop(generator_obj_mut); + + Self::resume( + generator_object, + state, + &generator_context, + completion, + context, + ); + } + + // 11. Return promiseCapability.[[Promise]]. + Ok(promise_capability.promise().clone().into()) + } + + /// `AsyncGenerator.prototype.return ( value )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-asyncgenerator-prototype-return + pub(crate) fn r#return( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let generator be the this value. + let generator = this; + + // 2. Let promiseCapability be ! NewPromiseCapability(%Promise%). + let promise_capability = PromiseCapability::new( + &context + .intrinsics() + .constructors() + .promise() + .constructor() + .into(), + context, + ) + .expect("cannot fail with promise constructor"); + + // 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)). + // 4. IfAbruptRejectPromise(result, promiseCapability). + let generator_object: JsResult<_> = generator.as_object().ok_or_else(|| { + JsNativeError::typ() + .with_message("generator resumed on non generator object") + .into() + }); + if_abrupt_reject_promise!(generator_object, promise_capability, context); + let mut generator_obj_mut = generator_object.borrow_mut(); + let generator: JsResult<_> = generator_obj_mut.as_async_generator_mut().ok_or_else(|| { + JsNativeError::typ() + .with_message("generator resumed on non generator object") + .into() + }); + if_abrupt_reject_promise!(generator, promise_capability, context); + + // 5. Let completion be Completion Record { [[Type]]: return, [[Value]]: value, [[Target]]: empty }. + let completion = (Ok(args.get_or_undefined(0).clone()), true); + + // 6. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability). + generator.enqueue(completion.clone(), promise_capability.clone()); + + // 7. Let state be generator.[[AsyncGeneratorState]]. + let state = generator.state; + + // 8. If state is either suspendedStart or completed, then + if state == AsyncGeneratorState::SuspendedStart || state == AsyncGeneratorState::Completed { + // a. Set generator.[[AsyncGeneratorState]] to awaiting-return. + generator.state = AsyncGeneratorState::AwaitingReturn; + + // b. Perform ! AsyncGeneratorAwaitReturn(generator). + let next = generator + .queue + .front() + .cloned() + .expect("queue cannot be empty here"); + drop(generator_obj_mut); + let (completion, _) = &next.completion; + Self::await_return(generator_object.clone(), completion.clone(), context); + } + // 9. Else if state is suspendedYield, then + else if state == AsyncGeneratorState::SuspendedYield { + // a. Perform AsyncGeneratorResume(generator, completion). + let generator_context = generator + .context + .clone() + .expect("generator context cannot be empty here"); + + drop(generator_obj_mut); + Self::resume( + generator_object, + state, + &generator_context, + completion, + context, + ); + } + + // 11. Return promiseCapability.[[Promise]]. + Ok(promise_capability.promise().clone().into()) + } + + /// `AsyncGenerator.prototype.throw ( exception )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-asyncgenerator-prototype-throw + pub(crate) fn throw( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let generator be the this value. + let generator = this; + + // 2. Let promiseCapability be ! NewPromiseCapability(%Promise%). + let promise_capability = PromiseCapability::new( + &context + .intrinsics() + .constructors() + .promise() + .constructor() + .into(), + context, + ) + .expect("cannot fail with promise constructor"); + + // 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)). + // 4. IfAbruptRejectPromise(result, promiseCapability). + let generator_object: JsResult<_> = generator.as_object().ok_or_else(|| { + JsNativeError::typ() + .with_message("generator resumed on non generator object") + .into() + }); + if_abrupt_reject_promise!(generator_object, promise_capability, context); + let mut generator_obj_mut = generator_object.borrow_mut(); + let generator: JsResult<_> = generator_obj_mut.as_async_generator_mut().ok_or_else(|| { + JsNativeError::typ() + .with_message("generator resumed on non generator object") + .into() + }); + if_abrupt_reject_promise!(generator, promise_capability, context); + + // 5. Let state be generator.[[AsyncGeneratorState]]. + let mut state = generator.state; + + // 6. If state is suspendedStart, then + if state == AsyncGeneratorState::SuspendedStart { + // a. Set generator.[[AsyncGeneratorState]] to completed. + generator.state = AsyncGeneratorState::Completed; + + // b. Set state to completed. + state = AsyncGeneratorState::Completed; + } + + // 7. If state is completed, then + if state == AsyncGeneratorState::Completed { + drop(generator_obj_mut); + + // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « exception »). + promise_capability + .reject() + .call( + &JsValue::undefined(), + &[args.get_or_undefined(0).clone()], + context, + ) + .expect("cannot fail per spec"); + + // b. Return promiseCapability.[[Promise]]. + return Ok(promise_capability.promise().clone().into()); + } + + // 8. Let completion be ThrowCompletion(exception). + let completion = ( + Err(JsError::from_opaque(args.get_or_undefined(0).clone())), + false, + ); + + // 9. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability). + generator.enqueue(completion.clone(), promise_capability.clone()); + + // 10. If state is suspendedYield, then + if state == AsyncGeneratorState::SuspendedYield { + let generator_context = generator + .context + .clone() + .expect("generator context cannot be empty here"); + drop(generator_obj_mut); + + // a. Perform AsyncGeneratorResume(generator, completion). + Self::resume( + generator_object, + state, + &generator_context, + completion, + context, + ); + } + + // 12. Return promiseCapability.[[Promise]]. + Ok(promise_capability.promise().clone().into()) + } + + /// `AsyncGeneratorEnqueue ( generator, completion, promiseCapability )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorenqueue + pub(crate) fn enqueue( + &mut self, + completion: (JsResult, bool), + promise_capability: PromiseCapability, + ) { + // 1. Let request be AsyncGeneratorRequest { [[Completion]]: completion, [[Capability]]: promiseCapability }. + let request = AsyncGeneratorRequest { + completion, + capability: promise_capability, + }; + + // 2. Append request to the end of generator.[[AsyncGeneratorQueue]]. + self.queue.push_back(request); + } + + /// `AsyncGeneratorCompleteStep ( generator, completion, done [ , realm ] )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorcompletestep + pub(crate) fn complete_step( + next: &AsyncGeneratorRequest, + completion: JsResult, + done: bool, + context: &mut Context<'_>, + ) { + // 1. Let queue be generator.[[AsyncGeneratorQueue]]. + // 2. Assert: queue is not empty. + // 3. Let next be the first element of queue. + // 4. Remove the first element from queue. + // 5. Let promiseCapability be next.[[Capability]]. + let promise_capability = &next.capability; + + // 6. Let value be completion.[[Value]]. + match completion { + // 7. If completion.[[Type]] is throw, then + Err(e) => { + // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « value »). + promise_capability + .reject() + .call(&JsValue::undefined(), &[e.to_opaque(context)], context) + .expect("cannot fail per spec"); + } + // 8. Else, + Ok(value) => { + // a. Assert: completion.[[Type]] is normal. + + // TODO: Realm handling not implemented yet. + // b. If realm is present, then + // i. Let oldRealm be the running execution context's Realm. + // ii. Set the running execution context's Realm to realm. + // iii. Let iteratorResult be CreateIterResultObject(value, done). + // iv. Set the running execution context's Realm to oldRealm. + // c. Else, + // i. Let iteratorResult be CreateIterResultObject(value, done). + let iterator_result = create_iter_result_object(value, done, context); + + // d. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iteratorResult »). + promise_capability + .resolve() + .call(&JsValue::undefined(), &[iterator_result], context) + .expect("cannot fail per spec"); + } + } + } + + /// `AsyncGeneratorResume ( generator, completion )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorresume + pub(crate) fn resume( + generator: &JsObject, + state: AsyncGeneratorState, + generator_context: &Gc>, + completion: (JsResult, bool), + context: &mut Context<'_>, + ) { + // 1. Assert: generator.[[AsyncGeneratorState]] is either suspendedStart or suspendedYield. + assert!( + state == AsyncGeneratorState::SuspendedStart + || state == AsyncGeneratorState::SuspendedYield + ); + + // 2. Let genContext be generator.[[AsyncGeneratorContext]]. + let mut generator_context_mut = generator_context.borrow_mut(); + + // 3. Let callerContext be the running execution context. + // 4. Suspend callerContext. + + // 5. Set generator.[[AsyncGeneratorState]] to executing. + generator + .borrow_mut() + .as_async_generator_mut() + .expect("already checked before") + .state = AsyncGeneratorState::Executing; + + // 6. Push genContext onto the execution context stack; genContext is now the running execution context. + std::mem::swap( + &mut context.realm.environments, + &mut generator_context_mut.environments, + ); + std::mem::swap(&mut context.vm.stack, &mut generator_context_mut.stack); + context + .vm + .push_frame(generator_context_mut.call_frame.clone()); + + // 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. + match completion { + (Ok(value), r#return) => { + context.vm.push(value); + if r#return { + context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Return; + } else { + context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Normal; + } + } + (Err(value), _) => { + let value = value.to_opaque(context); + context.vm.push(value); + context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Throw; + } + } + drop(generator_context_mut); + let result = context.run(); + + let mut generator_context_mut = generator_context.borrow_mut(); + std::mem::swap( + &mut context.realm.environments, + &mut generator_context_mut.environments, + ); + std::mem::swap(&mut context.vm.stack, &mut generator_context_mut.stack); + generator_context_mut.call_frame = + context.vm.pop_frame().expect("generator frame must exist"); + drop(generator_context_mut); + + // 8. Assert: result is never an abrupt completion. + assert!(result.is_ok()); + + // 9. Assert: When we return here, genContext has already been removed from the execution context stack and callerContext is the currently running execution context. + // 10. Return unused. + } + + /// `AsyncGeneratorAwaitReturn ( generator )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorawaitreturn + pub(crate) fn await_return( + generator: JsObject, + completion: JsResult, + context: &mut Context<'_>, + ) { + // 1. Let queue be generator.[[AsyncGeneratorQueue]]. + // 2. Assert: queue is not empty. + // 3. Let next be the first element of queue. + // 4. Let completion be Completion(next.[[Completion]]). + + // 5. Assert: completion.[[Type]] is return. + let value = completion.expect("completion must be a return completion"); + + // Note: The spec is currently broken here. + // See: https://github.com/tc39/ecma262/pull/2683 + + // 6. Let promise be ? PromiseResolve(%Promise%, completion.[[Value]]). + let promise_completion = Promise::promise_resolve( + context.intrinsics().constructors().promise().constructor(), + value, + context, + ); + + let promise = match promise_completion { + Ok(value) => value, + Err(value) => { + let mut generator_borrow_mut = generator.borrow_mut(); + let gen = generator_borrow_mut + .as_async_generator_mut() + .expect("already checked before"); + gen.state = AsyncGeneratorState::Completed; + let next = gen.queue.pop_front().expect("queue must not be empty"); + drop(generator_borrow_mut); + Self::complete_step(&next, Err(value), true, context); + Self::drain_queue(&generator, context); + return; + } + }; + + // 7. Let fulfilledClosure be a new Abstract Closure with parameters (value) that captures generator and performs the following steps when called: + // 8. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 1, "", « »). + let on_fulfilled = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure_with_captures( + |_this, args, generator, context| { + let next = { + let mut generator_borrow_mut = generator.borrow_mut(); + let gen = generator_borrow_mut + .as_async_generator_mut() + .expect("already checked before"); + + // a. Set generator.[[AsyncGeneratorState]] to completed. + gen.state = AsyncGeneratorState::Completed; + + gen.queue.pop_front().expect("must have one entry") + }; + + // b. Let result be NormalCompletion(value). + let result = Ok(args.get_or_undefined(0).clone()); + + // c. Perform AsyncGeneratorCompleteStep(generator, result, true). + Self::complete_step(&next, result, true, context); + + // d. Perform AsyncGeneratorDrainQueue(generator). + Self::drain_queue(generator, context); + + // e. Return undefined. + Ok(JsValue::undefined()) + }, + generator.clone(), + ), + ) + .name("") + .length(1) + .build(); + + // 9. Let rejectedClosure be a new Abstract Closure with parameters (reason) that captures generator and performs the following steps when called: + // 10. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "", « »). + let on_rejected = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure_with_captures( + |_this, args, generator, context| { + let mut generator_borrow_mut = generator.borrow_mut(); + let gen = generator_borrow_mut + .as_async_generator_mut() + .expect("already checked before"); + + // a. Set generator.[[AsyncGeneratorState]] to completed. + gen.state = AsyncGeneratorState::Completed; + + // b. Let result be ThrowCompletion(reason). + let result = Err(JsError::from_opaque(args.get_or_undefined(0).clone())); + + // c. Perform AsyncGeneratorCompleteStep(generator, result, true). + let next = gen.queue.pop_front().expect("must have one entry"); + drop(generator_borrow_mut); + Self::complete_step(&next, result, true, context); + + // d. Perform AsyncGeneratorDrainQueue(generator). + Self::drain_queue(generator, context); + + // e. Return undefined. + Ok(JsValue::undefined()) + }, + generator, + ), + ) + .name("") + .length(1) + .build(); + + // 11. Perform PerformPromiseThen(promise, onFulfilled, onRejected). + Promise::perform_promise_then( + &promise, + &on_fulfilled.into(), + &on_rejected.into(), + None, + context, + ); + } + + /// `AsyncGeneratorDrainQueue ( generator )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratordrainqueue + pub(crate) fn drain_queue(generator: &JsObject, context: &mut Context<'_>) { + let mut generator_borrow_mut = generator.borrow_mut(); + let gen = generator_borrow_mut + .as_async_generator_mut() + .expect("already checked before"); + + // 1. Assert: generator.[[AsyncGeneratorState]] is completed. + assert_eq!(gen.state, AsyncGeneratorState::Completed); + + // 2. Let queue be generator.[[AsyncGeneratorQueue]]. + let queue = &mut gen.queue; + + // 3. If queue is empty, return unused. + if queue.is_empty() { + return; + } + + // 4. Let done be false. + // 5. Repeat, while done is false, + loop { + // a. Let next be the first element of queue. + let next = queue.front().expect("must have entry"); + + // b. Let completion be Completion(next.[[Completion]]). + match &next.completion { + // c. If completion.[[Type]] is return, then + (completion, true) => { + // i. Set generator.[[AsyncGeneratorState]] to awaiting-return. + gen.state = AsyncGeneratorState::AwaitingReturn; + + // ii. Perform ! AsyncGeneratorAwaitReturn(generator). + let completion = completion.clone(); + drop(generator_borrow_mut); + Self::await_return(generator.clone(), completion, context); + + // iii. Set done to true. + break; + } + // d. Else, + (completion, false) => { + // i. If completion.[[Type]] is normal, then + let completion = if completion.is_ok() { + // 1. Set completion to NormalCompletion(undefined). + Ok(JsValue::undefined()) + } else { + completion.clone() + }; + + // ii. Perform AsyncGeneratorCompleteStep(generator, completion, true). + let next = queue.pop_front().expect("must have entry"); + Self::complete_step(&next, completion, true, context); + + // iii. If queue is empty, set done to true. + if queue.is_empty() { + break; + } + } + } + } + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/async_generator_function/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/async_generator_function/mod.rs new file mode 100644 index 0000000..3b66706 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/async_generator_function/mod.rs @@ -0,0 +1,130 @@ +//! Boa's implementation of ECMAScript's `AsyncGeneratorFunction` object. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorfunction-objects + +use crate::{ + builtins::{ + function::{BuiltInFunctionObject, ConstructorKind, Function}, + BuiltIn, + }, + native_function::NativeFunction, + object::ObjectData, + property::PropertyDescriptor, + symbol::JsSymbol, + value::JsValue, + Context, JsResult, +}; +use boa_profiler::Profiler; + +/// The internal representation of an `AsyncGeneratorFunction` object. +#[derive(Debug, Clone, Copy)] +pub struct AsyncGeneratorFunction; + +impl BuiltIn for AsyncGeneratorFunction { + const NAME: &'static str = "AsyncGeneratorFunction"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let prototype = &context + .intrinsics() + .constructors() + .async_generator_function() + .prototype; + let constructor = &context + .intrinsics() + .constructors() + .async_generator_function() + .constructor; + + constructor.set_prototype(Some( + context.intrinsics().constructors().function().constructor(), + )); + let property = PropertyDescriptor::builder() + .value(1) + .writable(false) + .enumerable(false) + .configurable(true); + constructor.borrow_mut().insert("length", property); + let property = PropertyDescriptor::builder() + .value(Self::NAME) + .writable(false) + .enumerable(false) + .configurable(true); + constructor.borrow_mut().insert("name", property); + let property = PropertyDescriptor::builder() + .value( + context + .intrinsics() + .constructors() + .async_generator_function() + .prototype(), + ) + .writable(false) + .enumerable(false) + .configurable(false); + constructor.borrow_mut().insert("prototype", property); + constructor.borrow_mut().data = ObjectData::function(Function::Native { + function: NativeFunction::from_fn_ptr(Self::constructor), + constructor: Some(ConstructorKind::Base), + }); + + prototype.set_prototype(Some( + context.intrinsics().constructors().function().prototype(), + )); + let property = PropertyDescriptor::builder() + .value( + context + .intrinsics() + .constructors() + .async_generator_function() + .constructor(), + ) + .writable(false) + .enumerable(false) + .configurable(true); + prototype.borrow_mut().insert("constructor", property); + let property = PropertyDescriptor::builder() + .value( + context + .intrinsics() + .constructors() + .async_generator() + .prototype(), + ) + .writable(false) + .enumerable(false) + .configurable(true); + prototype.borrow_mut().insert("prototype", property); + let property = PropertyDescriptor::builder() + .value("AsyncGeneratorFunction") + .writable(false) + .enumerable(false) + .configurable(true); + prototype + .borrow_mut() + .insert(JsSymbol::to_string_tag(), property); + + None + } +} + +impl AsyncGeneratorFunction { + /// `AsyncGeneratorFunction ( p1, p2, … , pn, body )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorfunction + pub(crate) fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + BuiltInFunctionObject::create_dynamic_function(new_target, args, true, true, context) + .map(Into::into) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/bigint/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/bigint/mod.rs new file mode 100644 index 0000000..b3dd59d --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/bigint/mod.rs @@ -0,0 +1,295 @@ +//! Boa's implementation of ECMAScript's global `BigInt` object. +//! +//! `BigInt` is a built-in object that provides a way to represent whole numbers larger +//! than the largest number JavaScript can reliably represent with the Number primitive +//! and represented by the `Number.MAX_SAFE_INTEGER` constant. +//! `BigInt` can be used for arbitrarily large integers. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! - [MDN documentation][mdn] +//! +//! [spec]: https://tc39.es/ecma262/#sec-bigint-objects +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt + +use crate::{ + builtins::{BuiltIn, JsArgs}, + error::JsNativeError, + object::ConstructorBuilder, + property::Attribute, + symbol::JsSymbol, + value::{IntegerOrInfinity, PreferredType}, + Context, JsBigInt, JsResult, JsValue, +}; +use boa_profiler::Profiler; +use num_bigint::ToBigInt; +use tap::{Conv, Pipe}; + +#[cfg(test)] +mod tests; + +/// `BigInt` implementation. +#[derive(Debug, Clone, Copy)] +pub struct BigInt; + +impl BuiltIn for BigInt { + const NAME: &'static str = "BigInt"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let to_string_tag = JsSymbol::to_string_tag(); + + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context.intrinsics().constructors().bigint_object().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .callable(true) + .constructor(true) + .method(Self::to_string, "toString", 0) + .method(Self::value_of, "valueOf", 0) + .static_method(Self::as_int_n, "asIntN", 2) + .static_method(Self::as_uint_n, "asUintN", 2) + .property( + to_string_tag, + Self::NAME, + Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ) + .build() + .conv::() + .pipe(Some) + } +} + +impl BigInt { + /// The amount of arguments this function object takes. + pub(crate) const LENGTH: usize = 1; + + /// `BigInt()` + /// + /// The `BigInt()` constructor is used to create `BigInt` objects. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-bigint-objects + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/BigInt + fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. If NewTarget is not undefined, throw a TypeError exception. + if !new_target.is_undefined() { + return Err(JsNativeError::typ() + .with_message("BigInt is not a constructor") + .into()); + } + + let value = args.get_or_undefined(0); + + // 2. Let prim be ? ToPrimitive(value, number). + let prim = value.to_primitive(context, PreferredType::Number)?; + + // 3. If Type(prim) is Number, return ? NumberToBigInt(prim). + if let Some(number) = prim.as_number() { + return Self::number_to_bigint(number); + } + + // 4. Otherwise, return ? ToBigInt(value). + Ok(value.to_bigint(context)?.into()) + } + + /// `NumberToBigInt ( number )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-numbertobigint + fn number_to_bigint(number: f64) -> JsResult { + // 1. If IsIntegralNumber(number) is false, throw a RangeError exception. + if number.is_nan() || number.is_infinite() || number.fract() != 0.0 { + return Err(JsNativeError::range() + .with_message(format!("Cannot convert {number} to BigInt")) + .into()); + } + + // 2. Return the BigInt value that represents ℝ(number). + Ok(JsBigInt::from(number.to_bigint().expect("This conversion must be safe")).into()) + } + + /// The abstract operation `thisBigIntValue` takes argument value. + /// + /// The phrase “this `BigInt` value” within the specification of a method refers to the + /// result returned by calling the abstract operation `thisBigIntValue` with the `this` value + /// of the method invocation passed as the argument. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-thisbigintvalue + fn this_bigint_value(value: &JsValue) -> JsResult { + value + // 1. If Type(value) is BigInt, return value. + .as_bigint() + .cloned() + // 2. If Type(value) is Object and value has a [[BigIntData]] internal slot, then + // a. Assert: Type(value.[[BigIntData]]) is BigInt. + // b. Return value.[[BigIntData]]. + .or_else(|| { + value + .as_object() + .and_then(|obj| obj.borrow().as_bigint().cloned()) + }) + // 3. Throw a TypeError exception. + .ok_or_else(|| { + JsNativeError::typ() + .with_message("'this' is not a BigInt") + .into() + }) + } + + /// `BigInt.prototype.toString( [radix] )` + /// + /// The `toString()` method returns a string representing the specified `BigInt` object. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.tostring + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString + #[allow(clippy::wrong_self_convention)] + pub(crate) fn to_string( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let x be ? thisBigIntValue(this value). + let x = Self::this_bigint_value(this)?; + + let radix = args.get_or_undefined(0); + + // 2. If radix is undefined, let radixMV be 10. + let radix_mv = if radix.is_undefined() { + // 5. If radixMV = 10, return ! ToString(x). + // Note: early return optimization. + return Ok(x.to_string().into()); + // 3. Else, let radixMV be ? ToIntegerOrInfinity(radix). + } else { + radix.to_integer_or_infinity(context)? + }; + + // 4. If radixMV < 2 or radixMV > 36, throw a RangeError exception. + let radix_mv = match radix_mv { + IntegerOrInfinity::Integer(i) if (2..=36).contains(&i) => i, + _ => { + return Err(JsNativeError::range() + .with_message("radix must be an integer at least 2 and no greater than 36") + .into()) + } + }; + + // 5. If radixMV = 10, return ! ToString(x). + if radix_mv == 10 { + return Ok(x.to_string().into()); + } + + // 1. Let x be ? thisBigIntValue(this value). + // 6. Return the String representation of this Number value using the radix specified by radixMV. + // Letters a-z are used for digits with values 10 through 35. + // The precise algorithm is implementation-defined, however the algorithm should be a generalization of that specified in 6.1.6.2.23. + Ok(JsValue::new(x.to_string_radix(radix_mv as u32))) + } + + /// `BigInt.prototype.valueOf()` + /// + /// The `valueOf()` method returns the wrapped primitive value of a Number object. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.valueof + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/valueOf + pub(crate) fn value_of( + this: &JsValue, + _: &[JsValue], + _: &mut Context<'_>, + ) -> JsResult { + Ok(JsValue::new(Self::this_bigint_value(this)?)) + } + + /// `BigInt.asIntN()` + /// + /// The `BigInt.asIntN()` method wraps the value of a `BigInt` to a signed integer between `-2**(width - 1)` and `2**(width-1) - 1`. + /// + /// [spec]: https://tc39.es/ecma262/#sec-bigint.asintn + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asIntN + #[allow(clippy::wrong_self_convention)] + pub(crate) fn as_int_n( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let (modulo, bits) = Self::calculate_as_uint_n(args, context)?; + + if bits > 0 + && modulo >= JsBigInt::pow(&JsBigInt::new(2), &JsBigInt::new(i64::from(bits) - 1))? + { + Ok(JsValue::new(JsBigInt::sub( + &modulo, + &JsBigInt::pow(&JsBigInt::new(2), &JsBigInt::new(i64::from(bits)))?, + ))) + } else { + Ok(JsValue::new(modulo)) + } + } + + /// `BigInt.asUintN()` + /// + /// The `BigInt.asUintN()` method wraps the value of a `BigInt` to an unsigned integer between `0` and `2**(width) - 1`. + /// + /// [spec]: https://tc39.es/ecma262/#sec-bigint.asuintn + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asUintN + #[allow(clippy::wrong_self_convention)] + pub(crate) fn as_uint_n( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let (modulo, _) = Self::calculate_as_uint_n(args, context)?; + + Ok(JsValue::new(modulo)) + } + + /// Helper function to wrap the value of a `BigInt` to an unsigned integer. + /// + /// This function expects the same arguments as `as_uint_n` and wraps the value of a `BigInt`. + /// Additionally to the wrapped unsigned value it returns the converted `bits` argument, so it + /// can be reused from the `as_int_n` method. + fn calculate_as_uint_n( + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult<(JsBigInt, u32)> { + let bits_arg = args.get_or_undefined(0); + let bigint_arg = args.get_or_undefined(1); + + let bits = bits_arg.to_index(context)?; + let bits = u32::try_from(bits).unwrap_or(u32::MAX); + + let bigint = bigint_arg.to_bigint(context)?; + + Ok(( + JsBigInt::mod_floor( + &bigint, + &JsBigInt::pow(&JsBigInt::new(2), &JsBigInt::new(i64::from(bits)))?, + ), + bits, + )) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/bigint/tests.rs b/javascript-engine/external/boa/boa_engine/src/builtins/bigint/tests.rs new file mode 100644 index 0000000..9e54b96 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/bigint/tests.rs @@ -0,0 +1,413 @@ +use crate::{forward, Context}; + +#[test] +fn equality() { + let mut context = Context::default(); + + assert_eq!(forward(&mut context, "0n == 0n"), "true"); + assert_eq!(forward(&mut context, "1n == 0n"), "false"); + assert_eq!( + forward( + &mut context, + "1000000000000000000000000000000000n == 1000000000000000000000000000000000n" + ), + "true" + ); + + assert_eq!(forward(&mut context, "0n == ''"), "true"); + assert_eq!(forward(&mut context, "100n == '100'"), "true"); + assert_eq!(forward(&mut context, "100n == '100.5'"), "false"); + assert_eq!( + forward(&mut context, "10000000000000000n == '10000000000000000'"), + "true" + ); + + assert_eq!(forward(&mut context, "'' == 0n"), "true"); + assert_eq!(forward(&mut context, "'100' == 100n"), "true"); + assert_eq!(forward(&mut context, "'100.5' == 100n"), "false"); + assert_eq!( + forward(&mut context, "'10000000000000000' == 10000000000000000n"), + "true" + ); + + assert_eq!(forward(&mut context, "0n == 0"), "true"); + assert_eq!(forward(&mut context, "0n == 0.0"), "true"); + assert_eq!(forward(&mut context, "100n == 100"), "true"); + assert_eq!(forward(&mut context, "100n == 100.0"), "true"); + assert_eq!(forward(&mut context, "100n == '100.5'"), "false"); + assert_eq!(forward(&mut context, "100n == '1005'"), "false"); + assert_eq!( + forward(&mut context, "10000000000000000n == 10000000000000000"), + "true" + ); + + assert_eq!(forward(&mut context, "0 == 0n"), "true"); + assert_eq!(forward(&mut context, "0.0 == 0n"), "true"); + assert_eq!(forward(&mut context, "100 == 100n"), "true"); + assert_eq!(forward(&mut context, "100.0 == 100n"), "true"); + assert_eq!(forward(&mut context, "100.5 == 100n"), "false"); + assert_eq!(forward(&mut context, "1005 == 100n"), "false"); + assert_eq!( + forward(&mut context, "10000000000000000 == 10000000000000000n"), + "true" + ); +} + +#[test] +fn bigint_function_conversion_from_integer() { + let mut context = Context::default(); + + assert_eq!(forward(&mut context, "BigInt(1000)"), "1000n"); + assert_eq!( + forward(&mut context, "BigInt(20000000000000000)"), + "20000000000000000n" + ); + assert_eq!( + forward(&mut context, "BigInt(1000000000000000000000000000000000)"), + "999999999999999945575230987042816n" + ); +} + +#[test] +fn bigint_function_conversion_from_rational() { + let mut context = Context::default(); + + assert_eq!(forward(&mut context, "BigInt(0.0)"), "0n"); + assert_eq!(forward(&mut context, "BigInt(1.0)"), "1n"); + assert_eq!(forward(&mut context, "BigInt(10000.0)"), "10000n"); +} + +#[test] +fn bigint_function_conversion_from_rational_with_fractional_part() { + let mut context = Context::default(); + + let scenario = r#" + try { + BigInt(0.1); + } catch (e) { + e.toString(); + } + "#; + assert_eq!( + forward(&mut context, scenario), + "\"RangeError: Cannot convert 0.1 to BigInt\"" + ); +} + +#[test] +fn bigint_function_conversion_from_null() { + let mut context = Context::default(); + + let scenario = r#" + try { + BigInt(null); + } catch (e) { + e.toString(); + } + "#; + assert_eq!( + forward(&mut context, scenario), + "\"TypeError: cannot convert null to a BigInt\"" + ); +} + +#[test] +fn bigint_function_conversion_from_undefined() { + let mut context = Context::default(); + + let scenario = r#" + try { + BigInt(undefined); + } catch (e) { + e.toString(); + } + "#; + assert_eq!( + forward(&mut context, scenario), + "\"TypeError: cannot convert undefined to a BigInt\"" + ); +} + +#[test] +fn bigint_function_conversion_from_string() { + let mut context = Context::default(); + + assert_eq!(forward(&mut context, "BigInt('')"), "0n"); + assert_eq!(forward(&mut context, "BigInt(' ')"), "0n"); + assert_eq!( + forward(&mut context, "BigInt('200000000000000000')"), + "200000000000000000n" + ); + assert_eq!( + forward(&mut context, "BigInt('1000000000000000000000000000000000')"), + "1000000000000000000000000000000000n" + ); + assert_eq!(forward(&mut context, "BigInt('0b1111')"), "15n"); + assert_eq!(forward(&mut context, "BigInt('0o70')"), "56n"); + assert_eq!(forward(&mut context, "BigInt('0xFF')"), "255n"); +} + +#[test] +fn add() { + let mut context = Context::default(); + + assert_eq!(forward(&mut context, "10000n + 1000n"), "11000n"); +} + +#[test] +fn sub() { + let mut context = Context::default(); + + assert_eq!(forward(&mut context, "10000n - 1000n"), "9000n"); +} + +#[test] +fn mul() { + let mut context = Context::default(); + + assert_eq!( + forward(&mut context, "123456789n * 102030n"), + "12596296181670n" + ); +} + +#[test] +fn div() { + let mut context = Context::default(); + + assert_eq!(forward(&mut context, "15000n / 50n"), "300n"); +} + +#[test] +fn div_with_truncation() { + let mut context = Context::default(); + + assert_eq!(forward(&mut context, "15001n / 50n"), "300n"); +} + +#[test] +fn r#mod() { + let mut context = Context::default(); + + assert_eq!(forward(&mut context, "15007n % 10n"), "7n"); +} + +#[test] +fn pow() { + let mut context = Context::default(); + + assert_eq!( + forward(&mut context, "100n ** 10n"), + "100000000000000000000n" + ); +} + +#[test] +fn pow_negative_exponent() { + let mut context = Context::default(); + + assert_throws(&mut context, "10n ** (-10n)", "RangeError"); +} + +#[test] +fn shl() { + let mut context = Context::default(); + + assert_eq!(forward(&mut context, "8n << 2n"), "32n"); +} + +#[test] +fn shl_out_of_range() { + let mut context = Context::default(); + + assert_throws(&mut context, "1000n << 1000000000000000n", "RangeError"); +} + +#[test] +fn shr() { + let mut context = Context::default(); + + assert_eq!(forward(&mut context, "8n >> 2n"), "2n"); +} + +#[test] +fn shr_out_of_range() { + let mut context = Context::default(); + + assert_throws(&mut context, "1000n >> 1000000000000000n", "RangeError"); +} + +#[test] +fn to_string() { + let mut context = Context::default(); + + assert_eq!(forward(&mut context, "1000n.toString()"), "\"1000\""); + assert_eq!(forward(&mut context, "1000n.toString(2)"), "\"1111101000\""); + assert_eq!(forward(&mut context, "255n.toString(16)"), "\"ff\""); + assert_eq!(forward(&mut context, "1000n.toString(36)"), "\"rs\""); +} + +#[test] +fn to_string_invalid_radix() { + let mut context = Context::default(); + + assert_throws(&mut context, "10n.toString(null)", "RangeError"); + assert_throws(&mut context, "10n.toString(-1)", "RangeError"); + assert_throws(&mut context, "10n.toString(37)", "RangeError"); +} + +#[test] +fn as_int_n() { + let mut context = Context::default(); + + assert_eq!(forward(&mut context, "BigInt.asIntN(0, 1n)"), "0n"); + assert_eq!(forward(&mut context, "BigInt.asIntN(1, 1n)"), "-1n"); + assert_eq!(forward(&mut context, "BigInt.asIntN(3, 10n)"), "2n"); + assert_eq!(forward(&mut context, "BigInt.asIntN({}, 1n)"), "0n"); + assert_eq!(forward(&mut context, "BigInt.asIntN(2, 0n)"), "0n"); + assert_eq!(forward(&mut context, "BigInt.asIntN(2, -0n)"), "0n"); + + assert_eq!( + forward(&mut context, "BigInt.asIntN(2, -123456789012345678901n)"), + "-1n" + ); + assert_eq!( + forward(&mut context, "BigInt.asIntN(2, -123456789012345678900n)"), + "0n" + ); + + assert_eq!( + forward(&mut context, "BigInt.asIntN(2, 123456789012345678900n)"), + "0n" + ); + assert_eq!( + forward(&mut context, "BigInt.asIntN(2, 123456789012345678901n)"), + "1n" + ); + + assert_eq!( + forward( + &mut context, + "BigInt.asIntN(200, 0xcffffffffffffffffffffffffffffffffffffffffffffffffffn)" + ), + "-1n" + ); + assert_eq!( + forward( + &mut context, + "BigInt.asIntN(201, 0xcffffffffffffffffffffffffffffffffffffffffffffffffffn)" + ), + "1606938044258990275541962092341162602522202993782792835301375n" + ); + + assert_eq!( + forward( + &mut context, + "BigInt.asIntN(200, 0xc89e081df68b65fedb32cffea660e55df9605650a603ad5fc54n)" + ), + "-741470203160010616172516490008037905920749803227695190508460n" + ); + assert_eq!( + forward( + &mut context, + "BigInt.asIntN(201, 0xc89e081df68b65fedb32cffea660e55df9605650a603ad5fc54n)" + ), + "865467841098979659369445602333124696601453190555097644792916n" + ); +} + +#[test] +fn as_int_n_errors() { + let mut context = Context::default(); + + assert_throws(&mut context, "BigInt.asIntN(-1, 0n)", "RangeError"); + assert_throws(&mut context, "BigInt.asIntN(-2.5, 0n)", "RangeError"); + assert_throws( + &mut context, + "BigInt.asIntN(9007199254740992, 0n)", + "RangeError", + ); + assert_throws(&mut context, "BigInt.asIntN(0n, 0n)", "TypeError"); +} + +#[test] +fn as_uint_n() { + let mut context = Context::default(); + + assert_eq!(forward(&mut context, "BigInt.asUintN(0, -2n)"), "0n"); + assert_eq!(forward(&mut context, "BigInt.asUintN(0, -1n)"), "0n"); + assert_eq!(forward(&mut context, "BigInt.asUintN(0, 0n)"), "0n"); + assert_eq!(forward(&mut context, "BigInt.asUintN(0, 1n)"), "0n"); + assert_eq!(forward(&mut context, "BigInt.asUintN(0, 2n)"), "0n"); + + assert_eq!(forward(&mut context, "BigInt.asUintN(1, -3n)"), "1n"); + assert_eq!(forward(&mut context, "BigInt.asUintN(1, -2n)"), "0n"); + assert_eq!(forward(&mut context, "BigInt.asUintN(1, -1n)"), "1n"); + assert_eq!(forward(&mut context, "BigInt.asUintN(1, 0n)"), "0n"); + assert_eq!(forward(&mut context, "BigInt.asUintN(1, 1n)"), "1n"); + assert_eq!(forward(&mut context, "BigInt.asUintN(1, 2n)"), "0n"); + assert_eq!(forward(&mut context, "BigInt.asUintN(1, 3n)"), "1n"); + + assert_eq!( + forward(&mut context, "BigInt.asUintN(1, -123456789012345678901n)"), + "1n" + ); + assert_eq!( + forward(&mut context, "BigInt.asUintN(1, -123456789012345678900n)"), + "0n" + ); + assert_eq!( + forward(&mut context, "BigInt.asUintN(1, 123456789012345678900n)"), + "0n" + ); + assert_eq!( + forward(&mut context, "BigInt.asUintN(1, 123456789012345678901n)"), + "1n" + ); + + assert_eq!( + forward( + &mut context, + "BigInt.asUintN(200, 0xbffffffffffffffffffffffffffffffffffffffffffffffffffn)" + ), + "1606938044258990275541962092341162602522202993782792835301375n" + ); + assert_eq!( + forward( + &mut context, + "BigInt.asUintN(201, 0xbffffffffffffffffffffffffffffffffffffffffffffffffffn)" + ), + "3213876088517980551083924184682325205044405987565585670602751n" + ); +} + +#[test] +fn as_uint_n_errors() { + let mut context = Context::default(); + + assert_throws(&mut context, "BigInt.asUintN(-1, 0n)", "RangeError"); + assert_throws(&mut context, "BigInt.asUintN(-2.5, 0n)", "RangeError"); + assert_throws( + &mut context, + "BigInt.asUintN(9007199254740992, 0n)", + "RangeError", + ); + assert_throws(&mut context, "BigInt.asUintN(0n, 0n)", "TypeError"); +} + +fn assert_throws(context: &mut Context<'_>, src: &str, error_type: &str) { + let result = forward(context, src); + assert!(result.contains(error_type)); +} + +#[test] +fn division_by_zero() { + let mut context = Context::default(); + assert_throws(&mut context, "1n/0n", "RangeError"); +} + +#[test] +fn remainder_by_zero() { + let mut context = Context::default(); + assert_throws(&mut context, "1n % 0n", "RangeError"); +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/boolean/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/boolean/mod.rs new file mode 100644 index 0000000..bacccff --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/boolean/mod.rs @@ -0,0 +1,127 @@ +//! Boa's implementation of ECMAScript's global `Boolean` object. +//! +//! The `Boolean` object is an object wrapper for a boolean value. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! - [MDN documentation][mdn] +//! +//! [spec]: https://tc39.es/ecma262/#sec-boolean-object +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean + +#[cfg(test)] +mod tests; + +use crate::{ + builtins::BuiltIn, + context::intrinsics::StandardConstructors, + error::JsNativeError, + object::{ + internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData, + }, + Context, JsResult, JsValue, +}; +use boa_profiler::Profiler; +use tap::{Conv, Pipe}; + +/// Boolean implementation. +#[derive(Debug, Clone, Copy)] +pub(crate) struct Boolean; + +impl BuiltIn for Boolean { + /// The name of the object. + const NAME: &'static str = "Boolean"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context.intrinsics().constructors().boolean().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .method(Self::to_string, "toString", 0) + .method(Self::value_of, "valueOf", 0) + .build() + .conv::() + .pipe(Some) + } +} + +impl Boolean { + /// The amount of arguments this function object takes. + pub(crate) const LENGTH: usize = 1; + + /// `[[Construct]]` Create a new boolean object + /// + /// `[[Call]]` Creates a new boolean primitive + pub(crate) fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // Get the argument, if any + let data = args.get(0).map_or(false, JsValue::to_boolean); + if new_target.is_undefined() { + return Ok(JsValue::new(data)); + } + let prototype = + get_prototype_from_constructor(new_target, StandardConstructors::boolean, context)?; + let boolean = JsObject::from_proto_and_data(prototype, ObjectData::boolean(data)); + + Ok(boolean.into()) + } + + /// An Utility function used to get the internal `[[BooleanData]]`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-thisbooleanvalue + fn this_boolean_value(value: &JsValue) -> JsResult { + value + .as_boolean() + .or_else(|| value.as_object().and_then(|obj| obj.borrow().as_boolean())) + .ok_or_else(|| { + JsNativeError::typ() + .with_message("'this' is not a boolean") + .into() + }) + } + + /// The `toString()` method returns a string representing the specified `Boolean` object. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-boolean-object + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/toString + #[allow(clippy::wrong_self_convention)] + pub(crate) fn to_string( + this: &JsValue, + _: &[JsValue], + _: &mut Context<'_>, + ) -> JsResult { + let boolean = Self::this_boolean_value(this)?; + Ok(JsValue::new(boolean.to_string())) + } + + /// The valueOf() method returns the primitive value of a `Boolean` object. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-boolean.prototype.valueof + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/valueOf + pub(crate) fn value_of( + this: &JsValue, + _: &[JsValue], + _: &mut Context<'_>, + ) -> JsResult { + Ok(JsValue::new(Self::this_boolean_value(this)?)) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/boolean/tests.rs b/javascript-engine/external/boa/boa_engine/src/builtins/boolean/tests.rs new file mode 100644 index 0000000..3d6ae4a --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/boolean/tests.rs @@ -0,0 +1,65 @@ +use crate::{forward, forward_val, Context}; + +/// Test the correct type is returned from call and construct +#[allow(clippy::unwrap_used)] +#[test] +fn construct_and_call() { + let mut context = Context::default(); + let init = r#" + var one = new Boolean(1); + var zero = Boolean(0); + "#; + eprintln!("{}", forward(&mut context, init)); + let one = forward_val(&mut context, "one").unwrap(); + let zero = forward_val(&mut context, "zero").unwrap(); + + assert!(one.is_object()); + assert!(zero.is_boolean()); +} + +#[test] +fn constructor_gives_true_instance() { + let mut context = Context::default(); + let init = r#" + var trueVal = new Boolean(true); + var trueNum = new Boolean(1); + var trueString = new Boolean("true"); + var trueBool = new Boolean(trueVal); + "#; + + eprintln!("{}", forward(&mut context, init)); + let true_val = forward_val(&mut context, "trueVal").expect("value expected"); + let true_num = forward_val(&mut context, "trueNum").expect("value expected"); + let true_string = forward_val(&mut context, "trueString").expect("value expected"); + let true_bool = forward_val(&mut context, "trueBool").expect("value expected"); + + // Values should all be objects + assert!(true_val.is_object()); + assert!(true_num.is_object()); + assert!(true_string.is_object()); + assert!(true_bool.is_object()); + + // Values should all be truthy + assert!(true_val.to_boolean()); + assert!(true_num.to_boolean()); + assert!(true_string.to_boolean()); + assert!(true_bool.to_boolean()); +} + +#[test] +fn instances_have_correct_proto_set() { + let mut context = Context::default(); + let init = r#" + var boolInstance = new Boolean(true); + var boolProto = Boolean.prototype; + "#; + + eprintln!("{}", forward(&mut context, init)); + let bool_instance = forward_val(&mut context, "boolInstance").expect("value expected"); + let bool_prototype = forward_val(&mut context, "boolProto").expect("value expected"); + + assert_eq!( + &*bool_instance.as_object().unwrap().prototype(), + &bool_prototype.as_object().cloned() + ); +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/console/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/console/mod.rs new file mode 100644 index 0000000..b2b31b7 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/console/mod.rs @@ -0,0 +1,634 @@ +//! Boa's implementation of JavaScript's `console` Web API object. +//! +//! The `console` object can be accessed from any global object. +//! +//! The specifics of how it works varies from browser to browser, but there is a de facto set of features that are typically provided. +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [WHATWG `console` specification][spec] +//! +//! [spec]: https://console.spec.whatwg.org/ +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Console + +#![allow(clippy::print_stdout)] + +#[cfg(test)] +mod tests; + +use crate::{ + builtins::{BuiltIn, JsArgs}, + object::ObjectInitializer, + value::{display::display_obj, JsValue, Numeric}, + Context, JsResult, JsString, +}; +use boa_profiler::Profiler; +use rustc_hash::FxHashMap; +use std::time::SystemTime; +use tap::{Conv, Pipe}; + +/// This represents the different types of log messages. +#[derive(Debug)] +enum LogMessage { + Log(String), + Info(String), + Warn(String), + Error(String), +} + +/// Helper function for logging messages. +fn logger(msg: LogMessage, console_state: &Console) { + let indent = 2 * console_state.groups.len(); + + match msg { + LogMessage::Error(msg) => { + eprintln!("{msg:>indent$}"); + } + LogMessage::Log(msg) | LogMessage::Info(msg) | LogMessage::Warn(msg) => { + println!("{msg:>indent$}"); + } + } +} + +/// This represents the `console` formatter. +pub fn formatter(data: &[JsValue], context: &mut Context<'_>) -> JsResult { + match data { + [] => Ok(String::new()), + [val] => Ok(val.to_string(context)?.to_std_string_escaped()), + data => { + let mut formatted = String::new(); + let mut arg_index = 1; + let target = data + .get_or_undefined(0) + .to_string(context)? + .to_std_string_escaped(); + let mut chars = target.chars(); + while let Some(c) = chars.next() { + if c == '%' { + let fmt = chars.next().unwrap_or('%'); + match fmt { + /* integer */ + 'd' | 'i' => { + let arg = match data.get_or_undefined(arg_index).to_numeric(context)? { + Numeric::Number(r) => (r.floor() + 0.0).to_string(), + Numeric::BigInt(int) => int.to_string(), + }; + formatted.push_str(&arg); + arg_index += 1; + } + /* float */ + 'f' => { + let arg = data.get_or_undefined(arg_index).to_number(context)?; + formatted.push_str(&format!("{arg:.6}")); + arg_index += 1; + } + /* object, FIXME: how to render this properly? */ + 'o' | 'O' => { + let arg = data.get_or_undefined(arg_index); + formatted.push_str(&arg.display().to_string()); + arg_index += 1; + } + /* string */ + 's' => { + let arg = data + .get_or_undefined(arg_index) + .to_string(context)? + .to_std_string_escaped(); + formatted.push_str(&arg); + arg_index += 1; + } + '%' => formatted.push('%'), + /* TODO: %c is not implemented */ + c => { + formatted.push('%'); + formatted.push(c); + } + } + } else { + formatted.push(c); + }; + } + + /* unformatted data */ + for rest in data.iter().skip(arg_index) { + formatted.push_str(&format!( + " {}", + rest.to_string(context)?.to_std_string_escaped() + )); + } + + Ok(formatted) + } + } +} + +/// This is the internal console object state. +#[derive(Debug, Default)] +pub(crate) struct Console { + count_map: FxHashMap, + timer_map: FxHashMap, + groups: Vec, +} + +impl BuiltIn for Console { + const NAME: &'static str = "console"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + ObjectInitializer::new(context) + .function(Self::assert, "assert", 0) + .function(Self::clear, "clear", 0) + .function(Self::debug, "debug", 0) + .function(Self::error, "error", 0) + .function(Self::info, "info", 0) + .function(Self::log, "log", 0) + .function(Self::trace, "trace", 0) + .function(Self::warn, "warn", 0) + .function(Self::error, "exception", 0) + .function(Self::count, "count", 0) + .function(Self::count_reset, "countReset", 0) + .function(Self::group, "group", 0) + .function(Self::group, "groupCollapsed", 0) + .function(Self::group_end, "groupEnd", 0) + .function(Self::time, "time", 0) + .function(Self::time_log, "timeLog", 0) + .function(Self::time_end, "timeEnd", 0) + .function(Self::dir, "dir", 0) + .function(Self::dir, "dirxml", 0) + .build() + .conv::() + .pipe(Some) + } +} + +impl Console { + /// The name of the object. + pub(crate) const NAME: &'static str = "console"; + + /// `console.assert(condition, ...data)` + /// + /// Prints a JavaScript value to the standard error if first argument evaluates to `false` or there + /// were no arguments. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [WHATWG `console` specification][spec] + /// + /// [spec]: https://console.spec.whatwg.org/#assert + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/assert + pub(crate) fn assert( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let assertion = args.get(0).map_or(false, JsValue::to_boolean); + + if !assertion { + let mut args: Vec = args.iter().skip(1).cloned().collect(); + let message = "Assertion failed".to_string(); + if args.is_empty() { + args.push(JsValue::new(message)); + } else if !args[0].is_string() { + args.insert(0, JsValue::new(message)); + } else { + let concat = format!("{message}: {}", args[0].display()); + args[0] = JsValue::new(concat); + } + + logger( + LogMessage::Error(formatter(&args, context)?), + context.console(), + ); + } + + Ok(JsValue::undefined()) + } + + /// `console.clear()` + /// + /// Removes all groups and clears console if possible. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [WHATWG `console` specification][spec] + /// + /// [spec]: https://console.spec.whatwg.org/#clear + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/clear + #[allow(clippy::unnecessary_wraps)] + pub(crate) fn clear( + _: &JsValue, + _: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + context.console_mut().groups.clear(); + Ok(JsValue::undefined()) + } + + /// `console.debug(...data)` + /// + /// Prints a JavaScript values with "debug" logLevel. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [WHATWG `console` specification][spec] + /// + /// [spec]: https://console.spec.whatwg.org/#debug + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/debug + pub(crate) fn debug( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + logger( + LogMessage::Log(formatter(args, context)?), + context.console(), + ); + Ok(JsValue::undefined()) + } + + /// `console.error(...data)` + /// + /// Prints a JavaScript values with "error" logLevel. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [WHATWG `console` specification][spec] + /// + /// [spec]: https://console.spec.whatwg.org/#error + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/error + pub(crate) fn error( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + logger( + LogMessage::Error(formatter(args, context)?), + context.console(), + ); + Ok(JsValue::undefined()) + } + + /// `console.info(...data)` + /// + /// Prints a JavaScript values with "info" logLevel. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [WHATWG `console` specification][spec] + /// + /// [spec]: https://console.spec.whatwg.org/#info + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/info + pub(crate) fn info( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + logger( + LogMessage::Info(formatter(args, context)?), + context.console(), + ); + Ok(JsValue::undefined()) + } + + /// `console.log(...data)` + /// + /// Prints a JavaScript values with "log" logLevel. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [WHATWG `console` specification][spec] + /// + /// [spec]: https://console.spec.whatwg.org/#log + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/log + pub(crate) fn log( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + logger( + LogMessage::Log(formatter(args, context)?), + context.console(), + ); + Ok(JsValue::undefined()) + } + + fn get_stack_trace(context: &mut Context<'_>) -> Vec { + let mut stack_trace: Vec = vec![]; + + for frame in context.vm.frames.iter().rev() { + stack_trace.push( + context + .interner() + .resolve_expect(frame.code_block.name) + .to_string(), + ); + } + + stack_trace + } + + /// `console.trace(...data)` + /// + /// Prints a stack trace with "trace" logLevel, optionally labelled by data. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [WHATWG `console` specification][spec] + /// + /// [spec]: https://console.spec.whatwg.org/#trace + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/trace + pub(crate) fn trace( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + if !args.is_empty() { + logger( + LogMessage::Log(formatter(args, context)?), + context.console(), + ); + + let stack_trace_dump = Self::get_stack_trace(context).join("\n"); + logger(LogMessage::Log(stack_trace_dump), context.console()); + } + + Ok(JsValue::undefined()) + } + + /// `console.warn(...data)` + /// + /// Prints a JavaScript values with "warn" logLevel. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [WHATWG `console` specification][spec] + /// + /// [spec]: https://console.spec.whatwg.org/#warn + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/warn + pub(crate) fn warn( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + logger( + LogMessage::Warn(formatter(args, context)?), + context.console(), + ); + Ok(JsValue::undefined()) + } + + /// `console.count(label)` + /// + /// Prints number of times the function was called with that particular label. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [WHATWG `console` specification][spec] + /// + /// [spec]: https://console.spec.whatwg.org/#count + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/count + pub(crate) fn count( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let label = match args.get(0) { + Some(value) => value.to_string(context)?, + None => "default".into(), + }; + + let msg = format!("count {}:", label.to_std_string_escaped()); + let c = context.console_mut().count_map.entry(label).or_insert(0); + *c += 1; + + logger(LogMessage::Info(format!("{msg} {c}")), context.console()); + Ok(JsValue::undefined()) + } + + /// `console.countReset(label)` + /// + /// Resets the counter for label. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [WHATWG `console` specification][spec] + /// + /// [spec]: https://console.spec.whatwg.org/#countreset + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/countReset + pub(crate) fn count_reset( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let label = match args.get(0) { + Some(value) => value.to_string(context)?, + None => "default".into(), + }; + + context.console_mut().count_map.remove(&label); + + logger( + LogMessage::Warn(format!("countReset {}", label.to_std_string_escaped())), + context.console(), + ); + + Ok(JsValue::undefined()) + } + + /// Returns current system time in ms. + fn system_time_in_ms() -> u128 { + let now = SystemTime::now(); + now.duration_since(SystemTime::UNIX_EPOCH) + .expect("negative duration") + .as_millis() + } + + /// `console.time(label)` + /// + /// Starts the timer for given label. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [WHATWG `console` specification][spec] + /// + /// [spec]: https://console.spec.whatwg.org/#time + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/time + pub(crate) fn time( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let label = match args.get(0) { + Some(value) => value.to_string(context)?, + None => "default".into(), + }; + + if context.console().timer_map.get(&label).is_some() { + logger( + LogMessage::Warn(format!( + "Timer '{}' already exist", + label.to_std_string_escaped() + )), + context.console(), + ); + } else { + let time = Self::system_time_in_ms(); + context.console_mut().timer_map.insert(label, time); + } + + Ok(JsValue::undefined()) + } + + /// `console.timeLog(label, ...data)` + /// + /// Prints elapsed time for timer with given label. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [WHATWG `console` specification][spec] + /// + /// [spec]: https://console.spec.whatwg.org/#timelog + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/timeLog + pub(crate) fn time_log( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let label = match args.get(0) { + Some(value) => value.to_string(context)?, + None => "default".into(), + }; + + if let Some(t) = context.console().timer_map.get(&label) { + let time = Self::system_time_in_ms(); + let mut concat = format!("{}: {} ms", label.to_std_string_escaped(), time - t); + for msg in args.iter().skip(1) { + concat = concat + " " + &msg.display().to_string(); + } + logger(LogMessage::Log(concat), context.console()); + } else { + logger( + LogMessage::Warn(format!( + "Timer '{}' doesn't exist", + label.to_std_string_escaped() + )), + context.console(), + ); + } + + Ok(JsValue::undefined()) + } + + /// `console.timeEnd(label)` + /// + /// Removes the timer with given label. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [WHATWG `console` specification][spec] + /// + /// [spec]: https://console.spec.whatwg.org/#timeend + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/timeEnd + pub(crate) fn time_end( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let label = match args.get(0) { + Some(value) => value.to_string(context)?, + None => "default".into(), + }; + + if let Some(t) = context.console_mut().timer_map.remove(&label) { + let time = Self::system_time_in_ms(); + logger( + LogMessage::Info(format!( + "{}: {} ms - timer removed", + label.to_std_string_escaped(), + time - t + )), + context.console(), + ); + } else { + logger( + LogMessage::Warn(format!( + "Timer '{}' doesn't exist", + label.to_std_string_escaped() + )), + context.console(), + ); + } + + Ok(JsValue::undefined()) + } + + /// `console.group(...data)` + /// + /// Adds new group with name from formatted data to stack. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [WHATWG `console` specification][spec] + /// + /// [spec]: https://console.spec.whatwg.org/#group + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/group + pub(crate) fn group( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let group_label = formatter(args, context)?; + + logger( + LogMessage::Info(format!("group: {group_label}")), + context.console(), + ); + context.console_mut().groups.push(group_label); + + Ok(JsValue::undefined()) + } + + /// `console.groupEnd(label)` + /// + /// Removes the last group from the stack. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [WHATWG `console` specification][spec] + /// + /// [spec]: https://console.spec.whatwg.org/#groupend + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/groupEnd + #[allow(clippy::unnecessary_wraps)] + pub(crate) fn group_end( + _: &JsValue, + _: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + context.console_mut().groups.pop(); + + Ok(JsValue::undefined()) + } + + /// `console.dir(item, options)` + /// + /// Prints info about item + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [WHATWG `console` specification][spec] + /// + /// [spec]: https://console.spec.whatwg.org/#dir + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/dir + #[allow(clippy::unnecessary_wraps)] + pub(crate) fn dir( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + logger( + LogMessage::Info(display_obj(args.get_or_undefined(0), true)), + context.console(), + ); + Ok(JsValue::undefined()) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/console/tests.rs b/javascript-engine/external/boa/boa_engine/src/builtins/console/tests.rs new file mode 100644 index 0000000..11f03a3 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/console/tests.rs @@ -0,0 +1,68 @@ +use crate::{builtins::console::formatter, Context, JsValue}; + +#[test] +fn formatter_no_args_is_empty_string() { + let mut context = Context::default(); + assert_eq!(formatter(&[], &mut context).unwrap(), ""); +} + +#[test] +fn formatter_empty_format_string_is_empty_string() { + let mut context = Context::default(); + let val = JsValue::new(""); + assert_eq!(formatter(&[val], &mut context).unwrap(), ""); +} + +#[test] +fn formatter_format_without_args_renders_verbatim() { + let mut context = Context::default(); + let val = [JsValue::new("%d %s %% %f")]; + let res = formatter(&val, &mut context).unwrap(); + assert_eq!(res, "%d %s %% %f"); +} + +#[test] +fn formatter_empty_format_string_concatenates_rest_of_args() { + let mut context = Context::default(); + + let val = [ + JsValue::new(""), + JsValue::new("to powinno zostać"), + JsValue::new("połączone"), + ]; + let res = formatter(&val, &mut context).unwrap(); + assert_eq!(res, " to powinno zostać połączone"); +} + +#[test] +fn formatter_utf_8_checks() { + let mut context = Context::default(); + + let val = [ + JsValue::new("Są takie chwile %dą %są tu%sów %привет%ź".to_string()), + JsValue::new(123), + JsValue::new(1.23), + JsValue::new("ł"), + ]; + let res = formatter(&val, &mut context).unwrap(); + assert_eq!(res, "Są takie chwile 123ą 1.23ą tułów %привет%ź"); +} + +#[test] +fn formatter_trailing_format_leader_renders() { + let mut context = Context::default(); + + let val = [JsValue::new("%%%%%"), JsValue::new("|")]; + let res = formatter(&val, &mut context).unwrap(); + assert_eq!(res, "%%% |"); +} + +#[test] +#[allow(clippy::approx_constant)] +fn formatter_float_format_works() { + let mut context = Context::default(); + + let val = [JsValue::new("%f"), JsValue::new(3.1415)]; + let res = formatter(&val, &mut context).unwrap(); + assert_eq!(res, "3.141500"); +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/dataview/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/dataview/mod.rs new file mode 100644 index 0000000..fcf7738 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/dataview/mod.rs @@ -0,0 +1,1067 @@ +//! Boa's implementation of ECMAScript's global `DataView` object. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! - [MDN documentation][mdn] +//! +//! [spec]: https://tc39.es/ecma262/#sec-dataview-objects +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView + +use crate::{ + builtins::{array_buffer::SharedMemoryOrder, typed_array::TypedArrayKind, BuiltIn, JsArgs}, + context::intrinsics::StandardConstructors, + error::JsNativeError, + native_function::NativeFunction, + object::{ + internal_methods::get_prototype_from_constructor, ConstructorBuilder, + FunctionObjectBuilder, JsObject, ObjectData, + }, + property::Attribute, + symbol::JsSymbol, + value::JsValue, + Context, JsResult, +}; +use boa_gc::{Finalize, Trace}; +use tap::{Conv, Pipe}; + +/// The internal representation of a `DataView` object. +#[derive(Debug, Clone, Trace, Finalize)] +pub struct DataView { + pub(crate) viewed_array_buffer: JsObject, + pub(crate) byte_length: u64, + pub(crate) byte_offset: u64, +} + +impl BuiltIn for DataView { + const NAME: &'static str = "DataView"; + + fn init(context: &mut Context<'_>) -> Option { + let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE; + + let get_buffer = + FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(Self::get_buffer)) + .name("get buffer") + .build(); + + let get_byte_length = + FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(Self::get_byte_length)) + .name("get byteLength") + .build(); + + let get_byte_offset = + FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(Self::get_byte_offset)) + .name("get byteOffset") + .build(); + + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context.intrinsics().constructors().data_view().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .accessor("buffer", Some(get_buffer), None, flag_attributes) + .accessor("byteLength", Some(get_byte_length), None, flag_attributes) + .accessor("byteOffset", Some(get_byte_offset), None, flag_attributes) + .method(Self::get_big_int64, "getBigInt64", 1) + .method(Self::get_big_uint64, "getBigUint64", 1) + .method(Self::get_float32, "getFloat32", 1) + .method(Self::get_float64, "getFloat64", 1) + .method(Self::get_int8, "getInt8", 1) + .method(Self::get_int16, "getInt16", 1) + .method(Self::get_int32, "getInt32", 1) + .method(Self::get_uint8, "getUint8", 1) + .method(Self::get_uint16, "getUint16", 1) + .method(Self::get_uint32, "getUint32", 1) + .method(Self::set_big_int64, "setBigInt64", 2) + .method(Self::set_big_uint64, "setBigUint64", 2) + .method(Self::set_float32, "setFloat32", 2) + .method(Self::set_float64, "setFloat64", 2) + .method(Self::set_int8, "setInt8", 2) + .method(Self::set_int16, "setInt16", 2) + .method(Self::set_int32, "setInt32", 2) + .method(Self::set_uint8, "setUint8", 2) + .method(Self::set_uint16, "setUint16", 2) + .method(Self::set_uint32, "setUint32", 2) + .property( + JsSymbol::to_string_tag(), + Self::NAME, + Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ) + .build() + .conv::() + .pipe(Some) + } +} + +impl DataView { + pub(crate) const LENGTH: usize = 1; + + /// `25.3.2.1 DataView ( buffer [ , byteOffset [ , byteLength ] ] )` + /// + /// The `DataView` view provides a low-level interface for reading and writing multiple number + /// types in a binary `ArrayBuffer`, without having to care about the platform's endianness. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview-buffer-byteoffset-bytelength + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/DataView + pub(crate) fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_length = args.get_or_undefined(2); + + let buffer_obj = args + .get_or_undefined(0) + .as_object() + .ok_or_else(|| JsNativeError::typ().with_message("buffer must be an ArrayBuffer"))?; + + // 1. If NewTarget is undefined, throw a TypeError exception. + let (offset, view_byte_length) = { + if new_target.is_undefined() { + return Err(JsNativeError::typ() + .with_message("new target is undefined") + .into()); + } + // 2. Perform ? RequireInternalSlot(buffer, [[ArrayBufferData]]). + let buffer_borrow = buffer_obj.borrow(); + let buffer = buffer_borrow.as_array_buffer().ok_or_else(|| { + JsNativeError::typ().with_message("buffer must be an ArrayBuffer") + })?; + + // 3. Let offset be ? ToIndex(byteOffset). + let offset = args.get_or_undefined(1).to_index(context)?; + // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if buffer.is_detached_buffer() { + return Err(JsNativeError::typ() + .with_message("ArrayBuffer is detached") + .into()); + } + // 5. Let bufferByteLength be buffer.[[ArrayBufferByteLength]]. + let buffer_byte_length = buffer.array_buffer_byte_length(); + // 6. If offset > bufferByteLength, throw a RangeError exception. + if offset > buffer_byte_length { + return Err(JsNativeError::range() + .with_message("Start offset is outside the bounds of the buffer") + .into()); + } + // 7. If byteLength is undefined, then + let view_byte_length = if byte_length.is_undefined() { + // a. Let viewByteLength be bufferByteLength - offset. + buffer_byte_length - offset + } else { + // 8.a. Let viewByteLength be ? ToIndex(byteLength). + let view_byte_length = byte_length.to_index(context)?; + // 8.b. If offset + viewByteLength > bufferByteLength, throw a RangeError exception. + if offset + view_byte_length > buffer_byte_length { + return Err(JsNativeError::range() + .with_message("Invalid data view length") + .into()); + } + + view_byte_length + }; + (offset, view_byte_length) + }; + + // 9. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%DataView.prototype%", « [[DataView]], [[ViewedArrayBuffer]], [[ByteLength]], [[ByteOffset]] »). + let prototype = + get_prototype_from_constructor(new_target, StandardConstructors::data_view, context)?; + + // 10. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if buffer_obj + .borrow() + .as_array_buffer() + .ok_or_else(|| JsNativeError::typ().with_message("buffer must be an ArrayBuffer"))? + .is_detached_buffer() + { + return Err(JsNativeError::typ() + .with_message("ArrayBuffer can't be detached") + .into()); + } + + let obj = JsObject::from_proto_and_data( + prototype, + ObjectData::data_view(Self { + // 11. Set O.[[ViewedArrayBuffer]] to buffer. + viewed_array_buffer: buffer_obj.clone(), + // 12. Set O.[[ByteLength]] to viewByteLength. + byte_length: view_byte_length, + // 13. Set O.[[ByteOffset]] to offset. + byte_offset: offset, + }), + ); + + // 14. Return O. + Ok(obj.into()) + } + + /// `25.3.4.1 get DataView.prototype.buffer` + /// + /// The buffer accessor property represents the `ArrayBuffer` or `SharedArrayBuffer` referenced + /// by the `DataView` at construction time. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-get-dataview.prototype.buffer + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/buffer + pub(crate) fn get_buffer( + this: &JsValue, + _args: &[JsValue], + _: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be the this value. + // 2. Perform ? RequireInternalSlot(O, [[DataView]]). + let dataview = this.as_object().map(JsObject::borrow); + let dataview = dataview + .as_ref() + .and_then(|obj| obj.as_data_view()) + .ok_or_else(|| JsNativeError::typ().with_message("`this` is not a DataView"))?; + // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. + // 4. Let buffer be O.[[ViewedArrayBuffer]]. + let buffer = dataview.viewed_array_buffer.clone(); + // 5. Return buffer. + Ok(buffer.into()) + } + + /// `25.3.4.1 get DataView.prototype.byteLength` + /// + /// The `byteLength` accessor property represents the length (in bytes) of the dataview. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-get-dataview.prototype.bytelength + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/byteLength + pub(crate) fn get_byte_length( + this: &JsValue, + _args: &[JsValue], + _: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be the this value. + // 2. Perform ? RequireInternalSlot(O, [[DataView]]). + let dataview = this.as_object().map(JsObject::borrow); + let dataview = dataview + .as_ref() + .and_then(|obj| obj.as_data_view()) + .ok_or_else(|| JsNativeError::typ().with_message("`this` is not a DataView"))?; + // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. + // 4. Let buffer be O.[[ViewedArrayBuffer]]. + let buffer_borrow = dataview.viewed_array_buffer.borrow(); + let borrow = buffer_borrow + .as_array_buffer() + .expect("DataView must be constructed with an ArrayBuffer"); + // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if borrow.is_detached_buffer() { + return Err(JsNativeError::typ() + .with_message("ArrayBuffer is detached") + .into()); + } + // 6. Let size be O.[[ByteLength]]. + let size = dataview.byte_length; + // 7. Return 𝔽(size). + Ok(size.into()) + } + + /// `25.3.4.1 get DataView.prototype.byteOffset` + /// + /// The `byteOffset` accessor property represents the offset (in bytes) of this view from the + /// start of its `ArrayBuffer` or `SharedArrayBuffer`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-get-dataview.prototype.byteoffset + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/byteOffset + pub(crate) fn get_byte_offset( + this: &JsValue, + _args: &[JsValue], + _: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be the this value. + // 2. Perform ? RequireInternalSlot(O, [[DataView]]). + let dataview = this.as_object().map(JsObject::borrow); + let dataview = dataview + .as_ref() + .and_then(|obj| obj.as_data_view()) + .ok_or_else(|| JsNativeError::typ().with_message("`this` is not a DataView"))?; + // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. + // 4. Let buffer be O.[[ViewedArrayBuffer]]. + let buffer_borrow = dataview.viewed_array_buffer.borrow(); + let borrow = buffer_borrow + .as_array_buffer() + .expect("DataView must be constructed with an ArrayBuffer"); + // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if borrow.is_detached_buffer() { + return Err(JsNativeError::typ() + .with_message("ArrayBuffer is detached") + .into()); + } + // 6. Let offset be O.[[ByteOffset]]. + let offset = dataview.byte_offset; + // 7. Return 𝔽(offset). + Ok(offset.into()) + } + + /// `25.3.1.1 GetViewValue ( view, requestIndex, isLittleEndian, type )` + /// + /// The abstract operation `GetViewValue` takes arguments view, requestIndex, `isLittleEndian`, + /// and type. It is used by functions on `DataView` instances to retrieve values from the + /// view's buffer. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-getviewvalue + fn get_view_value( + view: &JsValue, + request_index: &JsValue, + is_little_endian: &JsValue, + t: TypedArrayKind, + context: &mut Context<'_>, + ) -> JsResult { + // 1. Perform ? RequireInternalSlot(view, [[DataView]]). + // 2. Assert: view has a [[ViewedArrayBuffer]] internal slot. + let view = view.as_object().map(JsObject::borrow); + let view = view + .as_ref() + .and_then(|obj| obj.as_data_view()) + .ok_or_else(|| JsNativeError::typ().with_message("`this` is not a DataView"))?; + // 3. Let getIndex be ? ToIndex(requestIndex). + let get_index = request_index.to_index(context)?; + + // 4. Set isLittleEndian to ! ToBoolean(isLittleEndian). + let is_little_endian = is_little_endian.to_boolean(); + + // 5. Let buffer be view.[[ViewedArrayBuffer]]. + let buffer = &view.viewed_array_buffer; + let buffer_borrow = buffer.borrow(); + let buffer = buffer_borrow + .as_array_buffer() + .expect("Should be unreachable"); + + // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if buffer.is_detached_buffer() { + return Err(JsNativeError::typ() + .with_message("ArrayBuffer is detached") + .into()); + } + // 7. Let viewOffset be view.[[ByteOffset]]. + let view_offset = view.byte_offset; + + // 8. Let viewSize be view.[[ByteLength]]. + let view_size = view.byte_length; + + // 9. Let elementSize be the Element Size value specified in Table 72 for Element Type type. + let element_size = t.element_size(); + + // 10. If getIndex + elementSize > viewSize, throw a RangeError exception. + if get_index + element_size > view_size { + return Err(JsNativeError::range() + .with_message("Offset is outside the bounds of the DataView") + .into()); + } + + // 11. Let bufferIndex be getIndex + viewOffset. + let buffer_index = get_index + view_offset; + + // 12. Return GetValueFromBuffer(buffer, bufferIndex, type, false, Unordered, isLittleEndian). + Ok(buffer.get_value_from_buffer( + buffer_index, + t, + false, + SharedMemoryOrder::Unordered, + Some(is_little_endian), + )) + } + + /// `25.3.4.5 DataView.prototype.getBigInt64 ( byteOffset [ , littleEndian ] )` + /// + /// The `getBigInt64()` method gets a signed 64-bit integer (long long) at the specified byte + /// offset from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getbigint64 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getBigInt64 + pub(crate) fn get_big_int64( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let is_little_endian = args.get_or_undefined(1); + // 1. Let v be the this value. + // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64). + Self::get_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::BigInt64, + context, + ) + } + + /// `25.3.4.6 DataView.prototype.getBigUint64 ( byteOffset [ , littleEndian ] )` + /// + /// The `getBigUint64()` method gets an unsigned 64-bit integer (unsigned long long) at the + /// specified byte offset from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getbiguint64 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getBigUint64 + pub(crate) fn get_big_uint64( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let is_little_endian = args.get_or_undefined(1); + // 1. Let v be the this value. + // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64). + Self::get_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::BigUint64, + context, + ) + } + + /// `25.3.4.7 DataView.prototype.getBigUint64 ( byteOffset [ , littleEndian ] )` + /// + /// The `getFloat32()` method gets a signed 32-bit float (float) at the specified byte offset + /// from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getfloat32 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat32 + pub(crate) fn get_float32( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let is_little_endian = args.get_or_undefined(1); + // 1. Let v be the this value. + // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64). + Self::get_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::Float32, + context, + ) + } + + /// `25.3.4.8 DataView.prototype.getFloat64 ( byteOffset [ , littleEndian ] )` + /// + /// The `getFloat64()` method gets a signed 64-bit float (double) at the specified byte offset + /// from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getfloat64 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat64 + pub(crate) fn get_float64( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let is_little_endian = args.get_or_undefined(1); + // 1. Let v be the this value. + // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64). + Self::get_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::Float64, + context, + ) + } + + /// `25.3.4.9 DataView.prototype.getInt8 ( byteOffset [ , littleEndian ] )` + /// + /// The `getInt8()` method gets a signed 8-bit integer (byte) at the specified byte offset + /// from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getint8 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt8 + pub(crate) fn get_int8( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let is_little_endian = args.get_or_undefined(1); + // 1. Let v be the this value. + // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64). + Self::get_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::Int8, + context, + ) + } + + /// `25.3.4.10 DataView.prototype.getInt16 ( byteOffset [ , littleEndian ] )` + /// + /// The `getInt16()` method gets a signed 16-bit integer (short) at the specified byte offset + /// from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getint16 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt16 + pub(crate) fn get_int16( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let is_little_endian = args.get_or_undefined(1); + // 1. Let v be the this value. + // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64). + Self::get_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::Int16, + context, + ) + } + + /// `25.3.4.11 DataView.prototype.getInt32 ( byteOffset [ , littleEndian ] )` + /// + /// The `getInt32()` method gets a signed 32-bit integer (long) at the specified byte offset + /// from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getint32 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt32 + pub(crate) fn get_int32( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let is_little_endian = args.get_or_undefined(1); + // 1. Let v be the this value. + // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64). + Self::get_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::Int32, + context, + ) + } + + /// `25.3.4.12 DataView.prototype.getUint8 ( byteOffset [ , littleEndian ] )` + /// + /// The `getUint8()` method gets an unsigned 8-bit integer (unsigned byte) at the specified + /// byte offset from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getuint8 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint8 + pub(crate) fn get_uint8( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let is_little_endian = args.get_or_undefined(1); + // 1. Let v be the this value. + // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64). + Self::get_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::Uint8, + context, + ) + } + + /// `25.3.4.13 DataView.prototype.getUint16 ( byteOffset [ , littleEndian ] )` + /// + /// The `getUint16()` method gets an unsigned 16-bit integer (unsigned short) at the specified + /// byte offset from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getuint16 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint16 + pub(crate) fn get_uint16( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let is_little_endian = args.get_or_undefined(1); + // 1. Let v be the this value. + // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64). + Self::get_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::Uint16, + context, + ) + } + + /// `25.3.4.14 DataView.prototype.getUint32 ( byteOffset [ , littleEndian ] )` + /// + /// The `getUint32()` method gets an unsigned 32-bit integer (unsigned long) at the specified + /// byte offset from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getuint32 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint32 + pub(crate) fn get_uint32( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let is_little_endian = args.get_or_undefined(1); + // 1. Let v be the this value. + // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64). + Self::get_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::Uint32, + context, + ) + } + + /// `25.3.1.1 SetViewValue ( view, requestIndex, isLittleEndian, type )` + /// + /// The abstract operation `SetViewValue` takes arguments view, requestIndex, `isLittleEndian`, + /// type, and value. It is used by functions on `DataView` instances to store values into the + /// view's buffer. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-setviewvalue + fn set_view_value( + view: &JsValue, + request_index: &JsValue, + is_little_endian: &JsValue, + t: TypedArrayKind, + value: &JsValue, + context: &mut Context<'_>, + ) -> JsResult { + // 1. Perform ? RequireInternalSlot(view, [[DataView]]). + // 2. Assert: view has a [[ViewedArrayBuffer]] internal slot. + let view = view.as_object().map(JsObject::borrow); + let view = view + .as_ref() + .and_then(|obj| obj.as_data_view()) + .ok_or_else(|| JsNativeError::typ().with_message("`this` is not a DataView"))?; + // 3. Let getIndex be ? ToIndex(requestIndex). + let get_index = request_index.to_index(context)?; + + let number_value = if t.is_big_int_element_type() { + // 4. If ! IsBigIntElementType(type) is true, let numberValue be ? ToBigInt(value). + value.to_bigint(context)?.into() + } else { + // 5. Otherwise, let numberValue be ? ToNumber(value). + value.to_number(context)?.into() + }; + + // 6. Set isLittleEndian to ! ToBoolean(isLittleEndian). + let is_little_endian = is_little_endian.to_boolean(); + // 7. Let buffer be view.[[ViewedArrayBuffer]]. + let buffer = &view.viewed_array_buffer; + let mut buffer_borrow = buffer.borrow_mut(); + let buffer = buffer_borrow + .as_array_buffer_mut() + .expect("Should be unreachable"); + + // 8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if buffer.is_detached_buffer() { + return Err(JsNativeError::typ() + .with_message("ArrayBuffer is detached") + .into()); + } + + // 9. Let viewOffset be view.[[ByteOffset]]. + let view_offset = view.byte_offset; + + // 10. Let viewSize be view.[[ByteLength]]. + let view_size = view.byte_length; + + // 11. Let elementSize be the Element Size value specified in Table 72 for Element Type type. + let element_size = t.element_size(); + + // 12. If getIndex + elementSize > viewSize, throw a RangeError exception. + if get_index + element_size > view_size { + return Err(JsNativeError::range() + .with_message("Offset is outside the bounds of DataView") + .into()); + } + + // 13. Let bufferIndex be getIndex + viewOffset. + let buffer_index = get_index + view_offset; + + // 14. Return SetValueInBuffer(buffer, bufferIndex, type, numberValue, false, Unordered, isLittleEndian). + buffer.set_value_in_buffer( + buffer_index, + t, + &number_value, + SharedMemoryOrder::Unordered, + Some(is_little_endian), + context, + ) + } + + /// `25.3.4.15 DataView.prototype.setBigInt64 ( byteOffset, value [ , littleEndian ] )` + /// + /// The `setBigInt64()` method stores a signed 64-bit integer (long long) value at the + /// specified byte offset from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setbigint64 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setBigInt64 + pub(crate) fn set_big_int64( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let value = args.get_or_undefined(1); + let is_little_endian = args.get_or_undefined(2); + // 1. Let v be the this value. + // 2. Return ? SetViewValue(v, byteOffset, littleEndian, BigUint64, value). + Self::set_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::BigInt64, + value, + context, + ) + } + + /// `25.3.4.16 DataView.prototype.setBigUint64 ( byteOffset, value [ , littleEndian ] )` + /// + /// The `setBigUint64()` method stores an unsigned 64-bit integer (unsigned long long) value at + /// the specified byte offset from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setbiguint64 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setBigUint64 + pub(crate) fn set_big_uint64( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let value = args.get_or_undefined(1); + let is_little_endian = args.get_or_undefined(2); + // 1. Let v be the this value. + // 2. Return ? SetViewValue(v, byteOffset, littleEndian, BigUint64, value). + Self::set_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::BigUint64, + value, + context, + ) + } + + /// `25.3.4.17 DataView.prototype.setFloat32 ( byteOffset, value [ , littleEndian ] )` + /// + /// The `setFloat32()` method stores a signed 32-bit float (float) value at the specified byte + /// offset from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setfloat32 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat32 + pub(crate) fn set_float32( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let value = args.get_or_undefined(1); + let is_little_endian = args.get_or_undefined(2); + // 1. Let v be the this value. + // 2. Return ? SetViewValue(v, byteOffset, littleEndian, Float32, value). + Self::set_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::Float32, + value, + context, + ) + } + + /// `25.3.4.18 DataView.prototype.setFloat64 ( byteOffset, value [ , littleEndian ] )` + /// + /// The `setFloat64()` method stores a signed 64-bit float (double) value at the specified byte + /// offset from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setfloat64 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat64 + pub(crate) fn set_float64( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let value = args.get_or_undefined(1); + let is_little_endian = args.get_or_undefined(2); + // 1. Let v be the this value. + // 2. Return ? SetViewValue(v, byteOffset, littleEndian, Float64, value). + Self::set_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::Float64, + value, + context, + ) + } + + /// `25.3.4.19 DataView.prototype.setInt8 ( byteOffset, value [ , littleEndian ] )` + /// + /// The `setInt8()` method stores a signed 8-bit integer (byte) value at the specified byte + /// offset from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setint8 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt8 + pub(crate) fn set_int8( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let value = args.get_or_undefined(1); + let is_little_endian = args.get_or_undefined(2); + // 1. Let v be the this value. + // 2. Return ? SetViewValue(v, byteOffset, littleEndian, Int8, value). + Self::set_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::Int8, + value, + context, + ) + } + + /// `25.3.4.20 DataView.prototype.setInt16 ( byteOffset, value [ , littleEndian ] )` + /// + /// The `setInt16()` method stores a signed 16-bit integer (short) value at the specified byte + /// offset from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setint16 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt16 + pub(crate) fn set_int16( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let value = args.get_or_undefined(1); + let is_little_endian = args.get_or_undefined(2); + // 1. Let v be the this value. + // 2. Return ? SetViewValue(v, byteOffset, littleEndian, Int16, value). + Self::set_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::Int16, + value, + context, + ) + } + + /// `25.3.4.21 DataView.prototype.setInt32 ( byteOffset, value [ , littleEndian ] )` + /// + /// The `setInt32()` method stores a signed 32-bit integer (long) value at the specified byte + /// offset from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setint32 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt32 + pub(crate) fn set_int32( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let value = args.get_or_undefined(1); + let is_little_endian = args.get_or_undefined(2); + // 1. Let v be the this value. + // 2. Return ? SetViewValue(v, byteOffset, littleEndian, Int32, value). + Self::set_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::Int32, + value, + context, + ) + } + + /// `25.3.4.22 DataView.prototype.setUint8 ( byteOffset, value [ , littleEndian ] )` + /// + /// The `setUint8()` method stores an unsigned 8-bit integer (byte) value at the specified byte + /// offset from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setuint8 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint8 + pub(crate) fn set_uint8( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let value = args.get_or_undefined(1); + let is_little_endian = args.get_or_undefined(2); + // 1. Let v be the this value. + // 2. Return ? SetViewValue(v, byteOffset, littleEndian, Uint8, value). + Self::set_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::Uint8, + value, + context, + ) + } + + /// `25.3.4.23 DataView.prototype.setUint16 ( byteOffset, value [ , littleEndian ] )` + /// + /// The `setUint16()` method stores an unsigned 16-bit integer (unsigned short) value at the + /// specified byte offset from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setuint16 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint16 + pub(crate) fn set_uint16( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let value = args.get_or_undefined(1); + let is_little_endian = args.get_or_undefined(2); + // 1. Let v be the this value. + // 2. Return ? SetViewValue(v, byteOffset, littleEndian, Uint16, value). + Self::set_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::Uint16, + value, + context, + ) + } + + /// `25.3.4.24 DataView.prototype.setUint32 ( byteOffset, value [ , littleEndian ] )` + /// + /// The `setUint32()` method stores an unsigned 32-bit integer (unsigned long) value at the + /// specified byte offset from the start of the `DataView`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setuint32 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint32 + pub(crate) fn set_uint32( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let byte_offset = args.get_or_undefined(0); + let value = args.get_or_undefined(1); + let is_little_endian = args.get_or_undefined(2); + // 1. Let v be the this value. + // 2. Return ? SetViewValue(v, byteOffset, littleEndian, Uint32, value). + Self::set_view_value( + this, + byte_offset, + is_little_endian, + TypedArrayKind::Uint32, + value, + context, + ) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/date/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/date/mod.rs new file mode 100644 index 0000000..2890737 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/date/mod.rs @@ -0,0 +1,1484 @@ +//! Boa's implementation of ECMAScript's `Date` object. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! - [MDN documentation][mdn] +//! +//! [spec]: https://tc39.es/ecma262/#sec-date-objects +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date + +mod utils; +use utils::{make_date, make_day, make_time, replace_params, time_clip, DateParameters}; + +#[cfg(test)] +mod tests; + +use super::JsArgs; +use crate::{ + builtins::BuiltIn, + context::intrinsics::StandardConstructors, + error::JsNativeError, + js_string, + object::{ + internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData, + }, + string::utf16, + symbol::JsSymbol, + value::{IntegerOrNan, JsValue, PreferredType}, + Context, JsError, JsResult, +}; +use boa_profiler::Profiler; +use chrono::prelude::*; +use std::fmt::Display; +use tap::{Conv, Pipe}; + +/// Extracts `Some` from an `Option` or returns `NaN` if the object contains `None`. +macro_rules! some_or_nan { + ($v:expr) => { + match $v { + Some(dt) => dt, + None => return Ok(JsValue::from(f64::NAN)), + } + }; +} + +/// Gets a mutable reference to the inner `Date` object of `val` and stores it on `var`, or returns +/// a `TypeError` if `val` is not a `Date` object. +macro_rules! get_mut_date { + (let $var:ident = $val:expr) => { + let mut $var = $val + .as_object() + .map(JsObject::borrow_mut) + .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?; + let $var = $var + .as_date_mut() + .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?; + }; +} + +/// Abstract operation [`thisTimeValue`][spec]. +/// +/// [spec]: https://tc39.es/ecma262/#sec-thistimevalue +pub(super) fn this_time_value(value: &JsValue) -> JsResult> { + Ok(value + .as_object() + .and_then(|obj| obj.borrow().as_date().copied()) + .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))? + .0) +} + +/// The internal representation of a `Date` object. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Date(Option); + +impl Date { + /// Creates a new `Date`. + pub(crate) const fn new(dt: Option) -> Self { + Self(dt) + } + + /// Converts the `Date` into a `JsValue`, mapping `None` to `NaN` and `Some(datetime)` to + /// `JsValue::from(datetime.timestamp_millis())`. + fn as_value(&self) -> JsValue { + self.0 + .map_or_else(|| f64::NAN.into(), |dt| dt.timestamp_millis().into()) + } +} + +impl Display for Date { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(v) = self.to_local() { + write!(f, "{}", v.format("%a %b %d %Y %H:%M:%S GMT%:z")) + } else { + write!(f, "Invalid Date") + } + } +} + +impl Default for Date { + fn default() -> Self { + Self(Some(Utc::now().naive_utc())) + } +} + +impl BuiltIn for Date { + const NAME: &'static str = "Date"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context.intrinsics().constructors().date().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .method(Self::get_date::, "getDate", 0) + .method(Self::get_day::, "getDay", 0) + .method(Self::get_full_year::, "getFullYear", 0) + .method(Self::get_hours::, "getHours", 0) + .method(Self::get_milliseconds::, "getMilliseconds", 0) + .method(Self::get_minutes::, "getMinutes", 0) + .method(Self::get_month::, "getMonth", 0) + .method(Self::get_seconds::, "getSeconds", 0) + .method(Self::get_time, "getTime", 0) + .method(Self::get_timezone_offset, "getTimezoneOffset", 0) + .method(Self::get_date::, "getUTCDate", 0) + .method(Self::get_day::, "getUTCDay", 0) + .method(Self::get_full_year::, "getUTCFullYear", 0) + .method(Self::get_hours::, "getUTCHours", 0) + .method(Self::get_milliseconds::, "getUTCMilliseconds", 0) + .method(Self::get_minutes::, "getUTCMinutes", 0) + .method(Self::get_month::, "getUTCMonth", 0) + .method(Self::get_seconds::, "getUTCSeconds", 0) + .method(Self::get_year, "getYear", 0) + .static_method(Self::now, "now", 0) + .static_method(Self::parse, "parse", 1) + .method(Self::set_date::, "setDate", 1) + .method(Self::set_full_year::, "setFullYear", 3) + .method(Self::set_hours::, "setHours", 4) + .method(Self::set_milliseconds::, "setMilliseconds", 1) + .method(Self::set_minutes::, "setMinutes", 3) + .method(Self::set_month::, "setMonth", 2) + .method(Self::set_seconds::, "setSeconds", 2) + .method(Self::set_time, "setTime", 1) + .method(Self::set_date::, "setUTCDate", 1) + .method(Self::set_full_year::, "setUTCFullYear", 3) + .method(Self::set_hours::, "setUTCHours", 4) + .method(Self::set_milliseconds::, "setUTCMilliseconds", 1) + .method(Self::set_minutes::, "setUTCMinutes", 3) + .method(Self::set_month::, "setUTCMonth", 2) + .method(Self::set_seconds::, "setUTCSeconds", 2) + .method(Self::set_year, "setYear", 1) + .method(Self::to_date_string, "toDateString", 0) + .method(Self::to_gmt_string, "toGMTString", 0) + .method(Self::to_iso_string, "toISOString", 0) + .method(Self::to_json, "toJSON", 1) + .method(Self::to_locale_date_string, "toLocaleDateString", 0) + .method(Self::to_locale_string, "toLocaleString", 0) + .method(Self::to_locale_time_string, "toLocaleTimeString", 0) + .method(Self::to_string, "toString", 0) + .method(Self::to_time_string, "toTimeString", 0) + .method(Self::to_utc_string, "toUTCString", 0) + .static_method(Self::utc, "UTC", 7) + .method(Self::value_of, "valueOf", 0) + .method( + Self::to_primitive, + (JsSymbol::to_primitive(), "[Symbol.toPrimitive]"), + 1, + ) + .build() + .conv::() + .pipe(Some) + } +} + +impl Date { + /// The amount of arguments this function object takes. + pub(crate) const LENGTH: usize = 7; + + /// [`Date ( ...values )`][spec] + /// + /// - When called as a function, returns a string displaying the current time in the UTC timezone. + /// - When called as a constructor, it returns a new `Date` object from the provided arguments. + /// The [MDN documentation][mdn] has a more extensive explanation on the usages and return + /// values for all possible arguments. + /// + /// [spec]: https://tc39.es/ecma262/#sec-date-constructor + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date + pub(crate) fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. If NewTarget is undefined, then + if new_target.is_undefined() { + // a. Let now be the time value (UTC) identifying the current time. + // b. Return ToDateString(now). + return Ok(JsValue::new( + Local::now() + .format("%a %b %d %Y %H:%M:%S GMT%:z") + .to_string(), + )); + } + // 2. Let numberOfArgs be the number of elements in values. + let dv = match args { + // 3. If numberOfArgs = 0, then + [] => { + // a. Let dv be the time value (UTC) identifying the current time. + Date::default() + } + // 4. Else if numberOfArgs = 1, then + // a. Let value be values[0]. + [value] => match value + .as_object() + .and_then(|obj| obj.borrow().as_date().copied()) + { + // b. If value is an Object and value has a [[DateValue]] internal slot, then + Some(dt) => { + // i. Let tv be ! thisTimeValue(value). + dt + } + // c. Else, + None => { + // i. Let v be ? ToPrimitive(value). + match value.to_primitive(context, PreferredType::Default)? { + // ii. If v is a String, then + JsValue::String(ref str) => { + // 1. Assert: The next step never returns an abrupt completion because v is a String. + // 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). + Date( + str.to_std_string() + .ok() + .and_then(|s| { + chrono::DateTime::parse_from_rfc3339(s.as_str()).ok() + }) + .map(|dt| dt.naive_utc()), + ) + } + // iii. Else, + v => { + // Directly convert to integer + // 1. Let tv be ? ToNumber(v). + Date( + v.to_integer_or_nan(context)? + .as_integer() + // d. Let dv be TimeClip(tv). + .and_then(time_clip) + .and_then(NaiveDateTime::from_timestamp_millis), + ) + } + } + } + }, + // 5. Else, + _ => { + // Separating this into its own function to simplify the logic. + Date( + Self::construct_date(args, context)? + .and_then(|dt| Local.from_local_datetime(&dt).earliest()) + .map(|dt| dt.naive_utc()), + ) + } + }; + + // 6. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%Date.prototype%", « [[DateValue]] »). + let prototype = + get_prototype_from_constructor(new_target, StandardConstructors::date, context)?; + + // 7. Set O.[[DateValue]] to dv. + let obj = JsObject::from_proto_and_data(prototype, ObjectData::date(dv)); + + // 8. Return O. + Ok(obj.into()) + } + + /// Gets the timestamp from a list of component values. + fn construct_date( + values: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult> { + // 1. Let y be ? ToNumber(year). + let Some(mut year) = values.get_or_undefined(0).to_integer_or_nan(context)?.as_integer() else { + return Ok(None); + }; + + // 2. If month is present, let m be ? ToNumber(month); else let m be +0𝔽. + let Some(month) = values.get(1).map_or(Ok(Some(0)), |value| { + value + .to_integer_or_nan(context) + .map(IntegerOrNan::as_integer) + })? else { + return Ok(None); + }; + + // 3. If date is present, let dt be ? ToNumber(date); else let dt be 1𝔽. + let Some(date) = values.get(2).map_or(Ok(Some(1)), |value| { + value + .to_integer_or_nan(context) + .map(IntegerOrNan::as_integer) + })? else { + return Ok(None); + }; + + // 4. If hours is present, let h be ? ToNumber(hours); else let h be +0𝔽. + let Some(hour) = values.get(3).map_or(Ok(Some(0)), |value| { + value + .to_integer_or_nan(context) + .map(IntegerOrNan::as_integer) + })? else { + return Ok(None); + }; + + // 5. If minutes is present, let min be ? ToNumber(minutes); else let min be +0𝔽. + let Some(min) = values.get(4).map_or(Ok(Some(0)), |value| { + value + .to_integer_or_nan(context) + .map(IntegerOrNan::as_integer) + })? else { + return Ok(None); + }; + + // 6. If seconds is present, let s be ? ToNumber(seconds); else let s be +0𝔽. + let Some(sec) = values.get(5).map_or(Ok(Some(0)), |value| { + value + .to_integer_or_nan(context) + .map(IntegerOrNan::as_integer) + })? else { + return Ok(None); + }; + + // 7. If ms is present, let milli be ? ToNumber(ms); else let milli be +0𝔽. + let Some(ms) = values.get(6).map_or(Ok(Some(0)), |value| { + value + .to_integer_or_nan(context) + .map(IntegerOrNan::as_integer) + })? else { + return Ok(None); + }; + + // 8. If y is NaN, let yr be NaN. + // 9. Else, + // a. Let yi be ! ToIntegerOrInfinity(y). + // b. If 0 ≤ yi ≤ 99, let yr be 1900𝔽 + 𝔽(yi); otherwise, let yr be y. + if (0..=99).contains(&year) { + year += 1900; + } + + // 10. Return TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli))). + // PLEASE RUST TEAM GIVE US TRY BLOCKS ;-; + let timestamp = (move || { + let day = make_day(year, month, date)?; + let time = make_time(hour, min, sec, ms)?; + make_date(day, time) + })(); + + Ok(timestamp + .and_then(time_clip) + .and_then(NaiveDateTime::from_timestamp_millis)) + } + + /// `Date.now()` + /// + /// The static `Date.now()` method returns the number of milliseconds elapsed since January 1, 1970 00:00:00 UTC. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.now + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now + #[allow(clippy::unnecessary_wraps)] + pub(crate) fn now(_: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult { + Ok(JsValue::new(Utc::now().timestamp_millis())) + } + + /// `Date.parse()` + /// + /// The `Date.parse()` method parses a string representation of a date, and returns the number of milliseconds since + /// January 1, 1970, 00:00:00 UTC or `NaN` if the string is unrecognized or, in some cases, contains illegal date + /// values. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.parse + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse + pub(crate) fn parse( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // This method is implementation-defined and discouraged, so we just require the same format as the string + // constructor. + + let date = some_or_nan!(args.get(0)); + + let date = date.to_string(context)?; + + Ok(date + .to_std_string() + .ok() + .and_then(|s| DateTime::parse_from_rfc3339(s.as_str()).ok()) + .and_then(|date| time_clip(date.naive_utc().timestamp_millis())) + .map_or_else(|| JsValue::from(f64::NAN), JsValue::from)) + } + + /// `Date.UTC()` + /// + /// The `Date.UTC()` method accepts parameters similar to the `Date` constructor, but treats them as UTC. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.utc + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/UTC + pub(crate) fn utc( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let t = some_or_nan!(Self::construct_date(args, context)?); + + Ok(JsValue::from(t.timestamp_millis())) + } + + /// [`Date.prototype.getDate ( )`][local] and + /// [`Date.prototype.getUTCDate ( )`][utc]. + /// + /// The `getDate()` method returns the day of the month for the specified date. + /// + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getdate + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcdate + pub(crate) fn get_date( + this: &JsValue, + _args: &[JsValue], + _context: &mut Context<'_>, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + let mut t = some_or_nan!(this_time_value(this)?); + if LOCAL { + t = Local.from_utc_datetime(&t).naive_local(); + } + + // 3. Return DateFromTime(LocalTime(t)). + Ok(JsValue::new(t.day())) + } + + /// [`Date.prototype.getDay ( )`][local] and + /// [`Date.prototype.getUTCDay ( )`][utc]. + /// + /// The `getDay()` method returns the day of the week for the specified date, where 0 represents + /// Sunday. + /// + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getday + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcday + pub(crate) fn get_day( + this: &JsValue, + _args: &[JsValue], + _context: &mut Context<'_>, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + let mut t = some_or_nan!(this_time_value(this)?); + if LOCAL { + t = Local.from_utc_datetime(&t).naive_local(); + } + + // 3. Return WeekDay(LocalTime(t)). + Ok(JsValue::new(t.weekday().num_days_from_sunday())) + } + + /// [`Date.prototype.getYear()`][spec]. + /// + /// The `getYear()` method returns the year in the specified date according to local time. + /// Because `getYear()` does not return full years ("year 2000 problem"), it is no longer used + /// and has been replaced by the `getFullYear()` method. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getyear + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getYear + pub(crate) fn get_year( + this: &JsValue, + _args: &[JsValue], + _context: &mut Context<'_>, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + let t = some_or_nan!(this_time_value(this)?); + + // 3. Return YearFromTime(LocalTime(t)) - 1900𝔽. + let local = Local.from_utc_datetime(&t); + Ok(JsValue::from(local.year() - 1900)) + } + + /// [`Date.prototype.getFullYear ( )`][local] and + /// [`Date.prototype.getUTCFullYear ( )`][utc]. + /// + /// The `getFullYear()` method returns the year of the specified date. + /// + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getfullyear + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcfullyear + pub(crate) fn get_full_year( + this: &JsValue, + _args: &[JsValue], + _context: &mut Context<'_>, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + let mut t = some_or_nan!(this_time_value(this)?); + if LOCAL { + t = Local.from_utc_datetime(&t).naive_local(); + } + + // 3. Return YearFromTime(LocalTime(t)). + Ok(JsValue::new(t.year())) + } + + /// [`Date.prototype.getHours ( )`][local] and + /// [`Date.prototype.getUTCHours ( )`][utc]. + /// + /// The `getHours()` method returns the hour for the specified date. + /// + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.gethours + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutchours + pub(crate) fn get_hours( + this: &JsValue, + _args: &[JsValue], + _context: &mut Context<'_>, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + let mut t = some_or_nan!(this_time_value(this)?); + if LOCAL { + t = Local.from_utc_datetime(&t).naive_local(); + } + + // 3. Return HourFromTime(LocalTime(t)). + Ok(JsValue::new(t.hour())) + } + + /// [`Date.prototype.getMilliseconds ( )`][local] and + /// [`Date.prototype.getUTCMilliseconds ( )`][utc]. + /// + /// The `getMilliseconds()` method returns the milliseconds in the specified date. + /// + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getmilliseconds + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcmilliseconds + pub(crate) fn get_milliseconds( + this: &JsValue, + _args: &[JsValue], + _context: &mut Context<'_>, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + let mut t = some_or_nan!(this_time_value(this)?); + if LOCAL { + t = Local.from_utc_datetime(&t).naive_local(); + } + + // 3. Return msFromTime(LocalTime(t)). + Ok(JsValue::new(t.timestamp_subsec_millis())) + } + + /// [`Date.prototype.getMinutes ( )`][local] and + /// [`Date.prototype.getUTCMinutes ( )`][utc]. + /// + /// The `getMinutes()` method returns the minutes in the specified date. + /// + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getminutes + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcminutes + pub(crate) fn get_minutes( + this: &JsValue, + _args: &[JsValue], + _context: &mut Context<'_>, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + let mut t = some_or_nan!(this_time_value(this)?); + if LOCAL { + t = Local.from_utc_datetime(&t).naive_local(); + } + + // 3. Return MinFromTime(LocalTime(t)). + Ok(JsValue::new(t.minute())) + } + + /// [`Date.prototype.getMonth ( )`][local] and + /// [`Date.prototype.getUTCMonth ( )`][utc]. + /// + /// The `getMonth()` method returns the month in the specified date, as a zero-based value + /// (where zero indicates the first month of the year). + /// + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getmonth + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcmonth + pub(crate) fn get_month( + this: &JsValue, + _args: &[JsValue], + _context: &mut Context<'_>, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + let mut t = some_or_nan!(this_time_value(this)?); + if LOCAL { + t = Local.from_utc_datetime(&t).naive_local(); + } + + // 3. Return MonthFromTime(LocalTime(t)). + Ok(JsValue::new(t.month0())) + } + + /// [`Date.prototype.getSeconds ( )`][local] and + /// [`Date.prototype.getUTCSeconds ( )`][utc]. + /// + /// The `getSeconds()` method returns the seconds in the specified date. + /// + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getseconds + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcseconds + pub(crate) fn get_seconds( + this: &JsValue, + _args: &[JsValue], + _context: &mut Context<'_>, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + let mut t = some_or_nan!(this_time_value(this)?); + if LOCAL { + t = Local.from_utc_datetime(&t).naive_local(); + } + + // 3. Return SecFromTime(LocalTime(t)) + Ok(JsValue::new(t.second())) + } + + /// `Date.prototype.getTime()`. + /// + /// The `getTime()` method returns the number of milliseconds since the Unix Epoch. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.gettime + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTime + pub(crate) fn get_time( + this: &JsValue, + _args: &[JsValue], + _context: &mut Context<'_>, + ) -> JsResult { + // 1. Return ? thisTimeValue(this value). + let t = some_or_nan!(this_time_value(this)?); + Ok(JsValue::from(t.timestamp_millis())) + } + + /// `Date.prototype.getTimeZoneOffset()`. + /// + /// The `getTimezoneOffset()` method returns the time zone difference, in minutes, from current locale (host system + /// settings) to UTC. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.gettimezoneoffset + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset + pub(crate) fn get_timezone_offset( + this: &JsValue, + _: &[JsValue], + _: &mut Context<'_>, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + some_or_nan!(this_time_value(this)?); + + // 3. Return (t - LocalTime(t)) / msPerMinute. + Ok(JsValue::from(-Local::now().offset().local_minus_utc() / 60)) + } + + /// [`Date.prototype.setDate ( date )`][local] and + /// [`Date.prototype.setUTCDate ( date )`][utc]. + /// + /// The `setDate()` method sets the day of the `Date` object relative to the beginning of the + /// currently set month. + /// + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setdate + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcdate + pub(crate) fn set_date( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let t be LocalTime(? thisTimeValue(this value)). + get_mut_date!(let t = this); + + // 2. Let dt be ? ToNumber(date). + let date = args.get_or_undefined(0).to_integer_or_nan(context)?; + + // 3. If t is NaN, return NaN. + let datetime = some_or_nan!(t.0); + + // 4. Set t to LocalTime(t). + // 5. Let newDate be MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t)). + // 6. Let u be TimeClip(UTC(newDate)). + let datetime = replace_params( + datetime, + DateParameters { + date: Some(date), + ..Default::default() + }, + LOCAL, + ); + + // 7. Set the [[DateValue]] internal slot of this Date object to u. + *t = Date(datetime); + + // 8. Return u. + Ok(t.as_value()) + } + + /// [`Date.prototype.setFullYear ( year [ , month [ , date ] ] )`][local] and + /// [Date.prototype.setUTCFullYear ( year [ , month [ , date ] ] )][utc]. + /// + /// The `setFullYear()` method sets the full year for a specified date and returns the new + /// timestamp. + /// + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setfullyear + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcfullyear + pub(crate) fn set_full_year( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + get_mut_date!(let t = this); + + // 2. If t is NaN, set t to +0𝔽; otherwise, set t to LocalTime(t). + let datetime = match t.0 { + Some(dt) => dt, + None if LOCAL => { + let Some(datetime) = Local + .from_local_datetime(&NaiveDateTime::default()) + .earliest() + .as_ref() + .map(DateTime::naive_utc) else { + *t = Date(None); + return Ok(t.as_value()) + }; + datetime + } + None => NaiveDateTime::default(), + }; + + // 3. Let y be ? ToNumber(year). + let year = args.get_or_undefined(0).to_integer_or_nan(context)?; + + // 4. If month is not present, let m be MonthFromTime(t); otherwise, let m be ? ToNumber(month). + let month = args + .get(1) + .map(|v| v.to_integer_or_nan(context)) + .transpose()?; + + // 5. If date is not present, let dt be DateFromTime(t); otherwise, let dt be ? ToNumber(date). + let date = args + .get(2) + .map(|v| v.to_integer_or_nan(context)) + .transpose()?; + + // 6. Let newDate be MakeDate(MakeDay(y, m, dt), TimeWithinDay(t)). + // 7. Let u be TimeClip(UTC(newDate)). + let datetime = replace_params( + datetime, + DateParameters { + year: Some(year), + month, + date, + ..Default::default() + }, + LOCAL, + ); + + // 8. Set the [[DateValue]] internal slot of this Date object to u. + *t = Date(datetime); + + // 9. Return u. + Ok(t.as_value()) + } + + /// [`Date.prototype.setHours ( hour [ , min [ , sec [ , ms ] ] ] )`][local] and + /// [`Date.prototype.setUTCHours ( hour [ , min [ , sec [ , ms ] ] ] )`][utc]. + /// + /// The `setHours()` method sets the hours for a specified date, and returns the number + /// of milliseconds since January 1, 1970 00:00:00 UTC until the time represented by the + /// updated `Date` instance. + /// + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.sethours + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutchours + pub(crate) fn set_hours( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + get_mut_date!(let t = this); + + // 2. Let h be ? ToNumber(hour). + let hour = args.get_or_undefined(0).to_integer_or_nan(context)?; + + // 3. If min is present, let m be ? ToNumber(min). + let minute = args + .get(1) + .map(|v| v.to_integer_or_nan(context)) + .transpose()?; + + // 4. If sec is present, let s be ? ToNumber(sec). + let second = args + .get(2) + .map(|v| v.to_integer_or_nan(context)) + .transpose()?; + + // 5. If ms is present, let milli be ? ToNumber(ms). + let millisecond = args + .get(3) + .map(|v| v.to_integer_or_nan(context)) + .transpose()?; + + // 6. If t is NaN, return NaN. + let datetime = some_or_nan!(t.0); + + // 7. Set t to LocalTime(t). + // 8. If min is not present, let m be MinFromTime(t). + // 9. If sec is not present, let s be SecFromTime(t). + // 10. If ms is not present, let milli be msFromTime(t). + // 11. Let date be MakeDate(Day(t), MakeTime(h, m, s, milli)). + // 12. Let u be TimeClip(UTC(date)). + let datetime = replace_params( + datetime, + DateParameters { + hour: Some(hour), + minute, + second, + millisecond, + ..Default::default() + }, + LOCAL, + ); + + // 13. Set the [[DateValue]] internal slot of this Date object to u. + *t = Date(datetime); + + // 14. Return u. + Ok(t.as_value()) + } + + /// [`Date.prototype.setMilliseconds ( ms )`[local] and + /// [`Date.prototype.setUTCMilliseconds ( ms )`][utc]. + /// + /// The `setMilliseconds()` method sets the milliseconds for a specified date according to local time. + /// + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setmilliseconds + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcmilliseconds + pub(crate) fn set_milliseconds( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + // 1. Let t be LocalTime(? thisTimeValue(this value)). + get_mut_date!(let t = this); + + // 2. Set ms to ? ToNumber(ms). + let ms = args.get_or_undefined(0).to_integer_or_nan(context)?; + + // 3. If t is NaN, return NaN. + let datetime = some_or_nan!(t.0); + + // 4. Set t to LocalTime(t). + // 5. Let time be MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms). + // 6. Let u be TimeClip(UTC(MakeDate(Day(t), time))). + let datetime = replace_params( + datetime, + DateParameters { + millisecond: Some(ms), + ..Default::default() + }, + LOCAL, + ); + + // 7. Set the [[DateValue]] internal slot of this Date object to u. + *t = Date(datetime); + + // 8. Return u. + Ok(t.as_value()) + } + + /// [`Date.prototype.setMinutes ( min [ , sec [ , ms ] ] )`][local] and + /// [`Date.prototype.setUTCMinutes ( min [ , sec [ , ms ] ] )`][utc]. + /// + /// The `setMinutes()` method sets the minutes for a specified date. + /// + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setminutes + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcminutes + pub(crate) fn set_minutes( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + get_mut_date!(let t = this); + + // 2. Let m be ? ToNumber(min). + let minute = args.get_or_undefined(0).to_integer_or_nan(context)?; + + // 3. If sec is present, let s be ? ToNumber(sec). + let second = args + .get(1) + .map(|v| v.to_integer_or_nan(context)) + .transpose()?; + + // 4. If ms is present, let milli be ? ToNumber(ms). + let millisecond = args + .get(2) + .map(|v| v.to_integer_or_nan(context)) + .transpose()?; + + // 5. If t is NaN, return NaN. + let datetime = some_or_nan!(t.0); + + // 6. Set t to LocalTime(t). + // 7. If sec is not present, let s be SecFromTime(t). + // 8. If ms is not present, let milli be msFromTime(t). + // 9. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)). + // 10. Let u be TimeClip(UTC(date)). + let datetime = replace_params( + datetime, + DateParameters { + minute: Some(minute), + second, + millisecond, + ..Default::default() + }, + LOCAL, + ); + + // 11. Set the [[DateValue]] internal slot of this Date object to u. + *t = Date(datetime); + + // 12. Return u. + Ok(t.as_value()) + } + + /// [`Date.prototype.setMonth ( month [ , date ] )`][local] and + /// [`Date.prototype.setUTCMonth ( month [ , date ] )`][utc]. + /// + /// The `setMonth()` method sets the month for a specified date according to the currently set + /// year. + /// + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setmonth + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcmonth + pub(crate) fn set_month( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + get_mut_date!(let t = this); + + // 2. Let m be ? ToNumber(month). + let month = args.get_or_undefined(0).to_integer_or_nan(context)?; + + // 3. If date is present, let dt be ? ToNumber(date). + let date = args + .get(1) + .map(|v| v.to_integer_or_nan(context)) + .transpose()?; + + // 4. If t is NaN, return NaN. + let datetime = some_or_nan!(t.0); + + // 5. Set t to LocalTime(t). + // 6. If date is not present, let dt be DateFromTime(t). + // 7. Let newDate be MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t)). + // 8. Let u be TimeClip(UTC(newDate)). + let datetime = replace_params( + datetime, + DateParameters { + month: Some(month), + date, + ..Default::default() + }, + LOCAL, + ); + + // 9. Set the [[DateValue]] internal slot of this Date object to u. + *t = Date(datetime); + + // 10. Return u. + Ok(t.as_value()) + } + + /// [`Date.prototype.setSeconds ( sec [ , ms ] )`[local] and + /// [`Date.prototype.setUTCSeconds ( sec [ , ms ] )`][utc]. + /// + /// The `setSeconds()` method sets the seconds for a specified date. + /// + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setseconds + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcseconds + pub(crate) fn set_seconds( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + get_mut_date!(let t = this); + + // 2. Let s be ? ToNumber(sec). + let second = args.get_or_undefined(0).to_integer_or_nan(context)?; + + // 3. If ms is present, let milli be ? ToNumber(ms). + let millisecond = args + .get(1) + .map(|v| v.to_integer_or_nan(context)) + .transpose()?; + + // 4. If t is NaN, return NaN. + let datetime = some_or_nan!(t.0); + + // 5. Set t to LocalTime(t). + // 6. If ms is not present, let milli be msFromTime(t). + // 7. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)). + // 8. Let u be TimeClip(UTC(date)). + let datetime = replace_params( + datetime, + DateParameters { + second: Some(second), + millisecond, + ..Default::default() + }, + LOCAL, + ); + + // 9. Set the [[DateValue]] internal slot of this Date object to u. + *t = Date(datetime); + + // 10. Return u. + Ok(t.as_value()) + } + + /// [`Date.prototype.setYear()`][spec]. + /// + /// The `setYear()` method sets the year for a specified date according to local time. + /// + /// # Note + /// + /// The [`Self::set_full_year`] method is preferred for nearly all purposes, because it avoids + /// the “year 2000 problem.” + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setYear + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setyear + pub(crate) fn set_year( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + get_mut_date!(let t = this); + + // 2. Let y be ? ToNumber(year). + // 5. Let yi be ! ToIntegerOrInfinity(y). + let year = args.get_or_undefined(0).to_integer_or_nan(context)?; + + // 3. If t is NaN, set t to +0𝔽; otherwise, set t to LocalTime(t). + let Some(datetime) = t.0.or_else(|| { + Local + .from_local_datetime(&NaiveDateTime::default()) + .earliest() + .as_ref() + .map(DateTime::naive_utc) + }) else { + *t = Date(None); + return Ok(t.as_value()); + }; + + // 4. If y is NaN, then + let Some(mut year) = year.as_integer() else { + // a. Set the [[DateValue]] internal slot of this Date object to NaN. + *t = Date(None); + + // b. Return NaN. + return Ok(t.as_value()); + }; + + // 6. If 0 ≤ yi ≤ 99, let yyyy be 1900𝔽 + 𝔽(yi). + // 7. Else, let yyyy be y. + if (0..=99).contains(&year) { + year += 1900; + } + + // 8. Let d be MakeDay(yyyy, MonthFromTime(t), DateFromTime(t)). + // 9. Let date be UTC(MakeDate(d, TimeWithinDay(t))). + let datetime = replace_params( + datetime, + DateParameters { + year: Some(IntegerOrNan::Integer(year)), + ..Default::default() + }, + true, + ); + + // 10. Set the [[DateValue]] internal slot of this Date object to TimeClip(date). + *t = Date(datetime); + + // 11. Return the value of the [[DateValue]] internal slot of this Date object. + Ok(t.as_value()) + } + + /// [`Date.prototype.setTime()`][spec]. + /// + /// The `setTime()` method sets the Date object to the time represented by a number of milliseconds + /// since January 1, 1970, 00:00:00 UTC. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.settime + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setTime + pub(crate) fn set_time( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Perform ? thisTimeValue(this value). + get_mut_date!(let t = this); + + // 2. Let t be ? ToNumber(time). + // 3. Let v be TimeClip(t). + let timestamp = args + .get_or_undefined(0) + .to_integer_or_nan(context)? + .as_integer() + .and_then(time_clip) + .and_then(NaiveDateTime::from_timestamp_millis); + + // 4. Set the [[DateValue]] internal slot of this Date object to v. + *t = Date(timestamp); + + // 5. Return v. + Ok(t.as_value()) + } + + /// [`Date.prototype.toDateString()`][spec]. + /// + /// The `toDateString()` method returns the date portion of a Date object in English. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.todatestring + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toDateString + pub(crate) fn to_date_string( + this: &JsValue, + _: &[JsValue], + _: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be this Date object. + // 2. Let tv be ? thisTimeValue(O). + let Some(tv) = this_time_value(this)? else { + // 3. If tv is NaN, return "Invalid Date". + return Ok(js_string!("Invalid Date").into()); + }; + + // 4. Let t be LocalTime(tv). + // 5. Return DateString(t). + Ok(Local::now() + .timezone() + .from_utc_datetime(&tv) + .format("%a %b %d %Y") + .to_string() + .into()) + } + + /// [`Date.prototype.toISOString()`][spec]. + /// + /// The `toISOString()` method returns a string in simplified extended ISO format + /// ([ISO 8601][iso8601]). + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [iso8601]: http://en.wikipedia.org/wiki/ISO_8601 + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.toisostring + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString + pub(crate) fn to_iso_string( + this: &JsValue, + _: &[JsValue], + _: &mut Context<'_>, + ) -> JsResult { + let t = this_time_value(this)? + .ok_or_else(|| JsNativeError::range().with_message("Invalid time value"))?; + Ok(Utc::now() + .timezone() + .from_utc_datetime(&t) + .format("%Y-%m-%dT%H:%M:%S.%3fZ") + .to_string() + .into()) + } + + /// [`Date.prototype.toJSON()`][spec]. + /// + /// The `toJSON()` method returns a string representation of the `Date` object. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tojson + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toJSON + pub(crate) fn to_json( + this: &JsValue, + _: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be ? ToObject(this value). + let o = this.to_object(context)?; + + // 2. Let tv be ? ToPrimitive(O, number). + let tv = this.to_primitive(context, PreferredType::Number)?; + + // 3. If Type(tv) is Number and tv is not finite, return null. + if let Some(number) = tv.as_number() { + if !number.is_finite() { + return Ok(JsValue::null()); + } + } + + // 4. Return ? Invoke(O, "toISOString"). + let func = o.get("toISOString", context)?; + func.call(this, &[], context) + } + + /// [`Date.prototype.toLocaleDateString()`][spec]. + /// + /// The `toLocaleDateString()` method returns the date portion of the given Date instance according + /// to language-specific conventions. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tolocaledatestring + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString + pub(crate) fn to_locale_date_string( + _this: &JsValue, + _args: &[JsValue], + _context: &mut Context<'_>, + ) -> JsResult { + Err(JsError::from_opaque(JsValue::new("Function Unimplemented"))) + } + + /// [`Date.prototype.toLocaleString()`][spec]. + /// + /// The `toLocaleString()` method returns a string representing the specified Date object. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tolocalestring + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString + pub(crate) fn to_locale_string( + _this: &JsValue, + _: &[JsValue], + _context: &mut Context<'_>, + ) -> JsResult { + Err(JsError::from_opaque(JsValue::new( + "Function Unimplemented]", + ))) + } + + /// [`Date.prototype.toLocaleTimeString()`][spec]. + /// + /// The `toLocaleTimeString()` method returns the time portion of a Date object in human readable + /// form in American English. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tolocaletimestring + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleTimeString + pub(crate) fn to_locale_time_string( + _this: &JsValue, + _args: &[JsValue], + _context: &mut Context<'_>, + ) -> JsResult { + Err(JsError::from_opaque(JsValue::new( + "Function Unimplemented]", + ))) + } + + /// [`Date.prototype.toString()`][spec]. + /// + /// The `toString()` method returns a string representing the specified Date object. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tostring + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toString + pub(crate) fn to_string( + this: &JsValue, + _: &[JsValue], + _: &mut Context<'_>, + ) -> JsResult { + // 1. Let tv be ? thisTimeValue(this value). + // 2. Return ToDateString(tv). + let Some(t) = this_time_value(this)? else { + return Ok(js_string!("Invalid Date").into()); + }; + Ok(Local::now() + .timezone() + .from_utc_datetime(&t) + .format("%a %b %d %Y %H:%M:%S GMT%z") + .to_string() + .into()) + } + + /// [`Date.prototype.toTimeString()`][spec]. + /// + /// The `toTimeString()` method returns the time portion of a Date object in human readable form + /// in American English. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.totimestring + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toTimeString + pub(crate) fn to_time_string( + this: &JsValue, + _: &[JsValue], + _: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be this Date object. + // 2. Let tv be ? thisTimeValue(O). + let Some(t) = this_time_value(this)? else { + // 3. If tv is NaN, return "Invalid Date". + return Ok(js_string!("Invalid Date").into()); + }; + + // 4. Let t be LocalTime(tv). + // 5. Return the string-concatenation of TimeString(t) and TimeZoneString(tv). + Ok(Local::now() + .timezone() + .from_utc_datetime(&t) + .format("%H:%M:%S GMT%z") + .to_string() + .into()) + } + + /// [`Date.prototype.toUTCString()`][spec]. + /// + /// The `toUTCString()` method returns a string representing the specified Date object. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.toutcstring + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toUTCString + pub(crate) fn to_utc_string( + this: &JsValue, + _args: &[JsValue], + _context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be this Date object. + let Some(t) = this_time_value(this)? else { + // 3. If tv is NaN, return "Invalid Date". + return Ok(js_string!("Invalid Date").into()) + }; + + // 2. Let tv be ? thisTimeValue(O). + // 4. Let weekday be the Name of the entry in Table 60 with the Number WeekDay(tv). + // 5. Let month be the Name of the entry in Table 61 with the Number MonthFromTime(tv). + // 6. Let day be ToZeroPaddedDecimalString(ℝ(DateFromTime(tv)), 2). + // 7. Let yv be YearFromTime(tv). + // 8. If yv is +0𝔽 or yv > +0𝔽, let yearSign be the empty String; otherwise, let yearSign be "-". + // 9. Let paddedYear be ToZeroPaddedDecimalString(abs(ℝ(yv)), 4). + // 10. Return the string-concatenation of weekday, ",", the code unit 0x0020 (SPACE), day, the + // code unit 0x0020 (SPACE), month, the code unit 0x0020 (SPACE), yearSign, paddedYear, the code + // unit 0x0020 (SPACE), and TimeString(tv) + let utc_string = t.format("%a, %d %b %Y %H:%M:%S GMT").to_string(); + Ok(JsValue::new(utc_string)) + } + + /// [`Date.prototype.valueOf()`][spec]. + /// + /// The `valueOf()` method returns the primitive value of a `Date` object. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.valueof + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/valueOf + pub(crate) fn value_of( + this: &JsValue, + _args: &[JsValue], + _context: &mut Context<'_>, + ) -> JsResult { + // 1. Return ? thisTimeValue(this value). + Ok(Date(this_time_value(this)?).as_value()) + } + + /// [`Date.prototype [ @@toPrimitive ] ( hint )`][spec]. + /// + /// The \[@@toPrimitive\]() method converts a Date object to a primitive value. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype-@@toprimitive + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/@@toPrimitive + pub(crate) fn to_primitive( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be the this value. + // 2. If Type(O) is not Object, throw a TypeError exception. + let o = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Date.prototype[@@toPrimitive] called on non object") + })?; + + let hint = args.get_or_undefined(0); + + let try_first = match hint.as_string() { + // 3. If hint is "string" or "default", then + // a. Let tryFirst be string. + Some(string) if string == utf16!("string") || string == utf16!("default") => { + PreferredType::String + } + // 4. Else if hint is "number", then + // a. Let tryFirst be number. + Some(number) if number == utf16!("number") => PreferredType::Number, + // 5. Else, throw a TypeError exception. + _ => { + return Err(JsNativeError::typ() + .with_message("Date.prototype[@@toPrimitive] called with invalid hint") + .into()) + } + }; + + // 6. Return ? OrdinaryToPrimitive(O, tryFirst). + o.ordinary_to_primitive(context, try_first) + } + + /// [`Date.prototype.toGMTString ( )`][spec]. + /// + /// The `toGMTString()` method converts a date to a string, using Internet Greenwich Mean Time + /// (GMT) conventions. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.togmtstring + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toGMTString + pub(crate) fn to_gmt_string( + this: &JsValue, + _args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // The initial value of the "toGMTString" property is %Date.prototype.toUTCString% + Self::to_utc_string(this, &[], context) + } + + /// Converts the `Date` to a local `DateTime`. + /// + /// If the `Date` is invalid (i.e. NAN), this function will return `None`. + pub(crate) fn to_local(self) -> Option> { + self.0.map(|utc| Local.from_utc_datetime(&utc)) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/date/tests.rs b/javascript-engine/external/boa/boa_engine/src/builtins/date/tests.rs new file mode 100644 index 0000000..07324b8 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/date/tests.rs @@ -0,0 +1,1410 @@ +use crate::{forward, forward_val, Context, JsValue}; +use chrono::{prelude::*, Duration}; + +// NOTE: Javascript Uses 0-based months, where chrono uses 1-based months. Many of the assertions look wrong because of +// this. + +fn datetime_from_local( + year: i32, + month: u32, + date: u32, + hour: u32, + minute: u32, + second: u32, + millisecond: u32, +) -> NaiveDateTime { + Local + .from_local_datetime( + &NaiveDate::from_ymd_opt(year, month, date) + .unwrap() + .and_hms_milli_opt(hour, minute, second, millisecond) + .unwrap(), + ) + .earliest() + .unwrap() + .naive_utc() +} + +fn forward_dt(context: &mut Context<'_>, src: &str) -> Option { + let date_time = forward_val(context, src).unwrap(); + let date_time = date_time.as_object().unwrap(); + let date_time = date_time.borrow(); + date_time.as_date().unwrap().0 +} + +#[test] +fn date_display() { + let dt = super::Date(None); + assert_eq!("[Invalid Date]", format!("[{dt}]")); + + let cd = super::Date::default(); + assert_eq!( + format!( + "[{}]", + cd.to_local().unwrap().format("%a %b %d %Y %H:%M:%S GMT%:z") + ), + format!("[{cd}]") + ); +} + +#[test] +fn date_this_time_value() { + let mut context = Context::default(); + + let error = forward_val( + &mut context, + "({toString: Date.prototype.toString}).toString()", + ) + .unwrap_err(); + let error = error.as_native().unwrap(); + assert_eq!("\'this\' is not a Date", error.message()); +} + +#[test] +fn date_ctor_call() { + let mut context = Context::default(); + + let dt1 = forward_dt(&mut context, "new Date()"); + + std::thread::sleep(std::time::Duration::from_millis(1)); + + let dt2 = forward_dt(&mut context, "new Date()"); + + assert_ne!(dt1, dt2); +} + +#[test] +fn date_ctor_call_string() { + let mut context = Context::default(); + + let date_time = forward_dt(&mut context, "new Date('2020-06-08T09:16:15.779-06:30')"); + + // Internal date is expressed as UTC + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2020, 6, 8) + .unwrap() + .and_hms_milli_opt(15, 46, 15, 779) + .unwrap() + ), + date_time + ); +} + +#[test] +fn date_ctor_call_string_invalid() { + let mut context = Context::default(); + + let date_time = forward_dt(&mut context, "new Date('nope')"); + assert_eq!(None, date_time); +} + +#[test] +fn date_ctor_call_number() { + let mut context = Context::default(); + + let date_time = forward_dt(&mut context, "new Date(1594199775779)"); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), + date_time + ); +} + +#[test] +fn date_ctor_call_date() { + let mut context = Context::default(); + + let date_time = forward_dt(&mut context, "new Date(new Date(1594199775779))"); + + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), + date_time + ); +} + +#[test] +fn date_ctor_call_multiple() { + let mut context = Context::default(); + + let date_time = forward_dt(&mut context, "new Date(2020, 6, 8, 9, 16, 15, 779)"); + + assert_eq!( + Some(datetime_from_local(2020, 7, 8, 9, 16, 15, 779)), + date_time + ); +} + +#[test] +fn date_ctor_call_multiple_90s() { + let mut context = Context::default(); + + let date_time = forward_dt(&mut context, "new Date(99, 6, 8, 9, 16, 15, 779)"); + + assert_eq!( + Some(datetime_from_local(1999, 7, 8, 9, 16, 15, 779)), + date_time + ); +} + +#[test] +fn date_ctor_call_multiple_nan() { + fn check(src: &str) { + let mut context = Context::default(); + let date_time = forward_dt(&mut context, src); + assert_eq!(None, date_time); + } + + check("new Date(1/0, 6, 8, 9, 16, 15, 779)"); + check("new Date(2020, 1/0, 8, 9, 16, 15, 779)"); + check("new Date(2020, 6, 1/0, 9, 16, 15, 779)"); + check("new Date(2020, 6, 8, 1/0, 16, 15, 779)"); + check("new Date(2020, 6, 8, 9, 1/0, 15, 779)"); + check("new Date(2020, 6, 8, 9, 16, 1/0, 779)"); + check("new Date(2020, 6, 8, 9, 16, 15, 1/0)"); +} + +#[test] +fn date_ctor_now_call() { + let mut context = Context::default(); + + let date_time = forward(&mut context, "Date.now()"); + let dt1 = date_time.parse::().unwrap(); + + std::thread::sleep(std::time::Duration::from_millis(1)); + + let date_time = forward(&mut context, "Date.now()"); + let dt2 = date_time.parse::().unwrap(); + + assert_ne!(dt1, dt2); +} + +#[test] +fn date_ctor_parse_call() { + let mut context = Context::default(); + + let date_time = forward_val(&mut context, "Date.parse('2020-06-08T09:16:15.779-07:30')"); + + assert_eq!(JsValue::new(1_591_634_775_779_i64), date_time.unwrap()); +} + +#[test] +fn date_ctor_utc_call() { + let mut context = Context::default(); + + let date_time = forward_val(&mut context, "Date.UTC(2020, 6, 8, 9, 16, 15, 779)"); + + assert_eq!(JsValue::new(1_594_199_775_779_i64), date_time.unwrap()); +} + +#[test] +fn date_ctor_utc_call_nan() { + fn check(src: &str) { + let mut context = Context::default(); + let date_time = forward_val(&mut context, src).unwrap(); + assert_eq!(JsValue::nan(), date_time); + } + + check("Date.UTC(1/0, 6, 8, 9, 16, 15, 779)"); + check("Date.UTC(2020, 1/0, 8, 9, 16, 15, 779)"); + check("Date.UTC(2020, 6, 1/0, 9, 16, 15, 779)"); + check("Date.UTC(2020, 6, 8, 1/0, 16, 15, 779)"); + check("Date.UTC(2020, 6, 8, 9, 1/0, 15, 779)"); + check("Date.UTC(2020, 6, 8, 9, 16, 1/0, 779)"); + check("Date.UTC(2020, 6, 8, 9, 16, 15, 1/0)"); +} + +#[test] +fn date_proto_get_date_call() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(2020, 6, 8, 9, 16, 15, 779).getDate()", + ); + assert_eq!(JsValue::new(8), actual.unwrap()); + + let actual = forward_val(&mut context, "new Date(1/0).getDate()"); + assert_eq!(JsValue::nan(), actual.unwrap()); +} + +#[test] +fn date_proto_get_day_call() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(2020, 6, 8, 9, 16, 15, 779).getDay()", + ); + assert_eq!(JsValue::new(3), actual.unwrap()); + + let actual = forward_val(&mut context, "new Date(1/0).getDay()"); + assert_eq!(JsValue::nan(), actual.unwrap()); +} + +#[test] +fn date_proto_get_full_year_call() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(2020, 6, 8, 9, 16, 15, 779).getFullYear()", + ); + assert_eq!(JsValue::new(2020), actual.unwrap()); + + let actual = forward_val(&mut context, "new Date(1/0).getFullYear()"); + assert_eq!(JsValue::nan(), actual.unwrap()); +} + +#[test] +fn date_proto_get_hours_call() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(2020, 6, 8, 9, 16, 15, 779).getHours()", + ); + assert_eq!(JsValue::new(9), actual.unwrap()); + + let actual = forward_val(&mut context, "new Date(1/0).getHours()"); + assert_eq!(JsValue::nan(), actual.unwrap()); +} + +#[test] +fn date_proto_get_milliseconds_call() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(2020, 6, 8, 9, 16, 15, 779).getMilliseconds()", + ); + assert_eq!(JsValue::new(779), actual.unwrap()); + + let actual = forward_val(&mut context, "new Date(1/0).getMilliseconds()"); + assert_eq!(JsValue::nan(), actual.unwrap()); +} + +#[test] +fn date_proto_get_minutes_call() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(2020, 6, 8, 9, 16, 15, 779).getMinutes()", + ); + assert_eq!(JsValue::new(16), actual.unwrap()); + + let actual = forward_val(&mut context, "new Date(1/0).getMinutes()"); + assert_eq!(JsValue::nan(), actual.unwrap()); +} + +#[test] +fn date_proto_get_month() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(2020, 6, 8, 9, 16, 15, 779).getMonth()", + ); + assert_eq!(JsValue::new(6), actual.unwrap()); + + let actual = forward_val(&mut context, "new Date(1/0).getMonth()"); + assert_eq!(JsValue::nan(), actual.unwrap()); +} + +#[test] +fn date_proto_get_seconds() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(2020, 6, 8, 9, 16, 15, 779).getSeconds()", + ); + assert_eq!(JsValue::new(15), actual.unwrap()); + + let actual = forward_val(&mut context, "new Date(1/0).getSeconds()"); + assert_eq!(JsValue::nan(), actual.unwrap()); +} + +#[test] +fn date_proto_get_time() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(2020, 6, 8, 9, 16, 15, 779).getTime()", + ); + + let ts = Local.with_ymd_and_hms(2020, 7, 8, 9, 16, 15).unwrap() + Duration::milliseconds(779); + + assert_eq!(JsValue::new(ts.timestamp_millis()), actual.unwrap()); + + let actual = forward_val(&mut context, "new Date(1/0).getTime()"); + assert_eq!(JsValue::nan(), actual.unwrap()); +} + +#[test] +fn date_proto_get_year() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(2020, 6, 8, 9, 16, 15, 779).getYear()", + ); + assert_eq!(JsValue::new(120), actual.unwrap()); + + let actual = forward_val(&mut context, "new Date(1/0).getYear()"); + assert_eq!(JsValue::nan(), actual.unwrap()); +} + +#[test] +fn date_proto_get_timezone_offset() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date('1975-08-19T23:15:30+07:00').getTimezoneOffset() === new Date('1975-08-19T23:15:30-02:00').getTimezoneOffset()", + ); + + // NB: Host Settings, not TZ specified in the DateTime. + assert_eq!(JsValue::new(true), actual.unwrap()); + + let actual = forward_val( + &mut context, + "new Date('1975-08-19T23:15:30+07:00').getTimezoneOffset()", + ); + + // The value of now().offset() depends on the host machine, so we have to replicate the method code here. + let offset_seconds = chrono::Local::now().offset().local_minus_utc(); + let offset_minutes = -offset_seconds / 60; + assert_eq!(JsValue::new(offset_minutes), actual.unwrap()); + + let actual = forward_val( + &mut context, + "new Date('1975-08-19T23:15:30+07:00').getTimezoneOffset()", + ); + assert_eq!(JsValue::new(offset_minutes), actual.unwrap()); +} + +#[test] +fn date_proto_get_utc_date_call() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCDate()", + ); + assert_eq!(JsValue::new(8), actual.unwrap()); + + let actual = forward_val(&mut context, "new Date(1/0).getUTCDate()"); + assert_eq!(JsValue::nan(), actual.unwrap()); +} + +#[test] +fn date_proto_get_utc_day_call() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCDay()", + ); + assert_eq!(JsValue::new(3), actual.unwrap()); + + let actual = forward_val(&mut context, "new Date(1/0).getUTCDay()"); + assert_eq!(JsValue::nan(), actual.unwrap()); +} + +#[test] +fn date_proto_get_utc_full_year_call() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCFullYear()", + ); + assert_eq!(JsValue::new(2020), actual.unwrap()); + + let actual = forward_val(&mut context, "new Date(1/0).getUTCFullYear()"); + assert_eq!(JsValue::nan(), actual.unwrap()); +} + +#[test] +fn date_proto_get_utc_hours_call() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCHours()", + ); + assert_eq!(JsValue::new(9), actual.unwrap()); + + let actual = forward_val(&mut context, "new Date(1/0).getUTCHours()"); + assert_eq!(JsValue::nan(), actual.unwrap()); +} + +#[test] +fn date_proto_get_utc_milliseconds_call() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCMilliseconds()", + ); + assert_eq!(JsValue::new(779), actual.unwrap()); + + let actual = forward_val(&mut context, "new Date(1/0).getUTCMilliseconds()"); + assert_eq!(JsValue::nan(), actual.unwrap()); +} + +#[test] +fn date_proto_get_utc_minutes_call() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCMinutes()", + ); + assert_eq!(JsValue::new(16), actual.unwrap()); + + let actual = forward_val(&mut context, "new Date(1/0).getUTCMinutes()"); + assert_eq!(JsValue::nan(), actual.unwrap()); +} + +#[test] +fn date_proto_get_utc_month() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCMonth()", + ); + assert_eq!(JsValue::new(6), actual.unwrap()); + + let actual = forward_val(&mut context, "new Date(1/0).getUTCMonth()"); + assert_eq!(JsValue::nan(), actual.unwrap()); +} + +#[test] +fn date_proto_get_utc_seconds() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCSeconds()", + ); + assert_eq!(JsValue::new(15), actual.unwrap()); + + let actual = forward_val(&mut context, "new Date(1/0).getUTCSeconds()"); + assert_eq!(JsValue::nan(), actual.unwrap()); +} + +#[test] +fn date_proto_set_date() { + let mut context = Context::default(); + + let actual = forward_dt( + &mut context, + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setDate(21); dt", + ); + assert_eq!( + Some(datetime_from_local(2020, 7, 21, 9, 16, 15, 779)), + actual + ); + + // Date wraps to previous month for 0. + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setDate(0); dt", + ); + assert_eq!( + Some(datetime_from_local(2020, 6, 30, 9, 16, 15, 779)), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setDate(1/0); dt", + ); + assert_eq!(None, actual); +} + +#[test] +fn date_proto_set_full_year() { + let mut context = Context::default(); + + let actual = forward_dt( + &mut context, + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setFullYear(2012); dt", + ); + assert_eq!( + Some(datetime_from_local(2012, 7, 8, 9, 16, 15, 779)), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setFullYear(2012, 8); dt", + ); + assert_eq!( + Some(datetime_from_local(2012, 9, 8, 9, 16, 15, 779)), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setFullYear(2012, 8, 10); dt", + ); + assert_eq!( + Some(datetime_from_local(2012, 9, 10, 9, 16, 15, 779)), + actual + ); + + // Out-of-bounds + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 7, 8, 9, 16, 15, 779); dt.setFullYear(2012, 35); dt", + ); + assert_eq!( + Some(datetime_from_local(2014, 12, 8, 9, 16, 15, 779)), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 7, 8, 9, 16, 15, 779); dt.setFullYear(2012, -35); dt", + ); + assert_eq!( + Some(datetime_from_local(2009, 2, 8, 9, 16, 15, 779)), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 7, 8, 9, 16, 15, 779); dt.setFullYear(2012, 9, 950); dt", + ); + assert_eq!( + Some(datetime_from_local(2015, 5, 8, 9, 16, 15, 779)), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 7, 8, 9, 16, 15, 779); dt.setFullYear(2012, 9, -950); dt", + ); + assert_eq!( + Some(datetime_from_local(2010, 2, 23, 9, 16, 15, 779)), + actual + ); +} + +#[test] +fn date_proto_set_hours() { + let mut context = Context::default(); + + let actual = forward_dt( + &mut context, + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setHours(11); dt", + ); + assert_eq!( + Some(datetime_from_local(2020, 7, 8, 11, 16, 15, 779)), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setHours(11, 35); dt", + ); + assert_eq!( + Some(datetime_from_local(2020, 7, 8, 11, 35, 15, 779)), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setHours(11, 35, 23); dt", + ); + assert_eq!( + Some(datetime_from_local(2020, 7, 8, 11, 35, 23, 779)), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setHours(11, 35, 23, 537); dt", + ); + assert_eq!( + Some(datetime_from_local(2020, 7, 8, 11, 35, 23, 537)), + actual + ); + + // Out-of-bounds + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setHours(10000, 20000, 30000, 40123); dt", + ); + assert_eq!( + Some(datetime_from_local(2021, 9, 11, 21, 40, 40, 123)), + actual + ); +} + +#[test] +fn date_proto_set_milliseconds() { + let mut context = Context::default(); + + let actual = forward_dt( + &mut context, + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setMilliseconds(597); dt", + ); + assert_eq!( + Some(datetime_from_local(2020, 7, 8, 9, 16, 15, 597)), + actual + ); + + // Out-of-bounds + // Thorough tests are done by setHours + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setMilliseconds(40123); dt", + ); + assert_eq!( + Some(datetime_from_local(2020, 7, 8, 9, 16, 55, 123)), + actual + ); +} + +#[test] +fn date_proto_set_minutes() { + let mut context = Context::default(); + + let actual = forward_dt( + &mut context, + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setMinutes(11); dt", + ); + assert_eq!( + Some(datetime_from_local(2020, 7, 8, 9, 11, 15, 779)), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setMinutes(11, 35); dt", + ); + assert_eq!( + Some(datetime_from_local(2020, 7, 8, 9, 11, 35, 779)), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setMinutes(11, 35, 537); dt", + ); + assert_eq!( + Some(datetime_from_local(2020, 7, 8, 9, 11, 35, 537)), + actual + ); + + // Out-of-bounds + // Thorough tests are done by setHours + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setMinutes(600000, 30000, 40123); dt", + ); + assert_eq!( + Some(datetime_from_local(2021, 8, 29, 9, 20, 40, 123)), + actual + ); +} + +#[test] +fn date_proto_set_month() { + let mut context = Context::default(); + + let actual = forward_dt( + &mut context, + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setMonth(11); dt", + ); + assert_eq!( + Some(datetime_from_local(2020, 12, 8, 9, 16, 15, 779)), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setMonth(11, 16); dt", + ); + assert_eq!( + Some(datetime_from_local(2020, 12, 16, 9, 16, 15, 779)), + actual + ); + + // Out-of-bounds + // Thorough tests are done by setFullYear + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 7, 8, 9, 16, 15, 779); dt.setMonth(40, 83); dt", + ); + assert_eq!( + Some(datetime_from_local(2023, 7, 22, 9, 16, 15, 779)), + actual + ); +} + +#[test] +fn date_proto_set_seconds() { + let mut context = Context::default(); + + let actual = forward_dt( + &mut context, + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setSeconds(11); dt", + ); + assert_eq!( + Some(datetime_from_local(2020, 7, 8, 9, 16, 11, 779)), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setSeconds(11, 487); dt", + ); + assert_eq!( + Some(datetime_from_local(2020, 7, 8, 9, 16, 11, 487)), + actual + ); + + // Out-of-bounds + // Thorough tests are done by setHour + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 7, 8, 9, 16, 15, 779); dt.setSeconds(40000000, 40123); dt", + ); + assert_eq!( + Some(datetime_from_local(2021, 11, 14, 8, 23, 20, 123)), + actual + ); +} + +#[test] +fn set_year() { + let mut context = Context::default(); + + let actual = forward_dt( + &mut context, + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setYear(98); dt", + ); + assert_eq!( + Some(datetime_from_local(1998, 7, 8, 9, 16, 15, 779)), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setYear(2001); dt", + ); + assert_eq!( + Some(datetime_from_local(2001, 7, 8, 9, 16, 15, 779)), + actual + ); +} + +#[test] +fn date_proto_set_time() { + let mut context = Context::default(); + + let actual = forward_dt( + &mut context, + "let dt = new Date(); dt.setTime(new Date(2020, 6, 8, 9, 16, 15, 779).getTime()); dt", + ); + assert_eq!( + Some(datetime_from_local(2020, 7, 8, 9, 16, 15, 779)), + actual + ); +} + +#[test] +fn date_proto_set_utc_date() { + let mut context = Context::default(); + + let actual = forward_dt( + &mut context, + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCDate(21); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2020, 7, 21) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), + actual + ); + + // Date wraps to previous month for 0. + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCDate(0); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2020, 6, 30) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCDate(1/0); dt", + ); + assert_eq!(None, actual); +} + +#[test] +fn date_proto_set_utc_full_year() { + let mut context = Context::default(); + + let actual = forward_dt( + &mut context, + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCFullYear(2012); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2012, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCFullYear(2012, 8); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2012, 9, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCFullYear(2012, 8, 10); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2012, 9, 10) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), + actual + ); + + // Out-of-bounds + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 7, 8, 9, 16, 15, 779)); dt.setUTCFullYear(2012, 35); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2014, 12, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 7, 8, 9, 16, 15, 779)); dt.setUTCFullYear(2012, -35); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2009, 2, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 7, 8, 9, 16, 15, 779)); dt.setUTCFullYear(2012, 9, 950); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2015, 5, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 7, 8, 9, 16, 15, 779)); dt.setUTCFullYear(2012, 9, -950); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2010, 2, 23) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), + actual + ); +} + +#[test] +fn date_proto_set_utc_hours() { + let mut context = Context::default(); + + let actual = forward_dt( + &mut context, + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCHours(11); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(11, 16, 15, 779) + .unwrap() + ), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCHours(11, 35); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(11, 35, 15, 779) + .unwrap() + ), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCHours(11, 35, 23); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(11, 35, 23, 779) + .unwrap() + ), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCHours(11, 35, 23, 537); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(11, 35, 23, 537) + .unwrap() + ), + actual + ); + + // Out-of-bounds + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCHours(10000, 20000, 30000, 40123); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2021, 9, 11) + .unwrap() + .and_hms_milli_opt(21, 40, 40, 123) + .unwrap() + ), + actual + ); +} + +#[test] +fn date_proto_set_utc_milliseconds() { + let mut context = Context::default(); + + let actual = forward_dt( + &mut context, + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCMilliseconds(597); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 597) + .unwrap() + ), + actual + ); + + // Out-of-bounds + // Thorough tests are done by setHours + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCMilliseconds(40123); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 55, 123) + .unwrap() + ), + actual + ); +} + +#[test] +fn date_proto_set_utc_minutes() { + let mut context = Context::default(); + + let actual = forward_dt( + &mut context, + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCMinutes(11); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 11, 15, 779) + .unwrap() + ), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCMinutes(11, 35); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 11, 35, 779) + .unwrap() + ), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCMinutes(11, 35, 537); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 11, 35, 537) + .unwrap() + ), + actual + ); + + // Out-of-bounds + // Thorough tests are done by setHours + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCMinutes(600000, 30000, 40123); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2021, 8, 29) + .unwrap() + .and_hms_milli_opt(9, 20, 40, 123) + .unwrap() + ), + actual + ); +} + +#[test] +fn date_proto_set_utc_month() { + let mut context = Context::default(); + + let actual = forward_dt( + &mut context, + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCMonth(11); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2020, 12, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCMonth(11, 16); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2020, 12, 16) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), + actual + ); + + // Out-of-bounds + // Thorough tests are done by setFullYear + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 7, 8, 9, 16, 15, 779)); dt.setUTCMonth(40, 83); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2023, 7, 22) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), + actual + ); +} + +#[test] +fn date_proto_set_utc_seconds() { + let mut context = Context::default(); + + let actual = forward_dt( + &mut context, + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCSeconds(11); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 11, 779) + .unwrap() + ), + actual + ); + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCSeconds(11, 487); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 11, 487) + .unwrap() + ), + actual + ); + + // Out-of-bounds + // Thorough tests are done by setHour + + let actual = forward_dt( + &mut context, + "dt = new Date(Date.UTC(2020, 7, 8, 9, 16, 15, 779)); dt.setUTCSeconds(40000000, 40123); dt", + ); + assert_eq!( + Some( + NaiveDate::from_ymd_opt(2021, 11, 14) + .unwrap() + .and_hms_milli_opt(8, 23, 20, 123) + .unwrap() + ), + actual + ); +} + +#[test] +fn date_proto_to_date_string() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.toDateString()", + ) + .unwrap(); + assert_eq!(JsValue::new("Wed Jul 08 2020"), actual); +} + +#[test] +fn date_proto_to_gmt_string() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.toGMTString()", + ) + .unwrap(); + assert_eq!(JsValue::new("Wed, 08 Jul 2020 09:16:15 GMT"), actual); +} + +#[test] +fn date_proto_to_iso_string() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.toISOString()", + ) + .unwrap(); + assert_eq!(JsValue::new("2020-07-08T09:16:15.779Z"), actual); +} + +#[test] +fn date_proto_to_json() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.toJSON()", + ) + .unwrap(); + assert_eq!(JsValue::new("2020-07-08T09:16:15.779Z"), actual); +} + +#[test] +fn date_proto_to_string() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.toString()", + ) + .ok(); + + assert_eq!( + Some(JsValue::new( + Local + .from_local_datetime(&NaiveDateTime::new( + NaiveDate::from_ymd_opt(2020, 7, 8).unwrap(), + NaiveTime::from_hms_milli_opt(9, 16, 15, 779).unwrap() + )) + .earliest() + .unwrap() + .format("Wed Jul 08 2020 09:16:15 GMT%z") + .to_string() + )), + actual + ); +} + +#[test] +fn date_proto_to_time_string() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.toTimeString()", + ) + .ok(); + + assert_eq!( + Some(JsValue::new( + Local + .from_local_datetime(&NaiveDateTime::new( + NaiveDate::from_ymd_opt(2020, 7, 8).unwrap(), + NaiveTime::from_hms_milli_opt(9, 16, 15, 779).unwrap() + )) + .earliest() + .unwrap() + .format("09:16:15 GMT%z") + .to_string() + )), + actual + ); +} + +#[test] +fn date_proto_to_utc_string() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.toUTCString()", + ) + .unwrap(); + assert_eq!(JsValue::new("Wed, 08 Jul 2020 09:16:15 GMT"), actual); +} + +#[test] +fn date_proto_value_of() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).valueOf()", + ) + .unwrap(); + assert_eq!(JsValue::new(1_594_199_775_779_i64), actual); +} + +#[test] +fn date_neg() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "-new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779))", + ) + .unwrap(); + assert_eq!(JsValue::new(-1_594_199_775_779_i64), actual); +} + +#[test] +fn date_json() { + let mut context = Context::default(); + + let actual = forward_val( + &mut context, + "JSON.stringify({ date: new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)) })", + ) + .unwrap(); + assert_eq!( + JsValue::new(r#"{"date":"2020-07-08T09:16:15.779Z"}"#), + actual + ); +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/date/utils.rs b/javascript-engine/external/boa/boa_engine/src/builtins/date/utils.rs new file mode 100644 index 0000000..9cd142c --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/date/utils.rs @@ -0,0 +1,199 @@ +use chrono::{Datelike, Local, NaiveDateTime, TimeZone, Timelike}; + +use crate::value::IntegerOrNan; + +/// The absolute maximum value of a timestamp +pub(super) const MAX_TIMESTAMP: i64 = 864 * 10i64.pow(13); +/// The number of milliseconds in a second. +pub(super) const MILLIS_PER_SECOND: i64 = 1000; +/// The number of milliseconds in a minute. +pub(super) const MILLIS_PER_MINUTE: i64 = MILLIS_PER_SECOND * 60; +/// The number of milliseconds in an hour. +pub(super) const MILLIS_PER_HOUR: i64 = MILLIS_PER_MINUTE * 60; +/// The number of milliseconds in a day. +pub(super) const MILLIS_PER_DAY: i64 = MILLIS_PER_HOUR * 24; + +// https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-time-values-and-time-range +// +// The smaller range supported by a time value as specified in this section is approximately -273,790 to 273,790 +// years relative to 1970. +pub(super) const MIN_YEAR: i64 = -300_000; +pub(super) const MAX_YEAR: i64 = -MIN_YEAR; +pub(super) const MIN_MONTH: i64 = MIN_YEAR * 12; +pub(super) const MAX_MONTH: i64 = MAX_YEAR * 12; + +/// Calculates the absolute day number from the year number. +pub(super) const fn day_from_year(year: i64) -> i64 { + // Taken from https://chromium.googlesource.com/v8/v8/+/refs/heads/main/src/date/date.cc#496 + // Useful to avoid negative divisions and overflows on 32-bit platforms (if we plan to support them). + const YEAR_DELTA: i64 = 399_999; + const fn day(year: i64) -> i64 { + let year = year + YEAR_DELTA; + 365 * year + year / 4 - year / 100 + year / 400 + } + + assert!(MIN_YEAR <= year && year <= MAX_YEAR); + day(year) - day(1970) +} + +/// Abstract operation [`MakeTime`][spec]. +/// +/// [spec]: https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-maketime +pub(super) fn make_time(hour: i64, min: i64, sec: i64, ms: i64) -> Option { + // 1. If hour is not finite or min is not finite or sec is not finite or ms is not finite, return NaN. + // 2. Let h be 𝔽(! ToIntegerOrInfinity(hour)). + // 3. Let m be 𝔽(! ToIntegerOrInfinity(min)). + // 4. Let s be 𝔽(! ToIntegerOrInfinity(sec)). + // 5. Let milli be 𝔽(! ToIntegerOrInfinity(ms)). + + // 6. Let t be ((h * msPerHour + m * msPerMinute) + s * msPerSecond) + milli, performing the arithmetic according to IEEE 754-2019 rules (that is, as if using the ECMAScript operators * and +). + // 7. Return t. + + let h_ms = hour.checked_mul(MILLIS_PER_HOUR)?; + let m_ms = min.checked_mul(MILLIS_PER_MINUTE)?; + let s_ms = sec.checked_mul(MILLIS_PER_SECOND)?; + + h_ms.checked_add(m_ms)?.checked_add(s_ms)?.checked_add(ms) +} + +/// Abstract operation [`MakeDay`][spec]. +/// +/// [spec]: https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-makeday +pub(super) fn make_day(mut year: i64, mut month: i64, date: i64) -> Option { + // 1. If year is not finite or month is not finite or date is not finite, return NaN. + // 2. Let y be 𝔽(! ToIntegerOrInfinity(year)). + // 3. Let m be 𝔽(! ToIntegerOrInfinity(month)). + // 4. Let dt be 𝔽(! ToIntegerOrInfinity(date)). + if !(MIN_YEAR..=MAX_YEAR).contains(&year) || !(MIN_MONTH..=MAX_MONTH).contains(&month) { + return None; + } + + // At this point, we've already asserted that year and month are much less than its theoretical + // maximum and minimum values (i64::MAX/MIN), so we don't need to do checked operations. + + // 5. Let ym be y + 𝔽(floor(ℝ(m) / 12)). + // 6. If ym is not finite, return NaN. + year += month / 12; + // 7. Let mn be 𝔽(ℝ(m) modulo 12). + month %= 12; + if month < 0 { + month += 12; + year -= 1; + } + + // 8. Find a finite time value t such that YearFromTime(t) is ym and MonthFromTime(t) is mn and DateFromTime(t) is + // 1𝔽; but if this is not possible (because some argument is out of range), return NaN. + let month = usize::try_from(month).expect("month must be between 0 and 11 at this point"); + + let mut day = day_from_year(year); + + // Consider leap years when calculating the cumulative days added to the year from the input month + if (year % 4 != 0) || (year % 100 == 0 && year % 400 != 0) { + day += [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334][month]; + } else { + day += [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335][month]; + } + + // 9. Return Day(t) + dt - 1𝔽. + (day - 1).checked_add(date) +} + +/// Abstract operation [`MakeDate`][spec]. +/// +/// [spec]: https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-makedate +pub(super) fn make_date(day: i64, time: i64) -> Option { + // 1. If day is not finite or time is not finite, return NaN. + // 2. Let tv be day × msPerDay + time. + // 3. If tv is not finite, return NaN. + // 4. Return tv. + day.checked_mul(MILLIS_PER_DAY)?.checked_add(time) +} + +/// Abstract operation [`TimeClip`][spec] +/// Returns the timestamp (number of milliseconds) if it is in the expected range. +/// Otherwise, returns `None`. +/// +/// [spec]: https://tc39.es/ecma262/#sec-timeclip +pub(super) fn time_clip(time: i64) -> Option { + // 1. If time is not finite, return NaN. + // 2. If abs(ℝ(time)) > 8.64 × 10^15, return NaN. + // 3. Return 𝔽(! ToIntegerOrInfinity(time)). + (time.checked_abs()? <= MAX_TIMESTAMP).then_some(time) +} + +#[derive(Default, Debug, Clone, Copy)] +pub(super) struct DateParameters { + pub(super) year: Option, + pub(super) month: Option, + pub(super) date: Option, + pub(super) hour: Option, + pub(super) minute: Option, + pub(super) second: Option, + pub(super) millisecond: Option, +} + +/// Replaces some (or all) parameters of `date` with the specified parameters +pub(super) fn replace_params( + datetime: NaiveDateTime, + params: DateParameters, + local: bool, +) -> Option { + let DateParameters { + year, + month, + date, + hour, + minute, + second, + millisecond, + } = params; + + let datetime = if local { + Local.from_utc_datetime(&datetime).naive_local() + } else { + datetime + }; + + let year = match year { + Some(i) => i.as_integer()?, + None => i64::from(datetime.year()), + }; + let month = match month { + Some(i) => i.as_integer()?, + None => i64::from(datetime.month() - 1), + }; + let date = match date { + Some(i) => i.as_integer()?, + None => i64::from(datetime.day()), + }; + let hour = match hour { + Some(i) => i.as_integer()?, + None => i64::from(datetime.hour()), + }; + let minute = match minute { + Some(i) => i.as_integer()?, + None => i64::from(datetime.minute()), + }; + let second = match second { + Some(i) => i.as_integer()?, + None => i64::from(datetime.second()), + }; + let millisecond = match millisecond { + Some(i) => i.as_integer()?, + None => i64::from(datetime.timestamp_subsec_millis()), + }; + + let new_day = make_day(year, month, date)?; + let new_time = make_time(hour, minute, second, millisecond)?; + let mut ts = make_date(new_day, new_time)?; + + if local { + ts = Local + .from_local_datetime(&NaiveDateTime::from_timestamp_millis(ts)?) + .earliest()? + .naive_utc() + .timestamp_millis(); + } + + NaiveDateTime::from_timestamp_millis(time_clip(ts)?) +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/error/aggregate.rs b/javascript-engine/external/boa/boa_engine/src/builtins/error/aggregate.rs new file mode 100644 index 0000000..f3d1941 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/error/aggregate.rs @@ -0,0 +1,116 @@ +//! This module implements the global `AggregateError` object. +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-aggregate-error +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError + +use crate::{ + builtins::{iterable::iterable_to_list, Array, BuiltIn, JsArgs}, + context::intrinsics::StandardConstructors, + object::{ + internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData, + }, + property::{Attribute, PropertyDescriptorBuilder}, + Context, JsResult, JsValue, +}; +use boa_profiler::Profiler; +use tap::{Conv, Pipe}; + +use super::{Error, ErrorKind}; + +#[derive(Debug, Clone, Copy)] +pub(crate) struct AggregateError; + +impl BuiltIn for AggregateError { + const NAME: &'static str = "AggregateError"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let error_constructor = context.intrinsics().constructors().error().constructor(); + let error_prototype = context.intrinsics().constructors().error().prototype(); + + let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; + + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context + .intrinsics() + .constructors() + .aggregate_error() + .clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .inherit(error_prototype) + .custom_prototype(error_constructor) + .property("name", Self::NAME, attribute) + .property("message", "", attribute) + .build() + .conv::() + .pipe(Some) + } +} + +impl AggregateError { + /// The amount of arguments this function object takes. + pub(crate) const LENGTH: usize = 2; + + /// Create a new aggregate error object. + pub(crate) fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. + // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%AggregateError.prototype%", « [[ErrorData]] »). + let prototype = get_prototype_from_constructor( + new_target, + StandardConstructors::aggregate_error, + context, + )?; + let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Aggregate)); + + // 3. If message is not undefined, then + let message = args.get_or_undefined(1); + if !message.is_undefined() { + // a. Let msg be ? ToString(message). + let msg = message.to_string(context)?; + + // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). + o.create_non_enumerable_data_property_or_throw("message", msg, context); + } + + // 4. Perform ? InstallErrorCause(O, options). + Error::install_error_cause(&o, args.get_or_undefined(2), context)?; + + // 5. Let errorsList be ? IterableToList(errors). + let errors = args.get_or_undefined(0); + let errors_list = iterable_to_list(context, errors, None)?; + // 6. Perform ! DefinePropertyOrThrow(O, "errors", + // PropertyDescriptor { + // [[Configurable]]: true, + // [[Enumerable]]: false, + // [[Writable]]: true, + // [[Value]]: CreateArrayFromList(errorsList) + // }). + o.define_property_or_throw( + "errors", + PropertyDescriptorBuilder::new() + .configurable(true) + .enumerable(false) + .writable(true) + .value(Array::create_array_from_list(errors_list, context)) + .build(), + context, + ) + .expect("should not fail according to spec"); + + // 5. Return O. + Ok(o.into()) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/error/eval.rs b/javascript-engine/external/boa/boa_engine/src/builtins/error/eval.rs new file mode 100644 index 0000000..22b166f --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/error/eval.rs @@ -0,0 +1,91 @@ +//! This module implements the global `EvalError` object. +//! +//! Indicates an error regarding the global `eval()` function. +//! This exception is not thrown by JavaScript anymore, however +//! the `EvalError` object remains for compatibility. +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-evalerror +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/EvalError + +use crate::{ + builtins::{BuiltIn, JsArgs}, + context::intrinsics::StandardConstructors, + object::{ + internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData, + }, + property::Attribute, + Context, JsResult, JsValue, +}; +use boa_profiler::Profiler; +use tap::{Conv, Pipe}; + +use super::{Error, ErrorKind}; + +/// JavaScript `EvalError` implementation. +#[derive(Debug, Clone, Copy)] +pub(crate) struct EvalError; + +impl BuiltIn for EvalError { + const NAME: &'static str = "EvalError"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let error_constructor = context.intrinsics().constructors().error().constructor(); + let error_prototype = context.intrinsics().constructors().error().prototype(); + + let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context.intrinsics().constructors().eval_error().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .inherit(error_prototype) + .custom_prototype(error_constructor) + .property("name", Self::NAME, attribute) + .property("message", "", attribute) + .build() + .conv::() + .pipe(Some) + } +} + +impl EvalError { + /// The amount of arguments this function object takes. + pub(crate) const LENGTH: usize = 1; + + /// Create a new error object. + pub(crate) fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. + // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »). + let prototype = + get_prototype_from_constructor(new_target, StandardConstructors::eval_error, context)?; + let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Eval)); + + // 3. If message is not undefined, then + let message = args.get_or_undefined(0); + if !message.is_undefined() { + // a. Let msg be ? ToString(message). + let msg = message.to_string(context)?; + + // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). + o.create_non_enumerable_data_property_or_throw("message", msg, context); + } + + // 4. Perform ? InstallErrorCause(O, options). + Error::install_error_cause(&o, args.get_or_undefined(1), context)?; + + // 5. Return O. + Ok(o.into()) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/error/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/error/mod.rs new file mode 100644 index 0000000..a81a5fc --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/error/mod.rs @@ -0,0 +1,267 @@ +//! Boa's implementation of ECMAScript's global `Error` object. +//! +//! Error objects are thrown when runtime errors occur. +//! The Error object can also be used as a base object for user-defined exceptions. +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-error-objects +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error + +use crate::{ + builtins::BuiltIn, + context::intrinsics::StandardConstructors, + error::JsNativeError, + js_string, + object::{ + internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData, + }, + property::Attribute, + string::utf16, + Context, JsResult, JsValue, +}; +use boa_profiler::Profiler; +use tap::{Conv, Pipe}; + +pub(crate) mod aggregate; +pub(crate) mod eval; +pub(crate) mod range; +pub(crate) mod reference; +pub(crate) mod syntax; +pub(crate) mod r#type; +pub(crate) mod uri; + +#[cfg(test)] +mod tests; + +pub(crate) use self::aggregate::AggregateError; +pub(crate) use self::eval::EvalError; +pub(crate) use self::r#type::TypeError; +pub(crate) use self::range::RangeError; +pub(crate) use self::reference::ReferenceError; +pub(crate) use self::syntax::SyntaxError; +pub(crate) use self::uri::UriError; + +use super::JsArgs; + +/// The kind of a `NativeError` object, per the [ECMAScript spec][spec]. +/// +/// This is used internally to convert between [`JsObject`] and +/// [`JsNativeError`] correctly, but it can also be used to manually create `Error` +/// objects. However, the recommended way to create them is to construct a +/// `JsNativeError` first, then call [`JsNativeError::to_opaque`], +/// which will assign its prototype, properties and kind automatically. +/// +/// For a description of every error kind and its usage, see +/// [`JsNativeErrorKind`][crate::error::JsNativeErrorKind]. +/// +/// [spec]: https://tc39.es/ecma262/#sec-error-objects +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum ErrorKind { + /// The `AggregateError` object type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-aggregate-error-objects + Aggregate, + + /// The `Error` object type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-error-objects + Error, + + /// The `EvalError` type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-evalerror + Eval, + + /// The `TypeError` type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-typeerror + Type, + + /// The `RangeError` type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-rangeerror + Range, + + /// The `ReferenceError` type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-referenceerror + Reference, + + /// The `SyntaxError` type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-syntaxerror + Syntax, + + /// The `URIError` type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-urierror + Uri, +} + +/// Built-in `Error` object. +#[derive(Debug, Clone, Copy)] +pub(crate) struct Error; + +impl BuiltIn for Error { + const NAME: &'static str = "Error"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context.intrinsics().constructors().error().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .property("name", Self::NAME, attribute) + .property("message", "", attribute) + .method(Self::to_string, "toString", 0) + .build() + .conv::() + .pipe(Some) + } +} + +impl Error { + /// The amount of arguments this function object takes. + pub(crate) const LENGTH: usize = 1; + + pub(crate) fn install_error_cause( + o: &JsObject, + options: &JsValue, + context: &mut Context<'_>, + ) -> JsResult<()> { + // 1. If Type(options) is Object and ? HasProperty(options, "cause") is true, then + if let Some(options) = options.as_object() { + if options.has_property("cause", context)? { + // a. Let cause be ? Get(options, "cause"). + let cause = options.get("cause", context)?; + + // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "cause", cause). + o.create_non_enumerable_data_property_or_throw("cause", cause, context); + } + } + + // 2. Return unused. + Ok(()) + } + + /// `Error( message [ , options ] )` + /// + /// Create a new error object. + pub(crate) fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. + + // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%Error.prototype%", « [[ErrorData]] »). + let prototype = + get_prototype_from_constructor(new_target, StandardConstructors::error, context)?; + let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Error)); + + // 3. If message is not undefined, then + let message = args.get_or_undefined(0); + if !message.is_undefined() { + // a. Let msg be ? ToString(message). + let msg = message.to_string(context)?; + + // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). + o.create_non_enumerable_data_property_or_throw("message", msg, context); + } + + // 4. Perform ? InstallErrorCause(O, options). + Self::install_error_cause(&o, args.get_or_undefined(1), context)?; + + // 5. Return O. + Ok(o.into()) + } + + /// `Error.prototype.toString()` + /// + /// The toString() method returns a string representing the specified Error object. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString + #[allow(clippy::wrong_self_convention)] + pub(crate) fn to_string( + this: &JsValue, + _: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let O be the this value. + // 2. If Type(O) is not Object, throw a TypeError exception. + let o = this + .as_object() + .ok_or_else(|| JsNativeError::typ().with_message("'this' is not an Object"))?; + + // 3. Let name be ? Get(O, "name"). + let name = o.get(js_string!("name"), context)?; + + // 4. If name is undefined, set name to "Error"; otherwise set name to ? ToString(name). + let name = if name.is_undefined() { + js_string!("Error") + } else { + name.to_string(context)? + }; + + // 5. Let msg be ? Get(O, "message"). + let msg = o.get(js_string!("message"), context)?; + + // 6. If msg is undefined, set msg to the empty String; otherwise set msg to ? ToString(msg). + let msg = if msg.is_undefined() { + js_string!() + } else { + msg.to_string(context)? + }; + + // 7. If name is the empty String, return msg. + if name.is_empty() { + return Ok(msg.into()); + } + + // 8. If msg is the empty String, return name. + if msg.is_empty() { + return Ok(name.into()); + } + + // 9. Return the string-concatenation of name, the code unit 0x003A (COLON), + // the code unit 0x0020 (SPACE), and msg. + Ok(js_string!(&name, utf16!(": "), &msg).into()) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/error/range.rs b/javascript-engine/external/boa/boa_engine/src/builtins/error/range.rs new file mode 100644 index 0000000..d16db21 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/error/range.rs @@ -0,0 +1,89 @@ +//! This module implements the global `RangeError` object. +//! +//! Indicates a value that is not in the set or range of allowable values. +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-rangeerror +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RangeError + +use crate::{ + builtins::{BuiltIn, JsArgs}, + context::intrinsics::StandardConstructors, + object::{ + internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData, + }, + property::Attribute, + Context, JsResult, JsValue, +}; +use boa_profiler::Profiler; +use tap::{Conv, Pipe}; + +use super::{Error, ErrorKind}; + +/// JavaScript `RangeError` implementation. +#[derive(Debug, Clone, Copy)] +pub(crate) struct RangeError; + +impl BuiltIn for RangeError { + const NAME: &'static str = "RangeError"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let error_constructor = context.intrinsics().constructors().error().constructor(); + let error_prototype = context.intrinsics().constructors().error().prototype(); + + let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context.intrinsics().constructors().range_error().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .inherit(error_prototype) + .custom_prototype(error_constructor) + .property("name", Self::NAME, attribute) + .property("message", "", attribute) + .build() + .conv::() + .pipe(Some) + } +} + +impl RangeError { + /// The amount of arguments this function object takes. + pub(crate) const LENGTH: usize = 1; + + /// Create a new error object. + pub(crate) fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. + // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »). + let prototype = + get_prototype_from_constructor(new_target, StandardConstructors::range_error, context)?; + let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Range)); + + // 3. If message is not undefined, then + let message = args.get_or_undefined(0); + if !message.is_undefined() { + // a. Let msg be ? ToString(message). + let msg = message.to_string(context)?; + + // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). + o.create_non_enumerable_data_property_or_throw("message", msg, context); + } + + // 4. Perform ? InstallErrorCause(O, options). + Error::install_error_cause(&o, args.get_or_undefined(1), context)?; + + // 5. Return O. + Ok(o.into()) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/error/reference.rs b/javascript-engine/external/boa/boa_engine/src/builtins/error/reference.rs new file mode 100644 index 0000000..6b2911b --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/error/reference.rs @@ -0,0 +1,95 @@ +//! This module implements the global `ReferenceError` object. +//! +//! Indicates an error that occurs when de-referencing an invalid reference +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-referenceerror +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError + +use crate::{ + builtins::{BuiltIn, JsArgs}, + context::intrinsics::StandardConstructors, + object::{ + internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData, + }, + property::Attribute, + Context, JsResult, JsValue, +}; +use boa_profiler::Profiler; +use tap::{Conv, Pipe}; + +use super::{Error, ErrorKind}; + +#[derive(Debug, Clone, Copy)] +pub(crate) struct ReferenceError; + +impl BuiltIn for ReferenceError { + const NAME: &'static str = "ReferenceError"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let error_constructor = context.intrinsics().constructors().error().constructor(); + let error_prototype = context.intrinsics().constructors().error().prototype(); + + let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context + .intrinsics() + .constructors() + .reference_error() + .clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .inherit(error_prototype) + .custom_prototype(error_constructor) + .property("name", Self::NAME, attribute) + .property("message", "", attribute) + .build() + .conv::() + .pipe(Some) + } +} + +impl ReferenceError { + /// The amount of arguments this function object takes. + pub(crate) const LENGTH: usize = 1; + + /// Create a new error object. + pub(crate) fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. + // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »). + let prototype = get_prototype_from_constructor( + new_target, + StandardConstructors::reference_error, + context, + )?; + let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Reference)); + + // 3. If message is not undefined, then + let message = args.get_or_undefined(0); + if !message.is_undefined() { + // a. Let msg be ? ToString(message). + let msg = message.to_string(context)?; + + // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). + o.create_non_enumerable_data_property_or_throw("message", msg, context); + } + + // 4. Perform ? InstallErrorCause(O, options). + Error::install_error_cause(&o, args.get_or_undefined(1), context)?; + + // 5. Return O. + Ok(o.into()) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/error/syntax.rs b/javascript-engine/external/boa/boa_engine/src/builtins/error/syntax.rs new file mode 100644 index 0000000..7897ed1 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/error/syntax.rs @@ -0,0 +1,94 @@ +//! This module implements the global `SyntaxError` object. +//! +//! The `SyntaxError` object represents an error when trying to interpret syntactically invalid code. +//! It is thrown when the JavaScript context encounters tokens or token order that does not conform +//! to the syntax of the language when parsing code. +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-syntaxerror +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError + +use crate::{ + builtins::{BuiltIn, JsArgs}, + context::intrinsics::StandardConstructors, + object::{ + internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData, + }, + property::Attribute, + Context, JsResult, JsValue, +}; +use boa_profiler::Profiler; +use tap::{Conv, Pipe}; + +use super::{Error, ErrorKind}; + +/// JavaScript `SyntaxError` implementation. +#[derive(Debug, Clone, Copy)] +pub(crate) struct SyntaxError; + +impl BuiltIn for SyntaxError { + const NAME: &'static str = "SyntaxError"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let error_constructor = context.intrinsics().constructors().error().constructor(); + let error_prototype = context.intrinsics().constructors().error().prototype(); + + let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context.intrinsics().constructors().syntax_error().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .inherit(error_prototype) + .custom_prototype(error_constructor) + .property("name", Self::NAME, attribute) + .property("message", "", attribute) + .build() + .conv::() + .pipe(Some) + } +} + +impl SyntaxError { + /// The amount of arguments this function object takes. + pub(crate) const LENGTH: usize = 1; + + /// Create a new error object. + pub(crate) fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. + // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »). + let prototype = get_prototype_from_constructor( + new_target, + StandardConstructors::syntax_error, + context, + )?; + let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Syntax)); + + // 3. If message is not undefined, then + let message = args.get_or_undefined(0); + if !message.is_undefined() { + // a. Let msg be ? ToString(message). + let msg = message.to_string(context)?; + + // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). + o.create_non_enumerable_data_property_or_throw("message", msg, context); + } + + // 4. Perform ? InstallErrorCause(O, options). + Error::install_error_cause(&o, args.get_or_undefined(1), context)?; + + // 5. Return O. + Ok(o.into()) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/error/tests.rs b/javascript-engine/external/boa/boa_engine/src/builtins/error/tests.rs new file mode 100644 index 0000000..e082ba9 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/error/tests.rs @@ -0,0 +1,86 @@ +use crate::{forward, Context}; + +#[test] +fn error_to_string() { + let mut context = Context::default(); + let init = r#" + let e = new Error('1'); + let name = new Error(); + let message = new Error('message'); + message.name = ''; + let range_e = new RangeError('2'); + let ref_e = new ReferenceError('3'); + let syntax_e = new SyntaxError('4'); + let type_e = new TypeError('5'); + "#; + forward(&mut context, init); + assert_eq!(forward(&mut context, "e.toString()"), "\"Error: 1\""); + assert_eq!(forward(&mut context, "name.toString()"), "\"Error\""); + assert_eq!(forward(&mut context, "message.toString()"), "\"message\""); + assert_eq!( + forward(&mut context, "range_e.toString()"), + "\"RangeError: 2\"" + ); + assert_eq!( + forward(&mut context, "ref_e.toString()"), + "\"ReferenceError: 3\"" + ); + assert_eq!( + forward(&mut context, "syntax_e.toString()"), + "\"SyntaxError: 4\"" + ); + assert_eq!( + forward(&mut context, "type_e.toString()"), + "\"TypeError: 5\"" + ); +} + +#[test] +fn eval_error_name() { + let mut context = Context::default(); + assert_eq!(forward(&mut context, "EvalError.name"), "\"EvalError\""); +} + +#[test] +fn eval_error_length() { + let mut context = Context::default(); + assert_eq!(forward(&mut context, "EvalError.length"), "1"); +} + +#[test] +fn eval_error_to_string() { + let mut context = Context::default(); + assert_eq!( + forward(&mut context, "new EvalError('hello').toString()"), + "\"EvalError: hello\"" + ); + assert_eq!( + forward(&mut context, "new EvalError().toString()"), + "\"EvalError\"" + ); +} + +#[test] +fn uri_error_name() { + let mut context = Context::default(); + assert_eq!(forward(&mut context, "URIError.name"), "\"URIError\""); +} + +#[test] +fn uri_error_length() { + let mut context = Context::default(); + assert_eq!(forward(&mut context, "URIError.length"), "1"); +} + +#[test] +fn uri_error_to_string() { + let mut context = Context::default(); + assert_eq!( + forward(&mut context, "new URIError('hello').toString()"), + "\"URIError: hello\"" + ); + assert_eq!( + forward(&mut context, "new URIError().toString()"), + "\"URIError\"" + ); +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/error/type.rs b/javascript-engine/external/boa/boa_engine/src/builtins/error/type.rs new file mode 100644 index 0000000..cd14382 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/error/type.rs @@ -0,0 +1,120 @@ +//! This module implements the global `TypeError` object. +//! +//! The `TypeError` object represents an error when an operation could not be performed, +//! typically (but not exclusively) when a value is not of the expected type. +//! +//! A `TypeError` may be thrown when: +//! - an operand or argument passed to a function is incompatible with the type expected by that operator or function. +//! - when attempting to modify a value that cannot be changed. +//! - when attempting to use a value in an inappropriate way. +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-typeerror +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError + +use crate::{ + builtins::{function::Function, BuiltIn, JsArgs}, + context::intrinsics::StandardConstructors, + error::JsNativeError, + native_function::NativeFunction, + object::{ + internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData, + }, + property::{Attribute, PropertyDescriptor}, + Context, JsResult, JsValue, +}; +use boa_profiler::Profiler; +use tap::{Conv, Pipe}; + +use super::{Error, ErrorKind}; + +/// JavaScript `TypeError` implementation. +#[derive(Debug, Clone, Copy)] +pub(crate) struct TypeError; + +impl BuiltIn for TypeError { + const NAME: &'static str = "TypeError"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let error_constructor = context.intrinsics().constructors().error().constructor(); + let error_prototype = context.intrinsics().constructors().error().prototype(); + + let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context.intrinsics().constructors().type_error().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .inherit(error_prototype) + .custom_prototype(error_constructor) + .property("name", Self::NAME, attribute) + .property("message", "", attribute) + .build() + .conv::() + .pipe(Some) + } +} + +impl TypeError { + /// The amount of arguments this function object takes. + pub(crate) const LENGTH: usize = 1; + + /// Create a new error object. + pub(crate) fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. + // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »). + let prototype = + get_prototype_from_constructor(new_target, StandardConstructors::type_error, context)?; + let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Type)); + + // 3. If message is not undefined, then + let message = args.get_or_undefined(0); + if !message.is_undefined() { + // a. Let msg be ? ToString(message). + let msg = message.to_string(context)?; + + // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). + o.create_non_enumerable_data_property_or_throw("message", msg, context); + } + + // 4. Perform ? InstallErrorCause(O, options). + Error::install_error_cause(&o, args.get_or_undefined(1), context)?; + + // 5. Return O. + Ok(o.into()) + } +} + +pub(crate) fn create_throw_type_error(context: &mut Context<'_>) -> JsObject { + fn throw_type_error(_: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult { + Err(JsNativeError::typ().with_message("'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them").into()) + } + + let function = JsObject::from_proto_and_data( + context.intrinsics().constructors().function().prototype(), + ObjectData::function(Function::Native { + function: NativeFunction::from_fn_ptr(throw_type_error), + constructor: None, + }), + ); + + let property = PropertyDescriptor::builder() + .writable(false) + .enumerable(false) + .configurable(false); + function.insert_property("name", property.clone().value("ThrowTypeError")); + function.insert_property("length", property.value(0)); + + function +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/error/uri.rs b/javascript-engine/external/boa/boa_engine/src/builtins/error/uri.rs new file mode 100644 index 0000000..272d2d8 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/error/uri.rs @@ -0,0 +1,90 @@ +//! This module implements the global `URIError` object. +//! +//! The `URIError` object represents an error when a global URI handling +//! function was used in a wrong way. +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-urierror +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/URIError + +use crate::{ + builtins::{BuiltIn, JsArgs}, + context::intrinsics::StandardConstructors, + object::{ + internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData, + }, + property::Attribute, + Context, JsResult, JsValue, +}; +use boa_profiler::Profiler; +use tap::{Conv, Pipe}; + +use super::{Error, ErrorKind}; + +/// JavaScript `URIError` implementation. +#[derive(Debug, Clone, Copy)] +pub(crate) struct UriError; + +impl BuiltIn for UriError { + const NAME: &'static str = "URIError"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let error_constructor = context.intrinsics().constructors().error().constructor(); + let error_prototype = context.intrinsics().constructors().error().prototype(); + + let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context.intrinsics().constructors().uri_error().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .inherit(error_prototype) + .custom_prototype(error_constructor) + .property("name", Self::NAME, attribute) + .property("message", "", attribute) + .build() + .conv::() + .pipe(Some) + } +} + +impl UriError { + /// The amount of arguments this function object takes. + pub(crate) const LENGTH: usize = 1; + + /// Create a new error object. + pub(crate) fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. + // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »). + let prototype = + get_prototype_from_constructor(new_target, StandardConstructors::uri_error, context)?; + let o = JsObject::from_proto_and_data(prototype, ObjectData::error(ErrorKind::Uri)); + + // 3. If message is not undefined, then + let message = args.get_or_undefined(0); + if !message.is_undefined() { + // a. Let msg be ? ToString(message). + let msg = message.to_string(context)?; + + // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). + o.create_non_enumerable_data_property_or_throw("message", msg, context); + } + + // 4. Perform ? InstallErrorCause(O, options). + Error::install_error_cause(&o, args.get_or_undefined(1), context)?; + + // 5. Return O. + Ok(o.into()) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/eval/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/eval/mod.rs new file mode 100644 index 0000000..27c0c9e --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/eval/mod.rs @@ -0,0 +1,273 @@ +//! Boa's implementation of ECMAScript's global `eval` function. +//! +//! The `eval()` function evaluates ECMAScript code represented as a string. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! - [MDN documentation][mdn] +//! +//! [spec]: https://tc39.es/ecma262/#sec-eval-x +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval + +use crate::{ + builtins::{BuiltIn, JsArgs}, + environments::DeclarativeEnvironment, + error::JsNativeError, + native_function::NativeFunction, + object::FunctionObjectBuilder, + property::Attribute, + Context, JsResult, JsString, JsValue, +}; +use boa_ast::operations::{ + contains, contains_arguments, top_level_var_declared_names, ContainsSymbol, +}; +use boa_gc::Gc; +use boa_parser::Parser; +use boa_profiler::Profiler; + +#[derive(Debug, Clone, Copy)] +pub(crate) struct Eval; + +impl BuiltIn for Eval { + const NAME: &'static str = "eval"; + + const ATTRIBUTE: Attribute = Attribute::CONFIGURABLE + .union(Attribute::NON_ENUMERABLE) + .union(Attribute::WRITABLE); + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let object = FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(Self::eval)) + .name("eval") + .length(1) + .constructor(false) + .build(); + + Some(object.into()) + } +} + +impl Eval { + /// `19.2.1 eval ( x )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-eval-x + fn eval(_: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResult { + // 1. Return ? PerformEval(x, false, false). + Self::perform_eval(args.get_or_undefined(0), false, false, context) + } + + /// `19.2.1.1 PerformEval ( x, strictCaller, direct )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-performeval + pub(crate) fn perform_eval( + x: &JsValue, + direct: bool, + mut strict: bool, + context: &mut Context<'_>, + ) -> JsResult { + bitflags::bitflags! { + /// Flags used to throw early errors on invalid `eval` calls. + #[derive(Default)] + struct Flags: u8 { + const IN_FUNCTION = 0b0001; + const IN_METHOD = 0b0010; + const IN_DERIVED_CONSTRUCTOR = 0b0100; + const IN_CLASS_FIELD_INITIALIZER = 0b1000; + } + } + + /// Possible actions that can be executed after exiting this function to restore the environment to its + /// original state. + #[derive(Debug)] + enum EnvStackAction { + Truncate(usize), + Restore(Vec>), + } + + /// Restores the environment after calling `eval` or after throwing an error. + fn restore_environment(context: &mut Context<'_>, action: EnvStackAction) { + match action { + EnvStackAction::Truncate(size) => { + context.realm.environments.truncate(size); + } + EnvStackAction::Restore(envs) => { + // Pop all environments created during the eval execution and restore the original stack. + context.realm.environments.truncate(1); + context.realm.environments.extend(envs); + } + } + } + + // 1. Assert: If direct is false, then strictCaller is also false. + debug_assert!(direct || !strict); + + // 2. If Type(x) is not String, return x. + // TODO: rework parser to take an iterator of `u32` unicode codepoints + let Some(x) = x.as_string().map(JsString::to_std_string_escaped) else { + return Ok(x.clone()); + }; + + // Because of implementation details the following code differs from the spec. + + // 5. Perform ? HostEnsureCanCompileStrings(evalRealm). + context.host_hooks().ensure_can_compile_strings(context)?; + + // 11. Perform the following substeps in an implementation-defined order, possibly interleaving parsing and error detection: + // a. Let script be ParseText(StringToCodePoints(x), Script). + // b. If script is a List of errors, throw a SyntaxError exception. + // c. If script Contains ScriptBody is false, return undefined. + // d. Let body be the ScriptBody of script. + let mut parser = Parser::new(x.as_bytes()); + if strict { + parser.set_strict(); + } + let body = parser.parse_eval(direct, context.interner_mut())?; + + // 6. Let inFunction be false. + // 7. Let inMethod be false. + // 8. Let inDerivedConstructor be false. + // 9. Let inClassFieldInitializer be false. + // a. Let thisEnvRec be GetThisEnvironment(). + let flags = match context + .realm + .environments + .get_this_environment() + .as_function_slots() + { + // 10. If direct is true, then + // b. If thisEnvRec is a Function Environment Record, then + Some(function_env) if direct => { + let function_env = function_env.borrow(); + // i. Let F be thisEnvRec.[[FunctionObject]]. + let function_object = function_env.function_object().borrow(); + + // ii. Set inFunction to true. + let mut flags = Flags::IN_FUNCTION; + + // iii. Set inMethod to thisEnvRec.HasSuperBinding(). + if function_env.has_super_binding() { + flags |= Flags::IN_METHOD; + } + + let function_object = function_object + .as_function() + .expect("must be function object"); + + // iv. If F.[[ConstructorKind]] is derived, set inDerivedConstructor to true. + if function_object.is_derived_constructor() { + flags |= Flags::IN_DERIVED_CONSTRUCTOR; + } + + // v. Let classFieldInitializerName be F.[[ClassFieldInitializerName]]. + // vi. If classFieldInitializerName is not empty, set inClassFieldInitializer to true. + if function_object.class_field_initializer_name().is_some() { + flags |= Flags::IN_CLASS_FIELD_INITIALIZER; + } + + flags + } + _ => Flags::default(), + }; + + if !flags.contains(Flags::IN_FUNCTION) && contains(&body, ContainsSymbol::NewTarget) { + return Err(JsNativeError::syntax() + .with_message("invalid `new.target` expression inside eval") + .into()); + } + if !flags.contains(Flags::IN_METHOD) && contains(&body, ContainsSymbol::SuperProperty) { + return Err(JsNativeError::syntax() + .with_message("invalid `super` reference inside eval") + .into()); + } + if !flags.contains(Flags::IN_DERIVED_CONSTRUCTOR) + && contains(&body, ContainsSymbol::SuperCall) + { + return Err(JsNativeError::syntax() + .with_message("invalid `super` call inside eval") + .into()); + } + if flags.contains(Flags::IN_CLASS_FIELD_INITIALIZER) && contains_arguments(&body) { + return Err(JsNativeError::syntax() + .with_message("invalid `arguments` reference inside eval") + .into()); + } + + strict |= body.strict(); + + // Because our environment model does not map directly to the spec, this section looks very different. + // 12 - 13 are implicit in the call of `Context::compile_with_new_declarative`. + // 14 - 33 are in the following section, together with EvalDeclarationInstantiation. + let action = if direct { + // If the call to eval is direct, the code is executed in the current environment. + + // Poison the current environment, because it may contain new declarations after/during eval. + if !strict { + context.realm.environments.poison_current(); + } + + // Set the compile time environment to the current running environment and save the number of current environments. + context.realm.compile_env = context.realm.environments.current_compile_environment(); + let environments_len = context.realm.environments.len(); + + // Pop any added runtime environments that were not removed during the eval execution. + EnvStackAction::Truncate(environments_len) + } else { + // If the call to eval is indirect, the code is executed in the global environment. + + // Poison all environments, because the global environment may contain new declarations after/during eval. + if !strict { + context.realm.environments.poison_all(); + } + + // Pop all environments before the eval execution. + let environments = context.realm.environments.pop_to_global(); + context.realm.compile_env = context.realm.environments.current_compile_environment(); + + // Restore all environments to the state from before the eval execution. + EnvStackAction::Restore(environments) + }; + + // Only need to check on non-strict mode since strict mode automatically creates a function + // environment for all eval calls. + if !strict { + // Error if any var declaration in the eval code already exists as a let/const declaration in the current running environment. + if let Some(name) = context + .realm + .environments + .has_lex_binding_until_function_environment(&top_level_var_declared_names(&body)) + { + restore_environment(context, action); + let name = context.interner().resolve_expect(name.sym()); + let msg = format!("variable declaration {name} in eval function already exists as a lexical variable"); + return Err(JsNativeError::syntax().with_message(msg).into()); + } + } + + // TODO: check if private identifiers inside `eval` are valid. + + // Compile and execute the eval statement list. + let code_block = context.compile_with_new_declarative(&body, strict)?; + // Indirect calls don't need extensions, because a non-strict indirect call modifies only + // the global object. + // Strict direct calls also don't need extensions, since all strict eval calls push a new + // function environment before evaluating. + if direct && !strict { + context + .realm + .environments + .extend_outer_function_environment(); + } + let result = context.execute(code_block); + + restore_environment(context, action); + + result + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/function/arguments.rs b/javascript-engine/external/boa/boa_engine/src/builtins/function/arguments.rs new file mode 100644 index 0000000..dae9b0d --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/function/arguments.rs @@ -0,0 +1,293 @@ +use crate::{ + environments::DeclarativeEnvironment, + object::{JsObject, ObjectData}, + property::PropertyDescriptor, + symbol::{self, JsSymbol}, + Context, JsValue, +}; +use boa_ast::{function::FormalParameterList, operations::bound_names}; +use boa_gc::{Finalize, Gc, Trace}; +use rustc_hash::FxHashMap; + +/// `ParameterMap` represents the `[[ParameterMap]]` internal slot on a Arguments exotic object. +/// +/// This struct stores all the data to access mapped function parameters in their environment. +#[derive(Debug, Clone, Trace, Finalize)] +pub struct ParameterMap { + binding_indices: Vec>, + environment: Gc, +} + +impl ParameterMap { + /// Deletes the binding with the given index from the parameter map. + pub(crate) fn delete(&mut self, index: usize) { + if let Some(binding) = self.binding_indices.get_mut(index) { + *binding = None; + } + } + + /// Get the value of the binding at the given index from the function environment. + /// + /// Note: This function is the abstract getter closure described in 10.4.4.7.1 `MakeArgGetter ( name, env )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-makearggetter + pub(crate) fn get(&self, index: usize) -> Option { + if let Some(Some(binding_index)) = self.binding_indices.get(index) { + return Some(self.environment.get(*binding_index)); + } + None + } + + /// Set the value of the binding at the given index in the function environment. + /// + /// Note: This function is the abstract setter closure described in 10.4.4.7.2 `MakeArgSetter ( name, env )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-makeargsetter + pub(crate) fn set(&self, index: usize, value: &JsValue) { + if let Some(Some(binding_index)) = self.binding_indices.get(index) { + self.environment.set(*binding_index, value.clone()); + } + } +} + +#[derive(Debug, Clone, Trace, Finalize)] +pub enum Arguments { + Unmapped, + Mapped(ParameterMap), +} + +impl Arguments { + /// Creates a new unmapped Arguments ordinary object. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-createunmappedargumentsobject + pub(crate) fn create_unmapped_arguments_object( + arguments_list: &[JsValue], + context: &mut Context<'_>, + ) -> JsObject { + // 1. Let len be the number of elements in argumentsList. + let len = arguments_list.len(); + + // 2. Let obj be ! OrdinaryObjectCreate(%Object.prototype%, « [[ParameterMap]] »). + // 3. Set obj.[[ParameterMap]] to undefined. + // skipped because the `Arguments` enum ensures ordinary argument objects don't have a `[[ParameterMap]]` + let obj = JsObject::from_proto_and_data( + context.intrinsics().constructors().object().prototype(), + ObjectData::arguments(Self::Unmapped), + ); + + // 4. Perform DefinePropertyOrThrow(obj, "length", PropertyDescriptor { [[Value]]: 𝔽(len), + // [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }). + obj.define_property_or_throw( + "length", + PropertyDescriptor::builder() + .value(len) + .writable(true) + .enumerable(false) + .configurable(true), + context, + ) + .expect("Defining new own properties for a new ordinary object cannot fail"); + + // 5. Let index be 0. + // 6. Repeat, while index < len, + for (index, value) in arguments_list.iter().cloned().enumerate() { + // a. Let val be argumentsList[index]. + // b. Perform ! CreateDataPropertyOrThrow(obj, ! ToString(𝔽(index)), val). + obj.create_data_property_or_throw(index, value, context) + .expect("Defining new own properties for a new ordinary object cannot fail"); + + // c. Set index to index + 1. + } + + // 7. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor { + // [[Value]]: %Array.prototype.values%, [[Writable]]: true, [[Enumerable]]: false, + // [[Configurable]]: true }). + let values_function = context.intrinsics().objects().array_prototype_values(); + obj.define_property_or_throw( + symbol::JsSymbol::iterator(), + PropertyDescriptor::builder() + .value(values_function) + .writable(true) + .enumerable(false) + .configurable(true), + context, + ) + .expect("Defining new own properties for a new ordinary object cannot fail"); + + let throw_type_error = context.intrinsics().objects().throw_type_error(); + + // 8. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor { + // [[Get]]: %ThrowTypeError%, [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, + // [[Configurable]]: false }). + obj.define_property_or_throw( + "callee", + PropertyDescriptor::builder() + .get(throw_type_error.clone()) + .set(throw_type_error) + .enumerable(false) + .configurable(false), + context, + ) + .expect("Defining new own properties for a new ordinary object cannot fail"); + + // 9. Return obj. + obj + } + + /// Creates a new mapped Arguments exotic object. + /// + /// + pub(crate) fn create_mapped_arguments_object( + func: &JsObject, + formals: &FormalParameterList, + arguments_list: &[JsValue], + env: &Gc, + context: &mut Context<'_>, + ) -> JsObject { + // 1. Assert: formals does not contain a rest parameter, any binding patterns, or any initializers. + // It may contain duplicate identifiers. + // 2. Let len be the number of elements in argumentsList. + let len = arguments_list.len(); + + // 3. Let obj be ! MakeBasicObject(« [[Prototype]], [[Extensible]], [[ParameterMap]] »). + // 4. Set obj.[[GetOwnProperty]] as specified in 10.4.4.1. + // 5. Set obj.[[DefineOwnProperty]] as specified in 10.4.4.2. + // 6. Set obj.[[Get]] as specified in 10.4.4.3. + // 7. Set obj.[[Set]] as specified in 10.4.4.4. + // 8. Set obj.[[Delete]] as specified in 10.4.4.5. + // 9. Set obj.[[Prototype]] to %Object.prototype%. + + // Section 17-19 are done first, for easier object creation in 11. + // + // The section 17-19 differs from the spec, due to the way the runtime environments work. + // + // This section creates getters and setters for all mapped arguments. + // Getting and setting values on the `arguments` object will actually access the bindings in the environment: + // ``` + // function f(a) {console.log(a); arguments[0] = 1; console.log(a)}; + // f(0) // 0, 1 + // ``` + // + // The spec assumes, that identifiers are used at runtime to reference bindings in the environment. + // We use indices to access environment bindings at runtime. + // To map to function parameters to binding indices, we use the fact, that bindings in a + // function environment start with all of the arguments in order: + // `function f (a,b,c)` + // | binding index | `arguments` property key | identifier | + // | 0 | 0 | a | + // | 1 | 1 | b | + // | 2 | 2 | c | + // + // Notice that the binding index does not correspond to the argument index: + // `function f (a,a,b)` => binding indices 0 (a), 1 (b), 2 (c) + // | binding index | `arguments` property key | identifier | + // | - | 0 | - | + // | 0 | 1 | a | + // | 1 | 2 | b | + // While the `arguments` object contains all arguments, they must not be all bound. + // In the case of duplicate parameter names, the last one is bound as the environment binding. + // + // The following logic implements the steps 17-19 adjusted for our environment structure. + + let mut bindings = FxHashMap::default(); + let mut property_index = 0; + for name in bound_names(formals) { + if property_index >= len { + break; + } + let binding_index = bindings.len() + 1; + let entry = bindings + .entry(name) + .or_insert((binding_index, property_index)); + entry.1 = property_index; + property_index += 1; + } + + let mut map = ParameterMap { + binding_indices: vec![None; property_index], + environment: env.clone(), + }; + + for (binding_index, property_index) in bindings.values() { + map.binding_indices[*property_index] = Some(*binding_index); + } + + // 11. Set obj.[[ParameterMap]] to map. + let obj = JsObject::from_proto_and_data( + context.intrinsics().constructors().object().prototype(), + ObjectData::arguments(Self::Mapped(map)), + ); + + // 14. Let index be 0. + // 15. Repeat, while index < len, + for (index, val) in arguments_list.iter().cloned().enumerate() { + // a. Let val be argumentsList[index]. + // b. Perform ! CreateDataPropertyOrThrow(obj, ! ToString(𝔽(index)), val). + // Note: Insert is used here because `CreateDataPropertyOrThrow` would cause a panic while executing + // exotic argument object set methods before the variables in the environment are initialized. + obj.insert( + index, + PropertyDescriptor::builder() + .value(val) + .writable(true) + .enumerable(true) + .configurable(true) + .build(), + ); + // c. Set index to index + 1. + } + + // 16. Perform ! DefinePropertyOrThrow(obj, "length", PropertyDescriptor { [[Value]]: 𝔽(len), + // [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }). + obj.define_property_or_throw( + "length", + PropertyDescriptor::builder() + .value(len) + .writable(true) + .enumerable(false) + .configurable(true), + context, + ) + .expect("Defining new own properties for a new ordinary object cannot fail"); + + // 20. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor { + // [[Value]]: %Array.prototype.values%, [[Writable]]: true, [[Enumerable]]: false, + // [[Configurable]]: true }). + let values_function = context.intrinsics().objects().array_prototype_values(); + obj.define_property_or_throw( + JsSymbol::iterator(), + PropertyDescriptor::builder() + .value(values_function) + .writable(true) + .enumerable(false) + .configurable(true), + context, + ) + .expect("Defining new own properties for a new ordinary object cannot fail"); + + // 21. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor { + // [[Value]]: func, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }). + obj.define_property_or_throw( + "callee", + PropertyDescriptor::builder() + .value(func.clone()) + .writable(true) + .enumerable(false) + .configurable(true), + context, + ) + .expect("Defining new own properties for a new ordinary object cannot fail"); + + // 22. Return obj. + obj + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/function/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/function/mod.rs new file mode 100644 index 0000000..cdb035d --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/function/mod.rs @@ -0,0 +1,1048 @@ +//! Boa's implementation of ECMAScript's global `Function` object and Native Functions. +//! +//! Objects wrap `Function`s and expose them via call/construct slots. +//! +//! The `Function` object is used for matching text with a pattern. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! - [MDN documentation][mdn] +//! +//! [spec]: https://tc39.es/ecma262/#sec-function-objects +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function + +use crate::{ + builtins::{BuiltIn, JsArgs}, + bytecompiler::FunctionCompiler, + context::intrinsics::StandardConstructors, + environments::DeclarativeEnvironmentStack, + error::JsNativeError, + js_string, + native_function::{NativeFunction, NativeFunctionPointer}, + object::{internal_methods::get_prototype_from_constructor, JsObject, Object, ObjectData}, + object::{ConstructorBuilder, FunctionObjectBuilder, JsFunction, PrivateElement}, + property::{Attribute, PropertyDescriptor, PropertyKey}, + string::utf16, + symbol::JsSymbol, + value::IntegerOrInfinity, + Context, JsResult, JsString, JsValue, +}; +use boa_ast::{ + function::{FormalParameterList, PrivateName}, + operations::{bound_names, contains, lexically_declared_names, ContainsSymbol}, + StatementList, +}; +use boa_gc::{self, custom_trace, Finalize, Gc, Trace}; +use boa_interner::Sym; +use boa_parser::Parser; +use boa_profiler::Profiler; +use tap::{Conv, Pipe}; + +use std::fmt; + +use super::promise::PromiseCapability; + +pub(crate) mod arguments; +#[cfg(test)] +mod tests; + +/// Represents the `[[ThisMode]]` internal slot of function objects. +/// +/// More information: +/// - [ECMAScript specification][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-ecmascript-function-objects +#[derive(Debug, Trace, Finalize, PartialEq, Eq, Clone)] +pub enum ThisMode { + /// The `this` value refers to the `this` value of a lexically enclosing function. + Lexical, + + /// The `this` value is used exactly as provided by an invocation of the function. + Strict, + + /// The `this` value of `undefined` or `null` is interpreted as a reference to the global object, + /// and any other `this` value is first passed to `ToObject`. + Global, +} + +impl ThisMode { + /// Returns `true` if the this mode is `Lexical`. + #[must_use] + pub const fn is_lexical(&self) -> bool { + matches!(self, Self::Lexical) + } + + /// Returns `true` if the this mode is `Strict`. + #[must_use] + pub const fn is_strict(&self) -> bool { + matches!(self, Self::Strict) + } + + /// Returns `true` if the this mode is `Global`. + #[must_use] + pub const fn is_global(&self) -> bool { + matches!(self, Self::Global) + } +} + +/// Represents the `[[ConstructorKind]]` internal slot of function objects. +/// +/// More information: +/// - [ECMAScript specification][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-ecmascript-function-objects +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ConstructorKind { + /// The class constructor is not derived. + Base, + + /// The class constructor is a derived class constructor. + Derived, +} + +impl ConstructorKind { + /// Returns `true` if the constructor kind is `Base`. + #[must_use] + pub const fn is_base(&self) -> bool { + matches!(self, Self::Base) + } + + /// Returns `true` if the constructor kind is `Derived`. + #[must_use] + pub const fn is_derived(&self) -> bool { + matches!(self, Self::Derived) + } +} + +/// Record containing the field definition of classes. +/// +/// More information: +/// - [ECMAScript specification][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-classfielddefinition-record-specification-type +#[derive(Clone, Debug, Finalize)] +pub enum ClassFieldDefinition { + /// A class field definition with a `string` or `symbol` as a name. + Public(PropertyKey, JsFunction), + + /// A class field definition with a private name. + Private(PrivateName, JsFunction), +} + +unsafe impl Trace for ClassFieldDefinition { + custom_trace! {this, { + match this { + Self::Public(_key, func) => { + mark(func); + } + Self::Private(_, func) => { + mark(func); + } + } + }} +} + +/// Boa representation of a Function Object. +/// +/// `FunctionBody` is specific to this interpreter, it will either be Rust code or JavaScript code +/// (AST Node). +/// +/// +#[derive(Finalize)] +pub enum Function { + /// A rust function. + Native { + /// The rust function. + function: NativeFunction, + + /// The kind of the function constructor if it is a constructor. + constructor: Option, + }, + /// A bytecode function. + Ordinary { + /// The code block containing the compiled function. + code: Gc, + + /// The `[[Environment]]` internal slot. + environments: DeclarativeEnvironmentStack, + + /// The `[[ConstructorKind]]` internal slot. + constructor_kind: ConstructorKind, + + /// The `[[HomeObject]]` internal slot. + home_object: Option, + + /// The `[[Fields]]` internal slot. + fields: Vec, + + /// The `[[PrivateMethods]]` internal slot. + private_methods: Vec<(PrivateName, PrivateElement)>, + + /// The class object that this function is associated with. + class_object: Option, + }, + + /// A bytecode async function. + Async { + /// The code block containing the compiled function. + code: Gc, + + /// The `[[Environment]]` internal slot. + environments: DeclarativeEnvironmentStack, + + /// The `[[HomeObject]]` internal slot. + home_object: Option, + + /// The promise capability record of the async function. + promise_capability: PromiseCapability, + + /// The class object that this function is associated with. + class_object: Option, + }, + + /// A bytecode generator function. + Generator { + /// The code block containing the compiled function. + code: Gc, + + /// The `[[Environment]]` internal slot. + environments: DeclarativeEnvironmentStack, + + /// The `[[HomeObject]]` internal slot. + home_object: Option, + + /// The class object that this function is associated with. + class_object: Option, + }, + + /// A bytecode async generator function. + AsyncGenerator { + /// The code block containing the compiled function. + code: Gc, + + /// The `[[Environment]]` internal slot. + environments: DeclarativeEnvironmentStack, + + /// The `[[HomeObject]]` internal slot. + home_object: Option, + + /// The class object that this function is associated with. + class_object: Option, + }, +} + +unsafe impl Trace for Function { + custom_trace! {this, { + match this { + Self::Native { function, .. } => {mark(function)} + Self::Ordinary { code, environments, home_object, fields, private_methods, class_object, .. } => { + mark(code); + mark(environments); + mark(home_object); + mark(fields); + for (_, elem) in private_methods { + mark(elem); + } + mark(class_object); + } + Self::Async { code, environments, home_object, promise_capability, class_object } => { + mark(code); + mark(environments); + mark(home_object); + mark(promise_capability); + mark(class_object); + } + Self::Generator { code, environments, home_object, class_object} + | Self::AsyncGenerator { code, environments, home_object, class_object} => { + mark(code); + mark(environments); + mark(home_object); + mark(class_object); + } + } + }} +} + +impl fmt::Debug for Function { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Function {{ ... }}") + } +} + +impl Function { + /// Returns true if the function object is a constructor. + pub fn is_constructor(&self) -> bool { + match self { + Self::Native { constructor, .. } => constructor.is_some(), + Self::Generator { .. } | Self::AsyncGenerator { .. } | Self::Async { .. } => false, + Self::Ordinary { code, .. } => !(code.this_mode == ThisMode::Lexical), + } + } + + /// Returns true if the function object is a derived constructor. + pub(crate) const fn is_derived_constructor(&self) -> bool { + if let Self::Ordinary { + constructor_kind, .. + } = self + { + constructor_kind.is_derived() + } else { + false + } + } + + /// Returns the `[[ClassFieldInitializerName]]` internal slot of the function. + pub(crate) fn class_field_initializer_name(&self) -> Option { + if let Self::Ordinary { code, .. } = self { + code.class_field_initializer_name + } else { + None + } + } + + /// Returns a reference to the function `[[HomeObject]]` slot if present. + pub(crate) const fn get_home_object(&self) -> Option<&JsObject> { + match self { + Self::Ordinary { home_object, .. } + | Self::Async { home_object, .. } + | Self::Generator { home_object, .. } + | Self::AsyncGenerator { home_object, .. } => home_object.as_ref(), + Self::Native { .. } => None, + } + } + + /// Sets the `[[HomeObject]]` slot if present. + pub(crate) fn set_home_object(&mut self, object: JsObject) { + match self { + Self::Ordinary { home_object, .. } + | Self::Async { home_object, .. } + | Self::Generator { home_object, .. } + | Self::AsyncGenerator { home_object, .. } => *home_object = Some(object), + Self::Native { .. } => {} + } + } + + /// Returns the values of the `[[Fields]]` internal slot. + pub(crate) fn get_fields(&self) -> &[ClassFieldDefinition] { + if let Self::Ordinary { fields, .. } = self { + fields + } else { + &[] + } + } + + /// Pushes a value to the `[[Fields]]` internal slot if present. + pub(crate) fn push_field(&mut self, key: PropertyKey, value: JsFunction) { + if let Self::Ordinary { fields, .. } = self { + fields.push(ClassFieldDefinition::Public(key, value)); + } + } + + /// Pushes a private value to the `[[Fields]]` internal slot if present. + pub(crate) fn push_field_private(&mut self, key: PrivateName, value: JsFunction) { + if let Self::Ordinary { fields, .. } = self { + fields.push(ClassFieldDefinition::Private(key, value)); + } + } + + /// Returns the values of the `[[PrivateMethods]]` internal slot. + pub(crate) fn get_private_methods(&self) -> &[(PrivateName, PrivateElement)] { + if let Self::Ordinary { + private_methods, .. + } = self + { + private_methods + } else { + &[] + } + } + + /// Pushes a private method to the `[[PrivateMethods]]` internal slot if present. + pub(crate) fn push_private_method(&mut self, name: PrivateName, method: PrivateElement) { + if let Self::Ordinary { + private_methods, .. + } = self + { + private_methods.push((name, method)); + } + } + + /// Returns the promise capability if the function is an async function. + pub(crate) const fn get_promise_capability(&self) -> Option<&PromiseCapability> { + if let Self::Async { + promise_capability, .. + } = self + { + Some(promise_capability) + } else { + None + } + } + + /// Sets the class object. + pub(crate) fn set_class_object(&mut self, object: JsObject) { + match self { + Self::Ordinary { class_object, .. } + | Self::Async { class_object, .. } + | Self::Generator { class_object, .. } + | Self::AsyncGenerator { class_object, .. } => *class_object = Some(object), + Self::Native { .. } => {} + } + } +} + +/// Creates a new member function of a `Object` or `prototype`. +/// +/// A function registered using this macro can then be called from Javascript using: +/// +/// parent.name() +/// +/// See the javascript 'Number.toString()' as an example. +/// +/// # Arguments +/// function: The function to register as a built in function. +/// name: The name of the function (how it will be called but without the ()). +/// parent: The object to register the function on, if the global object is used then the function is instead called as name() +/// without requiring the parent, see parseInt() as an example. +/// length: As described at , The value of the "length" property is an integer that +/// indicates the typical number of arguments expected by the function. However, the language permits the function to be invoked with +/// some other number of arguments. +/// +/// If no length is provided, the length will be set to 0. +// TODO: deprecate/remove this. +pub(crate) fn make_builtin_fn( + function: NativeFunctionPointer, + name: N, + parent: &JsObject, + length: usize, + interpreter: &Context<'_>, +) where + N: Into, +{ + let name = name.into(); + let _timer = Profiler::global().start_event(&format!("make_builtin_fn: {name}"), "init"); + + let function = JsObject::from_proto_and_data( + interpreter + .intrinsics() + .constructors() + .function() + .prototype(), + ObjectData::function(Function::Native { + function: NativeFunction::from_fn_ptr(function), + constructor: None, + }), + ); + let attribute = PropertyDescriptor::builder() + .writable(false) + .enumerable(false) + .configurable(true); + function.insert_property("length", attribute.clone().value(length)); + function.insert_property("name", attribute.value(name.as_str())); + + parent.clone().insert_property( + name, + PropertyDescriptor::builder() + .value(function) + .writable(true) + .enumerable(false) + .configurable(true), + ); +} + +/// The internal representation of a `Function` object. +#[derive(Debug, Clone, Copy)] +pub struct BuiltInFunctionObject; + +impl BuiltInFunctionObject { + const LENGTH: usize = 1; + + /// `Function ( p1, p2, … , pn, body )` + /// + /// The apply() method invokes self with the first argument as the `this` value + /// and the rest of the arguments provided as an array (or an array-like object). + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-function-p1-p2-pn-body + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/Function + fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + Self::create_dynamic_function(new_target, args, false, false, context).map(Into::into) + } + + /// `CreateDynamicFunction ( constructor, newTarget, kind, args )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-createdynamicfunction + pub(crate) fn create_dynamic_function( + new_target: &JsValue, + args: &[JsValue], + r#async: bool, + generator: bool, + context: &mut Context<'_>, + ) -> JsResult { + let default = if r#async && generator { + StandardConstructors::async_generator_function + } else if r#async { + StandardConstructors::async_function + } else if generator { + StandardConstructors::generator_function + } else { + StandardConstructors::function + }; + + let prototype = get_prototype_from_constructor(new_target, default, context)?; + if let Some((body_arg, args)) = args.split_last() { + let parameters = if args.is_empty() { + FormalParameterList::default() + } else { + let mut parameters = Vec::with_capacity(args.len()); + for arg in args { + parameters.push(arg.to_string(context)?.as_slice().to_owned()); + } + let mut parameters = parameters.join(utf16!(",")); + parameters.push(u16::from(b')')); + + // TODO: make parser generic to u32 iterators + let parameters = match Parser::new(String::from_utf16_lossy(¶meters).as_bytes()) + .parse_formal_parameters(context.interner_mut(), generator, r#async) + { + Ok(parameters) => parameters, + Err(e) => { + return Err(JsNativeError::syntax() + .with_message(format!("failed to parse function parameters: {e}")) + .into()) + } + }; + + if generator && contains(¶meters, ContainsSymbol::YieldExpression) { + return Err(JsNativeError::syntax().with_message( + "yield expression is not allowed in formal parameter list of generator function", + ).into()); + } + + parameters + }; + + // It is a Syntax Error if FormalParameters Contains YieldExpression is true. + if generator && r#async && contains(¶meters, ContainsSymbol::YieldExpression) { + return Err(JsNativeError::syntax() + .with_message("yield expression not allowed in async generator parameters") + .into()); + } + + // It is a Syntax Error if FormalParameters Contains AwaitExpression is true. + if generator && r#async && contains(¶meters, ContainsSymbol::AwaitExpression) { + return Err(JsNativeError::syntax() + .with_message("await expression not allowed in async generator parameters") + .into()); + } + + let body_arg = body_arg.to_string(context)?; + + // TODO: make parser generic to u32 iterators + let body = match Parser::new(body_arg.to_std_string_escaped().as_bytes()) + .parse_function_body(context.interner_mut(), generator, r#async) + { + Ok(statement_list) => statement_list, + Err(e) => { + return Err(JsNativeError::syntax() + .with_message(format!("failed to parse function body: {e}")) + .into()) + } + }; + + // Early Error: If BindingIdentifier is present and the source text matched by BindingIdentifier is strict mode code, + // it is a Syntax Error if the StringValue of BindingIdentifier is "eval" or "arguments". + if body.strict() { + for name in bound_names(¶meters) { + if name == Sym::ARGUMENTS || name == Sym::EVAL { + return Err(JsNativeError::syntax() + .with_message(" Unexpected 'eval' or 'arguments' in strict mode") + .into()); + } + } + } + + // Early Error: If the source code matching FormalParameters is strict mode code, + // the Early Error rules for UniqueFormalParameters : FormalParameters are applied. + if (body.strict()) && parameters.has_duplicates() { + return Err(JsNativeError::syntax() + .with_message("Duplicate parameter name not allowed in this context") + .into()); + } + + // Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of GeneratorBody is true + // and IsSimpleParameterList of FormalParameters is false. + if body.strict() && !parameters.is_simple() { + return Err(JsNativeError::syntax() + .with_message( + "Illegal 'use strict' directive in function with non-simple parameter list", + ) + .into()); + } + + // It is a Syntax Error if any element of the BoundNames of FormalParameters + // also occurs in the LexicallyDeclaredNames of FunctionBody. + // https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors + { + let lexically_declared_names = lexically_declared_names(&body); + for name in bound_names(¶meters) { + if lexically_declared_names.contains(&name) { + return Err(JsNativeError::syntax() + .with_message(format!( + "Redeclaration of formal parameter `{}`", + context.interner().resolve_expect(name.sym()) + )) + .into()); + } + } + } + + let code = FunctionCompiler::new() + .name(Sym::ANONYMOUS) + .generator(generator) + .r#async(r#async) + .compile(¶meters, &body, context)?; + + let environments = context.realm.environments.pop_to_global(); + + let function_object = if generator { + crate::vm::create_generator_function_object(code, r#async, context) + } else { + crate::vm::create_function_object(code, r#async, false, Some(prototype), context) + }; + + context.realm.environments.extend(environments); + + Ok(function_object) + } else if generator { + let code = FunctionCompiler::new() + .name(Sym::ANONYMOUS) + .generator(true) + .compile( + &FormalParameterList::default(), + &StatementList::default(), + context, + )?; + + let environments = context.realm.environments.pop_to_global(); + let function_object = + crate::vm::create_generator_function_object(code, r#async, context); + context.realm.environments.extend(environments); + + Ok(function_object) + } else { + let code = FunctionCompiler::new().name(Sym::ANONYMOUS).compile( + &FormalParameterList::default(), + &StatementList::default(), + context, + )?; + + let environments = context.realm.environments.pop_to_global(); + let function_object = + crate::vm::create_function_object(code, r#async, false, Some(prototype), context); + context.realm.environments.extend(environments); + + Ok(function_object) + } + } + + /// `Function.prototype.apply ( thisArg, argArray )` + /// + /// The apply() method invokes self with the first argument as the `this` value + /// and the rest of the arguments provided as an array (or an array-like object). + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-function.prototype.apply + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply + fn apply(this: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResult { + // 1. Let func be the this value. + // 2. If IsCallable(func) is false, throw a TypeError exception. + let func = this.as_callable().ok_or_else(|| { + JsNativeError::typ().with_message(format!("{} is not a function", this.display())) + })?; + + let this_arg = args.get_or_undefined(0); + let arg_array = args.get_or_undefined(1); + // 3. If argArray is undefined or null, then + if arg_array.is_null_or_undefined() { + // a. Perform PrepareForTailCall(). + // TODO?: 3.a. PrepareForTailCall + + // b. Return ? Call(func, thisArg). + return func.call(this_arg, &[], context); + } + + // 4. Let argList be ? CreateListFromArrayLike(argArray). + let arg_list = arg_array.create_list_from_array_like(&[], context)?; + + // 5. Perform PrepareForTailCall(). + // TODO?: 5. PrepareForTailCall + + // 6. Return ? Call(func, thisArg, argList). + func.call(this_arg, &arg_list, context) + } + + /// `Function.prototype.bind ( thisArg, ...args )` + /// + /// The bind() method creates a new function that, when called, has its + /// this keyword set to the provided value, with a given sequence of arguments + /// preceding any provided when the new function is called. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-function.prototype.bind + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind + fn bind(this: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResult { + // 1. Let Target be the this value. + // 2. If IsCallable(Target) is false, throw a TypeError exception. + let target = this.as_callable().ok_or_else(|| { + JsNativeError::typ() + .with_message("cannot bind `this` without a `[[Call]]` internal method") + })?; + + let this_arg = args.get_or_undefined(0).clone(); + let bound_args = args.get(1..).unwrap_or(&[]).to_vec(); + let arg_count = bound_args.len() as i64; + + // 3. Let F be ? BoundFunctionCreate(Target, thisArg, args). + let f = BoundFunction::create(target.clone(), this_arg, bound_args, context)?; + + // 4. Let L be 0. + let mut l = JsValue::new(0); + + // 5. Let targetHasLength be ? HasOwnProperty(Target, "length"). + // 6. If targetHasLength is true, then + if target.has_own_property("length", context)? { + // a. Let targetLen be ? Get(Target, "length"). + let target_len = target.get("length", context)?; + // b. If Type(targetLen) is Number, then + if target_len.is_number() { + // 1. Let targetLenAsInt be ! ToIntegerOrInfinity(targetLen). + match target_len + .to_integer_or_infinity(context) + .expect("to_integer_or_infinity cannot fail for a number") + { + // i. If targetLen is +∞𝔽, set L to +∞. + IntegerOrInfinity::PositiveInfinity => l = f64::INFINITY.into(), + // ii. Else if targetLen is -∞𝔽, set L to 0. + IntegerOrInfinity::NegativeInfinity => {} + // iii. Else, + IntegerOrInfinity::Integer(target_len) => { + // 2. Assert: targetLenAsInt is finite. + // 3. Let argCount be the number of elements in args. + // 4. Set L to max(targetLenAsInt - argCount, 0). + l = (target_len - arg_count).max(0).into(); + } + } + } + } + + // 7. Perform ! SetFunctionLength(F, L). + f.define_property_or_throw( + "length", + PropertyDescriptor::builder() + .value(l) + .writable(false) + .enumerable(false) + .configurable(true), + context, + ) + .expect("defining the `length` property for a new object should not fail"); + + // 8. Let targetName be ? Get(Target, "name"). + let target_name = target.get("name", context)?; + + // 9. If Type(targetName) is not String, set targetName to the empty String. + let target_name = target_name.as_string().map_or(js_string!(), Clone::clone); + + // 10. Perform SetFunctionName(F, targetName, "bound"). + set_function_name(&f, &target_name.into(), Some(js_string!("bound")), context); + + // 11. Return F. + Ok(f.into()) + } + + /// `Function.prototype.call ( thisArg, ...args )` + /// + /// The call() method calls a function with a given this value and arguments provided individually. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-function.prototype.call + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call + fn call(this: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResult { + // 1. Let func be the this value. + // 2. If IsCallable(func) is false, throw a TypeError exception. + let func = this.as_callable().ok_or_else(|| { + JsNativeError::typ().with_message(format!("{} is not a function", this.display())) + })?; + let this_arg = args.get_or_undefined(0); + + // 3. Perform PrepareForTailCall(). + // TODO?: 3. Perform PrepareForTailCall + + // 4. Return ? Call(func, thisArg, args). + func.call(this_arg, args.get(1..).unwrap_or(&[]), context) + } + + #[allow(clippy::wrong_self_convention)] + fn to_string(this: &JsValue, _: &[JsValue], context: &mut Context<'_>) -> JsResult { + let object = this.as_object().map(JsObject::borrow); + let function = object + .as_deref() + .and_then(Object::as_function) + .ok_or_else(|| JsNativeError::typ().with_message("Not a function"))?; + + let name = { + // Is there a case here where if there is no name field on a value + // name should default to None? Do all functions have names set? + let value = this + .as_object() + .expect("checked that `this` was an object above") + .get("name", &mut *context)?; + if value.is_null_or_undefined() { + None + } else { + Some(value.to_string(context)?) + } + }; + + let name = name + .filter(|n| !n.is_empty()) + .unwrap_or_else(|| "anonymous".into()); + + match function { + Function::Native { .. } | Function::Ordinary { .. } => { + Ok(js_string!(utf16!("[Function: "), &name, utf16!("]")).into()) + } + Function::Async { .. } => { + Ok(js_string!(utf16!("[AsyncFunction: "), &name, utf16!("]")).into()) + } + Function::Generator { .. } => { + Ok(js_string!(utf16!("[GeneratorFunction: "), &name, utf16!("]")).into()) + } + Function::AsyncGenerator { .. } => { + Ok(js_string!(utf16!("[AsyncGeneratorFunction: "), &name, utf16!("]")).into()) + } + } + } + + /// `Function.prototype [ @@hasInstance ] ( V )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-function.prototype-@@hasinstance + fn has_instance( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let F be the this value. + // 2. Return ? OrdinaryHasInstance(F, V). + Ok(JsValue::ordinary_has_instance(this, args.get_or_undefined(0), context)?.into()) + } + + #[allow(clippy::unnecessary_wraps)] + fn prototype(_: &JsValue, _: &[JsValue], _: &mut Context<'_>) -> JsResult { + Ok(JsValue::undefined()) + } +} + +impl BuiltIn for BuiltInFunctionObject { + const NAME: &'static str = "Function"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event("function", "init"); + + let function_prototype = context.intrinsics().constructors().function().prototype(); + FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(Self::prototype)) + .name("") + .length(0) + .constructor(false) + .build_function_prototype(&function_prototype); + + let symbol_has_instance = JsSymbol::has_instance(); + + let has_instance = + FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(Self::has_instance)) + .name("[Symbol.iterator]") + .length(1) + .constructor(false) + .build(); + + let throw_type_error = context.intrinsics().objects().throw_type_error(); + + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context.intrinsics().constructors().function().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .method(Self::apply, "apply", 2) + .method(Self::bind, "bind", 1) + .method(Self::call, "call", 1) + .method(Self::to_string, "toString", 0) + .property(symbol_has_instance, has_instance, Attribute::default()) + .property_descriptor( + "caller", + PropertyDescriptor::builder() + .get(throw_type_error.clone()) + .set(throw_type_error.clone()) + .enumerable(false) + .configurable(true), + ) + .property_descriptor( + "arguments", + PropertyDescriptor::builder() + .get(throw_type_error.clone()) + .set(throw_type_error) + .enumerable(false) + .configurable(true), + ) + .build() + .conv::() + .pipe(Some) + } +} + +/// Abstract operation `SetFunctionName` +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-setfunctionname +pub(crate) fn set_function_name( + function: &JsObject, + name: &PropertyKey, + prefix: Option, + context: &mut Context<'_>, +) { + // 1. Assert: F is an extensible object that does not have a "name" own property. + // 2. If Type(name) is Symbol, then + let mut name = match name { + PropertyKey::Symbol(sym) => { + // a. Let description be name's [[Description]] value. + // b. If description is undefined, set name to the empty String. + // c. Else, set name to the string-concatenation of "[", description, and "]". + sym.description().map_or_else( + || js_string!(), + |desc| js_string!(utf16!("["), &desc, utf16!("]")), + ) + } + PropertyKey::String(string) => string.clone(), + PropertyKey::Index(index) => js_string!(format!("{index}")), + }; + + // 3. Else if name is a Private Name, then + // a. Set name to name.[[Description]]. + // todo: implement Private Names + + // 4. If F has an [[InitialName]] internal slot, then + // a. Set F.[[InitialName]] to name. + // todo: implement [[InitialName]] for builtins + + // 5. If prefix is present, then + if let Some(prefix) = prefix { + name = js_string!(&prefix, utf16!(" "), &name); + // b. If F has an [[InitialName]] internal slot, then + // i. Optionally, set F.[[InitialName]] to name. + // todo: implement [[InitialName]] for builtins + } + + // 6. Return ! DefinePropertyOrThrow(F, "name", PropertyDescriptor { [[Value]]: name, + // [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }). + function + .define_property_or_throw( + "name", + PropertyDescriptor::builder() + .value(name) + .writable(false) + .enumerable(false) + .configurable(true), + context, + ) + .expect("defining the `name` property must not fail per the spec"); +} + +/// Binds a `Function Object` when `bind` is called. +#[derive(Debug, Trace, Finalize)] +pub struct BoundFunction { + target_function: JsObject, + this: JsValue, + args: Vec, +} + +impl BoundFunction { + /// Abstract operation `BoundFunctionCreate` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-boundfunctioncreate + pub fn create( + target_function: JsObject, + this: JsValue, + args: Vec, + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let proto be ? targetFunction.[[GetPrototypeOf]](). + let proto = target_function.__get_prototype_of__(context)?; + let is_constructor = target_function.is_constructor(); + + // 2. Let internalSlotsList be the internal slots listed in Table 35, plus [[Prototype]] and [[Extensible]]. + // 3. Let obj be ! MakeBasicObject(internalSlotsList). + // 4. Set obj.[[Prototype]] to proto. + // 5. Set obj.[[Call]] as described in 10.4.1.1. + // 6. If IsConstructor(targetFunction) is true, then + // a. Set obj.[[Construct]] as described in 10.4.1.2. + // 7. Set obj.[[BoundTargetFunction]] to targetFunction. + // 8. Set obj.[[BoundThis]] to boundThis. + // 9. Set obj.[[BoundArguments]] to boundArgs. + // 10. Return obj. + Ok(JsObject::from_proto_and_data( + proto, + ObjectData::bound_function( + Self { + target_function, + this, + args, + }, + is_constructor, + ), + )) + } + + /// Get a reference to the bound function's this. + pub const fn this(&self) -> &JsValue { + &self.this + } + + /// Get a reference to the bound function's target function. + pub const fn target_function(&self) -> &JsObject { + &self.target_function + } + + /// Get a reference to the bound function's args. + pub fn args(&self) -> &[JsValue] { + self.args.as_slice() + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/function/tests.rs b/javascript-engine/external/boa/boa_engine/src/builtins/function/tests.rs new file mode 100644 index 0000000..76f786d --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/function/tests.rs @@ -0,0 +1,275 @@ +use crate::{ + error::JsNativeError, + forward, forward_val, js_string, + native_function::NativeFunction, + object::{FunctionObjectBuilder, JsObject}, + property::{Attribute, PropertyDescriptor}, + string::utf16, + Context, JsNativeErrorKind, +}; + +#[allow(clippy::float_cmp)] +#[test] +fn arguments_object() { + let mut context = Context::default(); + + let init = r#" + function jason(a, b) { + return arguments[0]; + } + var val = jason(100, 6); + "#; + + eprintln!("{}", forward(&mut context, init)); + + let return_val = forward_val(&mut context, "val").expect("value expected"); + assert!(return_val.is_integer()); + assert_eq!( + return_val + .to_i32(&mut context) + .expect("Could not convert value to i32"), + 100 + ); +} + +#[test] +fn self_mutating_function_when_calling() { + let mut context = Context::default(); + let func = r#" + function x() { + x.y = 3; + } + x(); + "#; + eprintln!("{}", forward(&mut context, func)); + let y = forward_val(&mut context, "x.y").expect("value expected"); + assert!(y.is_integer()); + assert_eq!( + y.to_i32(&mut context) + .expect("Could not convert value to i32"), + 3 + ); +} + +#[test] +fn self_mutating_function_when_constructing() { + let mut context = Context::default(); + let func = r#" + function x() { + x.y = 3; + } + new x(); + "#; + eprintln!("{}", forward(&mut context, func)); + let y = forward_val(&mut context, "x.y").expect("value expected"); + assert!(y.is_integer()); + assert_eq!( + y.to_i32(&mut context) + .expect("Could not convert value to i32"), + 3 + ); +} + +#[test] +fn call_function_prototype() { + let mut context = Context::default(); + let func = r#" + Function.prototype() + "#; + let value = forward_val(&mut context, func).unwrap(); + assert!(value.is_undefined()); +} + +#[test] +fn call_function_prototype_with_arguments() { + let mut context = Context::default(); + let func = r#" + Function.prototype(1, "", new String("")) + "#; + let value = forward_val(&mut context, func).unwrap(); + assert!(value.is_undefined()); +} + +#[test] +fn call_function_prototype_with_new() { + let mut context = Context::default(); + let func = r#" + new Function.prototype() + "#; + let value = forward_val(&mut context, func); + assert!(value.is_err()); +} + +#[test] +fn function_prototype_name() { + let mut context = Context::default(); + let func = r#" + Function.prototype.name + "#; + let value = forward_val(&mut context, func).unwrap(); + assert!(value.is_string()); + assert!(value.as_string().unwrap().is_empty()); +} + +#[test] +#[allow(clippy::float_cmp)] +fn function_prototype_length() { + let mut context = Context::default(); + let func = r#" + Function.prototype.length + "#; + let value = forward_val(&mut context, func).unwrap(); + assert!(value.is_number()); + assert_eq!(value.as_number().unwrap(), 0.0); +} + +#[test] +fn function_prototype_call() { + let mut context = Context::default(); + let func = r#" + let e = new Error() + Object.prototype.toString.call(e) + "#; + let value = forward_val(&mut context, func).unwrap(); + assert!(value.is_string()); + assert_eq!(value.as_string().unwrap(), utf16!("[object Error]")); +} + +#[test] +fn function_prototype_call_throw() { + let mut context = Context::default(); + let throw = r#" + let call = Function.prototype.call; + call(call) + "#; + let err = forward_val(&mut context, throw).unwrap_err(); + let err = err.as_native().unwrap(); + assert!(matches!( + err, + JsNativeError { + kind: JsNativeErrorKind::Type, + .. + } + )); +} + +#[test] +fn function_prototype_call_multiple_args() { + let mut context = Context::default(); + let init = r#" + function f(a, b) { + this.a = a; + this.b = b; + } + let o = {a: 0, b: 0}; + f.call(o, 1, 2); + "#; + forward_val(&mut context, init).unwrap(); + let boolean = forward_val(&mut context, "o.a == 1") + .unwrap() + .as_boolean() + .unwrap(); + assert!(boolean); + let boolean = forward_val(&mut context, "o.b == 2") + .unwrap() + .as_boolean() + .unwrap(); + assert!(boolean); +} + +#[test] +fn function_prototype_apply() { + let mut context = Context::default(); + let init = r#" + const numbers = [6, 7, 3, 4, 2]; + const max = Math.max.apply(null, numbers); + const min = Math.min.apply(null, numbers); + "#; + forward_val(&mut context, init).unwrap(); + + let boolean = forward_val(&mut context, "max == 7") + .unwrap() + .as_boolean() + .unwrap(); + assert!(boolean); + + let boolean = forward_val(&mut context, "min == 2") + .unwrap() + .as_boolean() + .unwrap(); + assert!(boolean); +} + +#[test] +fn function_prototype_apply_on_object() { + let mut context = Context::default(); + let init = r#" + function f(a, b) { + this.a = a; + this.b = b; + } + let o = {a: 0, b: 0}; + f.apply(o, [1, 2]); + "#; + forward_val(&mut context, init).unwrap(); + + let boolean = forward_val(&mut context, "o.a == 1") + .unwrap() + .as_boolean() + .unwrap(); + assert!(boolean); + + let boolean = forward_val(&mut context, "o.b == 2") + .unwrap() + .as_boolean() + .unwrap(); + assert!(boolean); +} + +#[test] +fn closure_capture_clone() { + let mut context = Context::default(); + + let string = js_string!("Hello"); + let object = JsObject::with_object_proto(&mut context); + + object + .define_property_or_throw( + "key", + PropertyDescriptor::builder() + .value(" world!") + .writable(false) + .enumerable(false) + .configurable(false), + &mut context, + ) + .unwrap(); + + let func = FunctionObjectBuilder::new( + &mut context, + NativeFunction::from_copy_closure_with_captures( + |_, _, captures, context| { + let (string, object) = &captures; + + let hw = js_string!( + string, + &object + .__get_own_property__(&"key".into(), context)? + .and_then(|prop| prop.value().cloned()) + .and_then(|val| val.as_string().cloned()) + .ok_or_else( + || JsNativeError::typ().with_message("invalid `key` property") + )? + ); + Ok(hw.into()) + }, + (string, object), + ), + ) + .name("closure") + .build(); + + context.register_global_property("closure", func, Attribute::default()); + + assert_eq!(forward(&mut context, "closure()"), "\"Hello world!\""); +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/generator/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/generator/mod.rs new file mode 100644 index 0000000..bdbecb1 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/generator/mod.rs @@ -0,0 +1,431 @@ +//! Boa's implementation of ECMAScript's global `Generator` object. +//! +//! A Generator is an instance of a generator function and conforms to both the Iterator and Iterable interfaces. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! - [MDN documentation][mdn] +//! +//! [spec]: https://tc39.es/ecma262/#sec-generator-objects +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator + +use crate::{ + builtins::{iterable::create_iter_result_object, BuiltIn, JsArgs}, + environments::DeclarativeEnvironmentStack, + error::JsNativeError, + object::{ConstructorBuilder, JsObject, ObjectData}, + property::{Attribute, PropertyDescriptor}, + symbol::JsSymbol, + value::JsValue, + vm::{CallFrame, GeneratorResumeKind, ReturnType}, + Context, JsError, JsResult, +}; +use boa_gc::{Finalize, Gc, GcCell, Trace}; +use boa_profiler::Profiler; + +/// Indicates the state of a generator. +#[derive(Debug, Clone, Copy, PartialEq)] +pub(crate) enum GeneratorState { + Undefined, + SuspendedStart, + SuspendedYield, + Executing, + Completed, +} + +/// Holds all information that a generator needs to continue it's execution. +/// +/// All of the fields must be changed with those that are currently present in the +/// context/vm before the generator execution starts/resumes and after it has ended/yielded. +#[derive(Debug, Clone, Finalize, Trace)] +pub(crate) struct GeneratorContext { + pub(crate) environments: DeclarativeEnvironmentStack, + pub(crate) call_frame: CallFrame, + pub(crate) stack: Vec, +} + +/// The internal representation of a `Generator` object. +#[derive(Debug, Clone, Finalize, Trace)] +pub struct Generator { + /// The `[[GeneratorState]]` internal slot. + #[unsafe_ignore_trace] + pub(crate) state: GeneratorState, + + /// The `[[GeneratorContext]]` internal slot. + pub(crate) context: Option>>, +} + +impl BuiltIn for Generator { + const NAME: &'static str = "Generator"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let iterator_prototype = context + .intrinsics() + .objects() + .iterator_prototypes() + .iterator_prototype(); + + let generator_function_prototype = context + .intrinsics() + .constructors() + .generator_function() + .prototype(); + + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context.intrinsics().constructors().generator().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .property( + JsSymbol::to_string_tag(), + Self::NAME, + Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ) + .method(Self::next, "next", 1) + .method(Self::r#return, "return", 1) + .method(Self::throw, "throw", 1) + .inherit(iterator_prototype) + .build(); + + context + .intrinsics() + .constructors() + .generator() + .prototype + .insert_property( + "constructor", + PropertyDescriptor::builder() + .value(generator_function_prototype) + .writable(false) + .enumerable(false) + .configurable(true), + ); + + None + } +} + +impl Generator { + pub(crate) const LENGTH: usize = 0; + + #[allow(clippy::unnecessary_wraps)] + pub(crate) fn constructor( + _: &JsValue, + _: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let prototype = context.intrinsics().constructors().generator().prototype(); + + let this = JsObject::from_proto_and_data( + prototype, + ObjectData::generator(Self { + state: GeneratorState::Undefined, + context: None, + }), + ); + + Ok(this.into()) + } + + /// `Generator.prototype.next ( value )` + /// + /// The `next()` method returns an object with two properties done and value. + /// You can also provide a parameter to the next method to send a value to the generator. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-generator.prototype.next + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/next + pub(crate) fn next( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Return ? GeneratorResume(this value, value, empty). + this.as_object().map_or_else( + || { + Err(JsNativeError::typ() + .with_message("Generator.prototype.next called on non generator") + .into()) + }, + |obj| Self::generator_resume(obj, args.get_or_undefined(0), context), + ) + } + + /// `Generator.prototype.return ( value )` + /// + /// The `return()` method returns the given value and finishes the generator. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-generator.prototype.return + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/return + pub(crate) fn r#return( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let g be the this value. + // 2. Let C be Completion { [[Type]]: return, [[Value]]: value, [[Target]]: empty }. + // 3. Return ? GeneratorResumeAbrupt(g, C, empty). + Self::generator_resume_abrupt(this, Ok(args.get_or_undefined(0).clone()), context) + } + + /// `Generator.prototype.throw ( exception )` + /// + /// The `throw()` method resumes the execution of a generator by throwing an error into it + /// and returns an object with two properties done and value. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-generator.prototype.throw + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/throw + pub(crate) fn throw( + this: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let g be the this value. + // 2. Let C be ThrowCompletion(exception). + // 3. Return ? GeneratorResumeAbrupt(g, C, empty). + Self::generator_resume_abrupt( + this, + Err(JsError::from_opaque(args.get_or_undefined(0).clone())), + context, + ) + } + + /// `27.5.3.3 GeneratorResume ( generator, value, generatorBrand )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-generatorresume + pub(crate) fn generator_resume( + generator_obj: &JsObject, + value: &JsValue, + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let state be ? GeneratorValidate(generator, generatorBrand). + let mut generator_obj_mut = generator_obj.borrow_mut(); + let generator = generator_obj_mut.as_generator_mut().ok_or_else(|| { + JsNativeError::typ().with_message("generator resumed on non generator object") + })?; + let state = generator.state; + + if state == GeneratorState::Executing { + return Err(JsNativeError::typ() + .with_message("Generator should not be executing") + .into()); + } + + // 2. If state is completed, return CreateIterResultObject(undefined, true). + if state == GeneratorState::Completed { + return Ok(create_iter_result_object( + JsValue::undefined(), + true, + context, + )); + } + + // 3. Assert: state is either suspendedStart or suspendedYield. + assert!(matches!( + state, + GeneratorState::SuspendedStart | GeneratorState::SuspendedYield + )); + + // 4. Let genContext be generator.[[GeneratorContext]]. + // 5. Let methodContext be the running execution context. + // 6. Suspend methodContext. + // 7. Set generator.[[GeneratorState]] to executing. + generator.state = GeneratorState::Executing; + let first_execution = matches!(state, GeneratorState::SuspendedStart); + + let generator_context_cell = generator + .context + .take() + .expect("generator context cannot be empty here"); + let mut generator_context = generator_context_cell.borrow_mut(); + drop(generator_obj_mut); + + std::mem::swap( + &mut context.realm.environments, + &mut generator_context.environments, + ); + std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); + context.vm.push_frame(generator_context.call_frame.clone()); + if !first_execution { + context.vm.push(value.clone()); + } + + context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Normal; + + let result = context.run(); + + generator_context.call_frame = context + .vm + .pop_frame() + .expect("generator call frame must exist"); + std::mem::swap( + &mut context.realm.environments, + &mut generator_context.environments, + ); + std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); + + let mut generator_obj_mut = generator_obj.borrow_mut(); + let generator = generator_obj_mut + .as_generator_mut() + .expect("already checked this object type"); + + match result { + Ok((value, ReturnType::Yield)) => { + generator.state = GeneratorState::SuspendedYield; + drop(generator_context); + generator.context = Some(generator_context_cell); + Ok(create_iter_result_object(value, false, context)) + } + Ok((value, _)) => { + generator.state = GeneratorState::Completed; + Ok(create_iter_result_object(value, true, context)) + } + Err(value) => { + generator.state = GeneratorState::Completed; + Err(value) + } + } + + // 8. Push genContext onto the execution context stack; genContext is now the running execution context. + // 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. + // 10. Assert: When we return here, genContext has already been removed from the execution context stack and methodContext is the currently running execution context. + // 11. Return Completion(result). + } + + /// `27.5.3.4 GeneratorResumeAbrupt ( generator, abruptCompletion, generatorBrand )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-generatorresumeabrupt + pub(crate) fn generator_resume_abrupt( + this: &JsValue, + abrupt_completion: JsResult, + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let state be ? GeneratorValidate(generator, generatorBrand). + let generator_obj = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("generator resumed on non generator object") + })?; + let mut generator_obj_mut = generator_obj.borrow_mut(); + let generator = generator_obj_mut.as_generator_mut().ok_or_else(|| { + JsNativeError::typ().with_message("generator resumed on non generator object") + })?; + let mut state = generator.state; + + if state == GeneratorState::Executing { + return Err(JsNativeError::typ() + .with_message("Generator should not be executing") + .into()); + } + + // 2. If state is suspendedStart, then + if state == GeneratorState::SuspendedStart { + // a. Set generator.[[GeneratorState]] to completed. + generator.state = GeneratorState::Completed; + // b. Once a generator enters the completed state it never leaves it and its associated execution context is never resumed. Any execution state associated with generator can be discarded at this point. + generator.context = None; + // c. Set state to completed. + state = GeneratorState::Completed; + } + + // 3. If state is completed, then + if state == GeneratorState::Completed { + // a. If abruptCompletion.[[Type]] is return, then + if let Ok(value) = abrupt_completion { + // i. Return CreateIterResultObject(abruptCompletion.[[Value]], true). + return Ok(create_iter_result_object(value, true, context)); + } + // b. Return Completion(abruptCompletion). + return abrupt_completion; + } + + // 4. Assert: state is suspendedYield. + // 5. Let genContext be generator.[[GeneratorContext]]. + // 6. Let methodContext be the running execution context. + // 7. Suspend methodContext. + // 8. Set generator.[[GeneratorState]] to executing. + // 9. Push genContext onto the execution context stack; genContext is now the running execution context. + // 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. + // 11. Assert: When we return here, genContext has already been removed from the execution context stack and methodContext is the currently running execution context. + // 12. Return Completion(result). + let generator_context_cell = generator + .context + .take() + .expect("generator context cannot be empty here"); + let mut generator_context = generator_context_cell.borrow_mut(); + + generator.state = GeneratorState::Executing; + drop(generator_obj_mut); + + std::mem::swap( + &mut context.realm.environments, + &mut generator_context.environments, + ); + std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); + context.vm.push_frame(generator_context.call_frame.clone()); + + let result = match abrupt_completion { + Ok(value) => { + context.vm.push(value); + context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Return; + context.run() + } + Err(value) => { + let value = value.to_opaque(context); + context.vm.push(value); + context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Throw; + context.run() + } + }; + generator_context.call_frame = context + .vm + .pop_frame() + .expect("generator call frame must exist"); + std::mem::swap( + &mut context.realm.environments, + &mut generator_context.environments, + ); + std::mem::swap(&mut context.vm.stack, &mut generator_context.stack); + + let mut generator_obj_mut = generator_obj.borrow_mut(); + let generator = generator_obj_mut + .as_generator_mut() + .expect("already checked this object type"); + + match result { + Ok((value, ReturnType::Yield)) => { + generator.state = GeneratorState::SuspendedYield; + drop(generator_context); + generator.context = Some(generator_context_cell); + Ok(create_iter_result_object(value, false, context)) + } + Ok((value, _)) => { + generator.state = GeneratorState::Completed; + Ok(create_iter_result_object(value, true, context)) + } + Err(value) => { + generator.state = GeneratorState::Completed; + Err(value) + } + } + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/generator_function/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/generator_function/mod.rs new file mode 100644 index 0000000..637a9cd --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/generator_function/mod.rs @@ -0,0 +1,129 @@ +//! Boa's implementation of ECMAScript's global `GeneratorFunction` object. +//! +//! The `GeneratorFunction` constructor creates a new generator function object. +//! In ECMAScript, every generator function is actually a `GeneratorFunction` object. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! - [MDN documentation][mdn] +//! +//! [spec]: https://tc39.es/ecma262/#sec-generatorfunction-objects +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/GeneratorFunction + +use crate::{ + builtins::{ + function::{BuiltInFunctionObject, ConstructorKind, Function}, + BuiltIn, + }, + native_function::NativeFunction, + object::ObjectData, + property::PropertyDescriptor, + symbol::JsSymbol, + value::JsValue, + Context, JsResult, +}; +use boa_profiler::Profiler; + +/// The internal representation of a `Generator` object. +#[derive(Debug, Clone, Copy)] +pub struct GeneratorFunction; + +impl BuiltIn for GeneratorFunction { + const NAME: &'static str = "GeneratorFunction"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let prototype = &context + .intrinsics() + .constructors() + .generator_function() + .prototype; + let constructor = &context + .intrinsics() + .constructors() + .generator_function() + .constructor; + + constructor.set_prototype(Some( + context.intrinsics().constructors().function().constructor(), + )); + let property = PropertyDescriptor::builder() + .value(1) + .writable(false) + .enumerable(false) + .configurable(true); + constructor.borrow_mut().insert("length", property); + let property = PropertyDescriptor::builder() + .value("GeneratorFunction") + .writable(false) + .enumerable(false) + .configurable(true); + constructor.borrow_mut().insert("name", property); + let property = PropertyDescriptor::builder() + .value( + context + .intrinsics() + .constructors() + .generator_function() + .prototype(), + ) + .writable(false) + .enumerable(false) + .configurable(false); + constructor.borrow_mut().insert("prototype", property); + constructor.borrow_mut().data = ObjectData::function(Function::Native { + function: NativeFunction::from_fn_ptr(Self::constructor), + constructor: Some(ConstructorKind::Base), + }); + + prototype.set_prototype(Some( + context.intrinsics().constructors().function().prototype(), + )); + let property = PropertyDescriptor::builder() + .value( + context + .intrinsics() + .constructors() + .generator_function() + .constructor(), + ) + .writable(false) + .enumerable(false) + .configurable(true); + prototype.borrow_mut().insert("constructor", property); + let property = PropertyDescriptor::builder() + .value(context.intrinsics().constructors().generator().prototype()) + .writable(false) + .enumerable(false) + .configurable(true); + prototype.borrow_mut().insert("prototype", property); + let property = PropertyDescriptor::builder() + .value("GeneratorFunction") + .writable(false) + .enumerable(false) + .configurable(true); + prototype + .borrow_mut() + .insert(JsSymbol::to_string_tag(), property); + + None + } +} + +impl GeneratorFunction { + /// `GeneratorFunction ( p1, p2, … , pn, body )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-generatorfunction + pub(crate) fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + BuiltInFunctionObject::create_dynamic_function(new_target, args, false, true, context) + .map(Into::into) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/global_this/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/global_this/mod.rs new file mode 100644 index 0000000..89525ed --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/global_this/mod.rs @@ -0,0 +1,30 @@ +//! Boa's implementation of ECMAScript's global `globalThis` property. +//! +//! The global globalThis property contains the global this value, +//! which is akin to the global object. +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-globalthis +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis + +use crate::{builtins::BuiltIn, Context, JsValue}; +use boa_profiler::Profiler; + +#[cfg(test)] +mod tests; + +/// The JavaScript `globalThis`. +pub(crate) struct GlobalThis; + +impl BuiltIn for GlobalThis { + const NAME: &'static str = "globalThis"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + Some(context.global_object().clone().into()) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/global_this/tests.rs b/javascript-engine/external/boa/boa_engine/src/builtins/global_this/tests.rs new file mode 100644 index 0000000..719ef51 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/global_this/tests.rs @@ -0,0 +1,10 @@ +use crate::exec; + +#[test] +fn global_this_exists_on_global_object_and_evaluates_to_an_object() { + let scenario = r#" + typeof globalThis; + "#; + + assert_eq!(&exec(scenario), "\"object\""); +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/infinity/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/infinity/mod.rs new file mode 100644 index 0000000..77c3b37 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/infinity/mod.rs @@ -0,0 +1,34 @@ +//! Boa's implementation of ECMAScript's global `Infinity` property. +//! +//! The global property `Infinity` is a numeric value representing infinity. +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-value-properties-of-the-global-object-infinity +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Infinity + +#[cfg(test)] +mod tests; + +use crate::{builtins::BuiltIn, property::Attribute, Context, JsValue}; +use boa_profiler::Profiler; + +/// JavaScript global `Infinity` property. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct Infinity; + +impl BuiltIn for Infinity { + const NAME: &'static str = "Infinity"; + + const ATTRIBUTE: Attribute = Attribute::READONLY + .union(Attribute::NON_ENUMERABLE) + .union(Attribute::PERMANENT); + + fn init(_: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + Some(f64::INFINITY.into()) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/infinity/tests.rs b/javascript-engine/external/boa/boa_engine/src/builtins/infinity/tests.rs new file mode 100644 index 0000000..f63cbde --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/infinity/tests.rs @@ -0,0 +1,19 @@ +use crate::exec; + +#[test] +fn infinity_exists_on_global_object_and_evaluates_to_infinity_value() { + let scenario = r#" + Infinity; + "#; + + assert_eq!(&exec(scenario), "Infinity"); +} + +#[test] +fn infinity_exists_and_equals_to_number_positive_infinity_value() { + let scenario = r#" + Number.POSITIVE_INFINITY === Infinity; + "#; + + assert_eq!(&exec(scenario), "true"); +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/intl/collator/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/intl/collator/mod.rs new file mode 100644 index 0000000..8fcca3d --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/intl/collator/mod.rs @@ -0,0 +1,558 @@ +use boa_gc::{custom_trace, Finalize, Trace}; +use boa_profiler::Profiler; +use icu_collator::{ + provider::CollationMetadataV1Marker, AlternateHandling, CaseFirst, MaxVariable, Numeric, +}; + +use icu_locid::{ + extensions::unicode::Value, extensions_unicode_key as key, extensions_unicode_value as value, + Locale, +}; +use icu_provider::DataLocale; +use tap::{Conv, Pipe}; + +use crate::{ + builtins::{BuiltIn, JsArgs}, + context::{intrinsics::StandardConstructors, BoaProvider}, + native_function::NativeFunction, + object::{ + internal_methods::get_prototype_from_constructor, ConstructorBuilder, + FunctionObjectBuilder, JsFunction, JsObject, ObjectData, + }, + property::Attribute, + symbol::JsSymbol, + Context, JsNativeError, JsResult, JsValue, +}; + +use super::{ + locale::{canonicalize_locale_list, resolve_locale, supported_locales, validate_extension}, + options::{coerce_options_to_object, get_option, IntlOptions, LocaleMatcher}, + Service, +}; + +mod options; +pub(crate) use options::*; + +pub struct Collator { + locale: Locale, + collation: Value, + numeric: bool, + case_first: Option, + usage: Usage, + sensitivity: Sensitivity, + ignore_punctuation: bool, + collator: icu_collator::Collator, + bound_compare: Option, +} + +impl Finalize for Collator {} + +// SAFETY: only `bound_compare` is a traceable object. +unsafe impl Trace for Collator { + custom_trace!(this, mark(&this.bound_compare)); +} + +impl Collator { + /// Gets the inner [`icu_collator::Collator`] comparator. + pub(crate) const fn collator(&self) -> &icu_collator::Collator { + &self.collator + } +} + +impl std::fmt::Debug for Collator { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Collator") + .field("locale", &self.locale) + .field("collation", &self.collation) + .field("numeric", &self.numeric) + .field("case_first", &self.case_first) + .field("usage", &self.usage) + .field("sensitivity", &self.sensitivity) + .field("ignore_punctuation", &self.ignore_punctuation) + .field("collator", &"ICUCollator") + .field("bound_compare", &self.bound_compare) + .finish() + } +} + +#[derive(Debug, Clone)] +pub(in crate::builtins::intl) struct CollatorLocaleOptions { + collation: Option, + numeric: Option, + case_first: Option, +} + +impl Service for Collator { + type LangMarker = CollationMetadataV1Marker; + + type LocaleOptions = CollatorLocaleOptions; + + fn resolve(locale: &mut Locale, options: &mut Self::LocaleOptions, provider: BoaProvider<'_>) { + let collation = options + .collation + .take() + .filter(|co| { + validate_extension::(locale.id.clone(), key!("co"), co, &provider) + }) + .or_else(|| { + locale + .extensions + .unicode + .keywords + .get(&key!("co")) + .cloned() + .filter(|co| { + validate_extension::( + locale.id.clone(), + key!("co"), + co, + &provider, + ) + }) + }) + .filter(|co| co != &value!("search")); + + let numeric = + options.numeric.or_else( + || match locale.extensions.unicode.keywords.get(&key!("kn")) { + Some(a) if a == &value!("true") => Some(true), + Some(_) => Some(false), + _ => None, + }, + ); + + let case_first = options.case_first.or_else(|| { + match locale.extensions.unicode.keywords.get(&key!("kf")) { + Some(a) if a == &value!("upper") => Some(CaseFirst::UpperFirst), + Some(a) if a == &value!("lower") => Some(CaseFirst::LowerFirst), + Some(_) => Some(CaseFirst::Off), + _ => None, + } + }); + + locale.extensions.unicode.clear(); + + if let Some(co) = collation.clone() { + locale.extensions.unicode.keywords.set(key!("co"), co); + } + + if let Some(kn) = numeric.map(|kn| if kn { value!("true") } else { value!("false") }) { + locale.extensions.unicode.keywords.set(key!("kn"), kn); + } + + if let Some(kf) = case_first.map(|kf| match kf { + CaseFirst::Off => value!("false"), + CaseFirst::LowerFirst => value!("lower"), + CaseFirst::UpperFirst => value!("upper"), + _ => unreachable!(), + }) { + locale.extensions.unicode.keywords.set(key!("kf"), kf); + } + + options.collation = collation; + options.numeric = numeric; + options.case_first = case_first; + } +} + +impl BuiltIn for Collator { + const NAME: &'static str = "Collator"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let compare = + FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(Self::compare)) + .name("get compare") + .constructor(false) + .build(); + + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context.intrinsics().constructors().collator().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .static_method(Self::supported_locales_of, "supportedLocalesOf", 1) + .property( + JsSymbol::to_string_tag(), + "Intl.Collator", + Attribute::CONFIGURABLE, + ) + .accessor("compare", Some(compare), None, Attribute::CONFIGURABLE) + .method(Self::resolved_options, "resolvedOptions", 0) + .build() + .conv::() + .pipe(Some) + } +} + +impl Collator { + pub(crate) const LENGTH: usize = 0; + + /// Constructor [`Intl.Collator ( [ locales [ , options ] ] )`][spec]. + /// + /// Constructor for `Collator` objects. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma402/#sec-intl.collator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator + pub(crate) fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. + // 2. Let internalSlotsList be « [[InitializedCollator]], [[Locale]], [[Usage]], [[Sensitivity]], [[IgnorePunctuation]], [[Collation]], [[BoundCompare]] ». + // 3. If %Collator%.[[RelevantExtensionKeys]] contains "kn", then + // a. Append [[Numeric]] as the last element of internalSlotsList. + // 4. If %Collator%.[[RelevantExtensionKeys]] contains "kf", then + // a. Append [[CaseFirst]] as the last element of internalSlotsList. + // 5. Let collator be ? OrdinaryCreateFromConstructor(newTarget, "%Collator.prototype%", internalSlotsList). + // 6. Return ? InitializeCollator(collator, locales, options). + + let locales = args.get_or_undefined(0); + let options = args.get_or_undefined(1); + + // Abstract operation `InitializeCollator ( collator, locales, options )` + // https://tc39.es/ecma402/#sec-initializecollator + + // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales). + let requested_locales = canonicalize_locale_list(locales, context)?; + + // 2. Set options to ? CoerceOptionsToObject(options). + let options = coerce_options_to_object(options, context)?; + + // 3. Let usage be ? GetOption(options, "usage", string, « "sort", "search" », "sort"). + // 4. Set collator.[[Usage]] to usage. + // 5. If usage is "sort", then + // a. Let localeData be %Collator%.[[SortLocaleData]]. + // 6. Else, + // a. Let localeData be %Collator%.[[SearchLocaleData]]. + let usage = get_option::(&options, "usage", false, context)?.unwrap_or_default(); + + // 7. Let opt be a new Record. + // 8. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit"). + // 9. Set opt.[[localeMatcher]] to matcher. + let matcher = get_option::(&options, "localeMatcher", false, context)? + .unwrap_or_default(); + + // 10. Let collation be ? GetOption(options, "collation", string, empty, undefined). + // 11. If collation is not undefined, then + // a. If collation does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception. + // 12. Set opt.[[co]] to collation. + let collation = get_option::(&options, "collation", false, context)?; + + // 13. Let numeric be ? GetOption(options, "numeric", boolean, empty, undefined). + // 14. If numeric is not undefined, then + // a. Let numeric be ! ToString(numeric). + // 15. Set opt.[[kn]] to numeric. + let numeric = get_option::(&options, "numeric", false, context)?; + + // 16. Let caseFirst be ? GetOption(options, "caseFirst", string, « "upper", "lower", "false" », undefined). + // 17. Set opt.[[kf]] to caseFirst. + let case_first = get_option::(&options, "caseFirst", false, context)?; + + let mut intl_options = IntlOptions { + matcher, + service_options: CollatorLocaleOptions { + collation, + numeric, + case_first, + }, + }; + + // 18. Let relevantExtensionKeys be %Collator%.[[RelevantExtensionKeys]]. + // 19. Let r be ResolveLocale(%Collator%.[[AvailableLocales]], requestedLocales, opt, relevantExtensionKeys, localeData). + let mut locale = + resolve_locale::(&requested_locales, &mut intl_options, context.icu()); + + let collator_locale = { + // `collator_locale` needs to be different from the resolved locale because ECMA402 doesn't + // define `search` as a resolvable extension of a locale, so we need to add that extension + // only to the locale passed to the collator. + let mut col_loc = DataLocale::from(&locale); + if usage == Usage::Search { + intl_options.service_options.collation = None; + locale.extensions.unicode.keywords.remove(key!("co")); + col_loc.set_unicode_ext(key!("co"), value!("search")); + } + col_loc + }; + + // 20. Set collator.[[Locale]] to r.[[locale]]. + + // 21. Let collation be r.[[co]]. + // 22. If collation is null, let collation be "default". + // 23. Set collator.[[Collation]] to collation. + let collation = intl_options + .service_options + .collation + .unwrap_or(value!("default")); + + // 24. If relevantExtensionKeys contains "kn", then + // a. Set collator.[[Numeric]] to SameValue(r.[[kn]], "true"). + let numeric = intl_options.service_options.numeric.unwrap_or_default(); + + // 25. If relevantExtensionKeys contains "kf", then + // a. Set collator.[[CaseFirst]] to r.[[kf]]. + let case_first = intl_options.service_options.case_first; + + // 26. Let sensitivity be ? GetOption(options, "sensitivity", string, « "base", "accent", "case", "variant" », undefined). + // 28. Set collator.[[Sensitivity]] to sensitivity. + let sensitivity = get_option::(&options, "sensitivity", false, context)? + // 27. If sensitivity is undefined, then + // a. If usage is "sort", then + // i. Let sensitivity be "variant". + // b. Else, + // i. Let dataLocale be r.[[dataLocale]]. + // ii. Let dataLocaleData be localeData.[[]]. + // iii. Let sensitivity be dataLocaleData.[[sensitivity]]. + .or_else(|| (usage == Usage::Sort).then_some(Sensitivity::Variant)); + + // 29. Let ignorePunctuation be ? GetOption(options, "ignorePunctuation", boolean, empty, false). + // 30. Set collator.[[IgnorePunctuation]] to ignorePunctuation. + let ignore_punctuation = + get_option::(&options, "ignorePunctuation", false, context)?.unwrap_or_default(); + + let (strength, case_level) = sensitivity.map(Sensitivity::to_collator_options).unzip(); + + let (alternate_handling, max_variable) = ignore_punctuation + .then_some((AlternateHandling::Shifted, MaxVariable::Punctuation)) + .unzip(); + + let collator = context + .icu() + .provider() + .try_new_collator(&collator_locale, { + let mut options = icu_collator::CollatorOptions::new(); + options.strength = strength; + options.case_level = case_level; + options.case_first = case_first; + options.numeric = Some(if numeric { Numeric::On } else { Numeric::Off }); + options.alternate_handling = alternate_handling; + options.max_variable = max_variable; + options + }) + .map_err(|e| JsNativeError::typ().with_message(e.to_string()))?; + + let prototype = + get_prototype_from_constructor(new_target, StandardConstructors::collator, context)?; + let collator = JsObject::from_proto_and_data( + prototype, + ObjectData::collator(Collator { + locale, + collation, + numeric, + case_first, + usage, + sensitivity: sensitivity.unwrap_or(Sensitivity::Variant), + ignore_punctuation, + collator, + bound_compare: None, + }), + ); + + // 31. Return collator. + Ok(collator.into()) + } + + /// [`Intl.Collator.supportedLocalesOf ( locales [ , options ] )`][spec]. + /// + /// Returns an array containing those of the provided locales that are supported in collation + /// without having to fall back to the runtime's default locale. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma402/#sec-intl.collator.supportedlocalesof + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/supportedLocalesOf + fn supported_locales_of( + _: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + let locales = args.get_or_undefined(0); + let options = args.get_or_undefined(1); + + // 1. Let availableLocales be %Collator%.[[AvailableLocales]]. + // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales). + let requested_locales = canonicalize_locale_list(locales, context)?; + + // 3. Return ? SupportedLocales(availableLocales, requestedLocales, options). + supported_locales::<::LangMarker>(&requested_locales, options, context) + .map(JsValue::from) + } + + /// [`get Intl.Collator.prototype.compare`][spec]. + /// + /// Compares two strings according to the sort order of this Intl.Collator object. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma402/#sec-intl.collator.prototype.compare + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/compare + fn compare(this: &JsValue, _: &[JsValue], context: &mut Context<'_>) -> JsResult { + // 1. Let collator be the this value. + // 2. Perform ? RequireInternalSlot(collator, [[InitializedCollator]]). + let this = this.as_object().ok_or_else(|| { + JsNativeError::typ() + .with_message("`resolvedOptions` can only be called on a `Collator` object") + })?; + let collator_obj = this.clone(); + let mut collator = this.borrow_mut(); + let collator = collator.as_collator_mut().ok_or_else(|| { + JsNativeError::typ() + .with_message("`resolvedOptions` can only be called on a `Collator` object") + })?; + + // 3. If collator.[[BoundCompare]] is undefined, then + // a. Let F be a new built-in function object as defined in 10.3.3.1. + // b. Set F.[[Collator]] to collator. + // c. Set collator.[[BoundCompare]] to F. + let bound_compare = if let Some(f) = collator.bound_compare.clone() { + f + } else { + let bound_compare = FunctionObjectBuilder::new( + context, + // 10.3.3.1. Collator Compare Functions + // https://tc39.es/ecma402/#sec-collator-compare-functions + NativeFunction::from_copy_closure_with_captures( + |_, args, collator, context| { + // 1. Let collator be F.[[Collator]]. + // 2. Assert: Type(collator) is Object and collator has an [[InitializedCollator]] internal slot. + let collator = collator.borrow(); + let collator = collator + .as_collator() + .expect("checked above that the object was a collator object"); + + // 3. If x is not provided, let x be undefined. + // 5. Let X be ? ToString(x). + let x = args.get_or_undefined(0).to_string(context)?; + + // 4. If y is not provided, let y be undefined. + // 6. Let Y be ? ToString(y). + let y = args.get_or_undefined(1).to_string(context)?; + + // 7. Return CompareStrings(collator, X, Y). + let result = collator.collator.compare_utf16(&x, &y) as i32; + + Ok(result.into()) + }, + collator_obj, + ), + ) + .length(2) + .build(); + + collator.bound_compare = Some(bound_compare.clone()); + bound_compare + }; + + // 4. Return collator.[[BoundCompare]]. + Ok(bound_compare.into()) + } + + /// [`Intl.Collator.prototype.resolvedOptions ( )`][spec]. + /// + /// Returns a new object with properties reflecting the locale and collation options computed + /// during initialization of this `Intl.Collator` object. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma402/#sec-intl.collator.prototype.resolvedoptions + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/resolvedOptions + fn resolved_options( + this: &JsValue, + _: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. Let collator be the this value. + // 2. Perform ? RequireInternalSlot(collator, [[InitializedCollator]]). + let collator = this.as_object().map(JsObject::borrow).ok_or_else(|| { + JsNativeError::typ() + .with_message("`resolvedOptions` can only be called on a `Collator` object") + })?; + let collator = collator.as_collator().ok_or_else(|| { + JsNativeError::typ() + .with_message("`resolvedOptions` can only be called on a `Collator` object") + })?; + + // 3. Let options be OrdinaryObjectCreate(%Object.prototype%). + let options = JsObject::from_proto_and_data( + context.intrinsics().constructors().object().prototype(), + ObjectData::ordinary(), + ); + + // 4. For each row of Table 4, except the header row, in table order, do + // a. Let p be the Property value of the current row. + // b. Let v be the value of collator's internal slot whose name is the Internal Slot value of the current row. + // c. If the current row has an Extension Key value, then + // i. Let extensionKey be the Extension Key value of the current row. + // ii. If %Collator%.[[RelevantExtensionKeys]] does not contain extensionKey, then + // 1. Let v be undefined. + // d. If v is not undefined, then + // i. Perform ! CreateDataPropertyOrThrow(options, p, v). + // 5. Return options. + options + .create_data_property_or_throw("locale", collator.locale.to_string(), context) + .expect("operation must not fail per the spec"); + options + .create_data_property_or_throw( + "usage", + match collator.usage { + Usage::Search => "search", + Usage::Sort => "sort", + }, + context, + ) + .expect("operation must not fail per the spec"); + options + .create_data_property_or_throw( + "sensitivity", + match collator.sensitivity { + Sensitivity::Base => "base", + Sensitivity::Accent => "accent", + Sensitivity::Case => "case", + Sensitivity::Variant => "variant", + }, + context, + ) + .expect("operation must not fail per the spec"); + options + .create_data_property_or_throw( + "ignorePunctuation", + collator.ignore_punctuation, + context, + ) + .expect("operation must not fail per the spec"); + options + .create_data_property_or_throw("collation", collator.collation.to_string(), context) + .expect("operation must not fail per the spec"); + options + .create_data_property_or_throw("numeric", collator.numeric, context) + .expect("operation must not fail per the spec"); + if let Some(kf) = collator.case_first { + options + .create_data_property_or_throw( + "caseFirst", + match kf { + CaseFirst::Off => "false", + CaseFirst::LowerFirst => "lower", + CaseFirst::UpperFirst => "upper", + _ => unreachable!(), + }, + context, + ) + .expect("operation must not fail per the spec"); + } + + // 5. Return options. + Ok(options.into()) + } +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/intl/collator/options.rs b/javascript-engine/external/boa/boa_engine/src/builtins/intl/collator/options.rs new file mode 100644 index 0000000..928c038 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/intl/collator/options.rs @@ -0,0 +1,80 @@ +use std::str::FromStr; + +use icu_collator::{CaseLevel, Strength}; + +use crate::builtins::intl::options::OptionTypeParsable; + +#[derive(Debug, Clone, Copy)] +pub(crate) enum Sensitivity { + Base, + Accent, + Case, + Variant, +} + +impl Sensitivity { + /// Converts the sensitivity option to the equivalent ICU4X collator options. + pub(crate) const fn to_collator_options(self) -> (Strength, CaseLevel) { + match self { + Sensitivity::Base => (Strength::Primary, CaseLevel::Off), + Sensitivity::Accent => (Strength::Secondary, CaseLevel::Off), + Sensitivity::Case => (Strength::Primary, CaseLevel::On), + Sensitivity::Variant => (Strength::Tertiary, CaseLevel::On), + } + } +} + +#[derive(Debug)] +pub(crate) struct ParseSensitivityError; + +impl std::fmt::Display for ParseSensitivityError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("provided string was not `base`, `accent`, `case` or `variant`") + } +} + +impl FromStr for Sensitivity { + type Err = ParseSensitivityError; + + fn from_str(s: &str) -> Result { + match s { + "base" => Ok(Self::Base), + "accent" => Ok(Self::Accent), + "case" => Ok(Self::Case), + "variant" => Ok(Self::Variant), + _ => Err(ParseSensitivityError), + } + } +} + +impl OptionTypeParsable for Sensitivity {} + +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +pub(crate) enum Usage { + #[default] + Sort, + Search, +} + +#[derive(Debug)] +pub(crate) struct ParseUsageError; + +impl std::fmt::Display for ParseUsageError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("provided string was not `sort` or `search`") + } +} + +impl FromStr for Usage { + type Err = ParseUsageError; + + fn from_str(s: &str) -> Result { + match s { + "sort" => Ok(Self::Sort), + "search" => Ok(Self::Search), + _ => Err(ParseUsageError), + } + } +} + +impl OptionTypeParsable for Usage {} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/intl/date_time_format.rs b/javascript-engine/external/boa/boa_engine/src/builtins/intl/date_time_format.rs new file mode 100644 index 0000000..a4c90ac --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/intl/date_time_format.rs @@ -0,0 +1,259 @@ +//! This module implements the global `Intl.DateTimeFormat` object. +//! +//! `Intl.DateTimeFormat` is a built-in object that has properties and methods for date and time i18n. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma402/#datetimeformat-objects + +use crate::{ + context::intrinsics::StandardConstructors, + error::JsNativeError, + js_string, + object::{ + internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsFunction, JsObject, + ObjectData, + }, + Context, JsResult, JsString, JsValue, +}; + +use boa_gc::{Finalize, Trace}; +use boa_profiler::Profiler; +use icu_datetime::options::preferences::HourCycle; + +use super::options::OptionType; + +impl OptionType for HourCycle { + fn from_value(value: JsValue, context: &mut Context<'_>) -> JsResult { + match value.to_string(context)?.to_std_string_escaped().as_str() { + "h11" => Ok(HourCycle::H11), + "h12" => Ok(HourCycle::H12), + "h23" => Ok(HourCycle::H23), + "h24" => Ok(HourCycle::H24), + _ => Err(JsNativeError::range() + .with_message("provided string was not `h11`, `h12`, `h23` or `h24`") + .into()), + } + } +} + +/// JavaScript `Intl.DateTimeFormat` object. +#[derive(Debug, Clone, Trace, Finalize)] +pub struct DateTimeFormat { + initialized_date_time_format: bool, + locale: JsString, + calendar: JsString, + numbering_system: JsString, + time_zone: JsString, + weekday: JsString, + era: JsString, + year: JsString, + month: JsString, + day: JsString, + day_period: JsString, + hour: JsString, + minute: JsString, + second: JsString, + fractional_second_digits: JsString, + time_zone_name: JsString, + hour_cycle: JsString, + pattern: JsString, + bound_format: JsString, +} + +impl DateTimeFormat { + const NAME: &'static str = "DateTimeFormat"; + + pub(super) fn init(context: &mut Context<'_>) -> JsFunction { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + ConstructorBuilder::new(context, Self::constructor) + .name(Self::NAME) + .length(0) + .build() + } +} + +impl DateTimeFormat { + /// The `Intl.DateTimeFormat` constructor is the `%DateTimeFormat%` intrinsic object and a standard built-in property of the `Intl` object. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma402/#datetimeformat-objects + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat + pub(crate) fn constructor( + new_target: &JsValue, + _args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. + let prototype = get_prototype_from_constructor( + new_target, + StandardConstructors::date_time_format, + context, + )?; + // 2. Let dateTimeFormat be ? OrdinaryCreateFromConstructor(newTarget, "%DateTimeFormat.prototype%", + // « [[InitializedDateTimeFormat]], [[Locale]], [[Calendar]], [[NumberingSystem]], [[TimeZone]], [[Weekday]], + // [[Era]], [[Year]], [[Month]], [[Day]], [[DayPeriod]], [[Hour]], [[Minute]], [[Second]], + // [[FractionalSecondDigits]], [[TimeZoneName]], [[HourCycle]], [[Pattern]], [[BoundFormat]] »). + let date_time_format = JsObject::from_proto_and_data( + prototype, + ObjectData::date_time_format(Box::new(Self { + initialized_date_time_format: true, + locale: js_string!("en-US"), + calendar: js_string!("gregory"), + numbering_system: js_string!("arab"), + time_zone: js_string!("UTC"), + weekday: js_string!("narrow"), + era: js_string!("narrow"), + year: js_string!("numeric"), + month: js_string!("narrow"), + day: js_string!("numeric"), + day_period: js_string!("narrow"), + hour: js_string!("numeric"), + minute: js_string!("numeric"), + second: js_string!("numeric"), + fractional_second_digits: js_string!(""), + time_zone_name: js_string!(""), + hour_cycle: js_string!("h24"), + pattern: js_string!("{hour}:{minute}"), + bound_format: js_string!("undefined"), + })), + ); + + // TODO 3. Perform ? InitializeDateTimeFormat(dateTimeFormat, locales, options). + // TODO 4. If the implementation supports the normative optional constructor mode of 4.3 Note 1, then + // TODO a. Let this be the this value. + // TODO b. Return ? ChainDateTimeFormat(dateTimeFormat, NewTarget, this). + + // 5. Return dateTimeFormat. + Ok(date_time_format.into()) + } +} + +/// Represents the `required` and `defaults` arguments in the abstract operation +/// `toDateTimeOptions`. +/// +/// Since `required` and `defaults` differ only in the `any` and `all` variants, +/// we combine both in a single variant `AnyAll`. +#[allow(unused)] +#[derive(Debug, PartialEq)] +pub(crate) enum DateTimeReqs { + Date, + Time, + AnyAll, +} + +/// The abstract operation `toDateTimeOptions` is called with arguments `options`, `required` and +/// `defaults`. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma402/#sec-todatetimeoptions +#[allow(unused)] +pub(crate) fn to_date_time_options( + options: &JsValue, + required: &DateTimeReqs, + defaults: &DateTimeReqs, + context: &mut Context<'_>, +) -> JsResult { + // 1. If options is undefined, let options be null; + // otherwise let options be ? ToObject(options). + // 2. Let options be ! OrdinaryObjectCreate(options). + let options = if options.is_undefined() { + None + } else { + Some(options.to_object(context)?) + }; + let options = JsObject::from_proto_and_data(options, ObjectData::ordinary()); + + // 3. Let needDefaults be true. + let mut need_defaults = true; + + // 4. If required is "date" or "any", then + if [DateTimeReqs::Date, DateTimeReqs::AnyAll].contains(required) { + // a. For each property name prop of « "weekday", "year", "month", "day" », do + for property in ["weekday", "year", "month", "day"] { + // i. Let value be ? Get(options, prop). + let value = options.get(property, context)?; + + // ii. If value is not undefined, let needDefaults be false. + if !value.is_undefined() { + need_defaults = false; + } + } + } + + // 5. If required is "time" or "any", then + if [DateTimeReqs::Time, DateTimeReqs::AnyAll].contains(required) { + // a. For each property name prop of « "dayPeriod", "hour", "minute", "second", + // "fractionalSecondDigits" », do + for property in [ + "dayPeriod", + "hour", + "minute", + "second", + "fractionalSecondDigits", + ] { + // i. Let value be ? Get(options, prop). + let value = options.get(property, context)?; + + // ii. If value is not undefined, let needDefaults be false. + if !value.is_undefined() { + need_defaults = false; + } + } + } + + // 6. Let dateStyle be ? Get(options, "dateStyle"). + let date_style = options.get("dateStyle", context)?; + + // 7. Let timeStyle be ? Get(options, "timeStyle"). + let time_style = options.get("timeStyle", context)?; + + // 8. If dateStyle is not undefined or timeStyle is not undefined, let needDefaults be false. + if !date_style.is_undefined() || !time_style.is_undefined() { + need_defaults = false; + } + + // 9. If required is "date" and timeStyle is not undefined, then + if required == &DateTimeReqs::Date && !time_style.is_undefined() { + // a. Throw a TypeError exception. + return Err(JsNativeError::typ() + .with_message("'date' is required, but timeStyle was defined") + .into()); + } + + // 10. If required is "time" and dateStyle is not undefined, then + if required == &DateTimeReqs::Time && !date_style.is_undefined() { + // a. Throw a TypeError exception. + return Err(JsNativeError::typ() + .with_message("'time' is required, but dateStyle was defined") + .into()); + } + + // 11. If needDefaults is true and defaults is either "date" or "all", then + if need_defaults && [DateTimeReqs::Date, DateTimeReqs::AnyAll].contains(defaults) { + // a. For each property name prop of « "year", "month", "day" », do + for property in ["year", "month", "day"] { + // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). + options.create_data_property_or_throw(property, "numeric", context)?; + } + } + + // 12. If needDefaults is true and defaults is either "time" or "all", then + if need_defaults && [DateTimeReqs::Time, DateTimeReqs::AnyAll].contains(defaults) { + // a. For each property name prop of « "hour", "minute", "second" », do + for property in ["hour", "minute", "second"] { + // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). + options.create_data_property_or_throw(property, "numeric", context)?; + } + } + + // 13. Return options. + Ok(options) +} diff --git a/javascript-engine/external/boa/boa_engine/src/builtins/intl/list_format/mod.rs b/javascript-engine/external/boa/boa_engine/src/builtins/intl/list_format/mod.rs new file mode 100644 index 0000000..42c5f20 --- /dev/null +++ b/javascript-engine/external/boa/boa_engine/src/builtins/intl/list_format/mod.rs @@ -0,0 +1,498 @@ +use std::fmt::Write; + +use boa_profiler::Profiler; +use icu_list::{provider::AndListV1Marker, ListFormatter, ListLength}; +use icu_locid::Locale; +use icu_provider::DataLocale; +use tap::{Conv, Pipe}; + +use crate::{ + builtins::{Array, BuiltIn, JsArgs}, + context::intrinsics::StandardConstructors, + object::{ + internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData, + }, + property::Attribute, + symbol::JsSymbol, + Context, JsNativeError, JsResult, JsString, JsValue, +}; + +use super::{ + locale::{canonicalize_locale_list, resolve_locale, supported_locales}, + options::{get_option, get_options_object, IntlOptions, LocaleMatcher}, + Service, +}; + +mod options; +pub(crate) use options::*; +pub struct ListFormat { + locale: Locale, + typ: ListFormatType, + style: ListLength, + formatter: ListFormatter, +} + +impl std::fmt::Debug for ListFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ListFormat") + .field("locale", &self.locale) + .field("typ", &self.typ) + .field("style", &self.style) + .field("formatter", &"ListFormatter") + .finish() + } +} + +impl Service for ListFormat { + type LangMarker = AndListV1Marker; + + type LocaleOptions = (); +} + +impl BuiltIn for ListFormat { + const NAME: &'static str = "ListFormat"; + + fn init(context: &mut Context<'_>) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + ConstructorBuilder::with_standard_constructor( + context, + Self::constructor, + context.intrinsics().constructors().list_format().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .static_method(Self::supported_locales_of, "supportedLocalesOf", 1) + .property( + JsSymbol::to_string_tag(), + "Intl.ListFormat", + Attribute::CONFIGURABLE, + ) + .method(Self::format, "format", 1) + .method(Self::format_to_parts, "formatToParts", 1) + .method(Self::resolved_options, "resolvedOptions", 0) + .build() + .conv::() + .pipe(Some) + } +} + +impl ListFormat { + pub(crate) const LENGTH: usize = 0; + + /// Constructor [`Intl.ListFormat ( [ locales [ , options ] ] )`][spec]. + /// + /// Constructor for `ListFormat` objects. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma402/#sec-Intl.ListFormat + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat + pub(crate) fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context<'_>, + ) -> JsResult { + // 1. If NewTarget is undefined, throw a TypeError exception. + if new_target.is_undefined() { + return Err(JsNativeError::typ() + .with_message("cannot call `Intl.ListFormat` constructor without `new`") + .into()); + } + + let locales = args.get_or_undefined(0); + let options = args.get_or_undefined(1); + + // 3. Let requestedLocales be ? CanonicalizeLocaleList(locales). + let requested_locales = canonicalize_locale_list(locales, context)?; + + // 4. Set options to ? GetOptionsObject(options). + let options = get_options_object(options)?; + + // 5. Let opt be a new Record. + // 6. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit"). + let matcher = get_option::(&options, "localeMatcher", false, context)? + .unwrap_or_default(); + + // 7. Set opt.[[localeMatcher]] to matcher. + // 8. Let localeData be %ListFormat%.[[LocaleData]]. + // 9. Let r be ResolveLocale(%ListFormat%.[[AvailableLocales]], requestedLocales, opt, %ListFormat%.[[RelevantExtensionKeys]], localeData). + // 10. Set listFormat.[[Locale]] to r.[[locale]]. + let locale = resolve_locale::( + &requested_locales, + &mut IntlOptions { + matcher, + ..Default::default() + }, + context.icu(), + ); + + // 11. Let type be ? GetOption(options, "type", string, « "conjunction", "disjunction", "unit" », "conjunction"). + // 12. Set listFormat.[[Type]] to type. + let typ = + get_option::(&options, "type", false, context)?.unwrap_or_default(); + + // 13. Let style be ? GetOption(options, "style", string, « "long", "short", "narrow" », "long"). + // 14. Set listFormat.[[Style]] to style. + let style = get_option::(&options, "style", false, context)? + .unwrap_or(ListLength::Wide); + + // 15. Let dataLocale be r.[[dataLocale]]. + // 16. Let dataLocaleData be localeData.[[]]. + // 17. Let dataLocaleTypes be dataLocaleData.[[]]. + // 18. Set listFormat.[[Templates]] to dataLocaleTypes.[[ + + +
+
+ +

Boa Playground

+
+
+
+

+
+
+ + diff --git a/javascript-engine/external/boa/index.js b/javascript-engine/external/boa/index.js new file mode 100644 index 0000000..be1b960 --- /dev/null +++ b/javascript-engine/external/boa/index.js @@ -0,0 +1,46 @@ +import { evaluate } from "./boa_wasm/pkg"; + +import * as monaco from "monaco-editor/esm/vs/editor/editor.api"; + +const initialCode = `\ +function greet(targetName) { + return 'Hello, ' + targetName + '!'; +} + +greet('World') +`; + +const editor = monaco.editor.create( + document.getElementsByClassName("textbox")[0], + { + value: initialCode, + language: "javascript", + theme: "vs", + minimap: { + enabled: false, + }, + } +); + +// Fix size of Monaco Editor when window resize +window.addEventListener("resize", () => { + editor.layout(); +}); + +window.evaluate = evaluate; + +editor.getModel().onDidChangeContent(inputHandler); +inputHandler(); // Evaluate initial code + +function inputHandler(evt) { + const text = editor.getValue(); + let p = document.querySelector("p.output"); + + try { + let result = window.evaluate(text); + p.textContent = `> ${result}`; + } catch (err) { + console.error(err); + p.innerHTML = `${err}`; + } +} diff --git a/javascript-engine/external/boa/package-lock.json b/javascript-engine/external/boa/package-lock.json new file mode 100644 index 0000000..cfa4ee9 --- /dev/null +++ b/javascript-engine/external/boa/package-lock.json @@ -0,0 +1,8120 @@ +{ + "name": "boa", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "monaco-editor": "^0.34.1" + }, + "devDependencies": { + "@wasm-tool/wasm-pack-plugin": "^1.6.0", + "bootstrap": "^5.2.3", + "clean-webpack-plugin": "^4.0.0", + "copy-webpack-plugin": "^11.0.0", + "css-loader": "^6.7.3", + "file-loader": "^6.2.0", + "html-webpack-plugin": "^5.5.0", + "monaco-editor-webpack-plugin": "^7.0.1", + "prettier": "^2.8.3", + "style-loader": "^3.3.1", + "terser-webpack-plugin": "^5.3.6", + "webpack": "^5.75.0", + "webpack-cli": "^5.0.1", + "webpack-dev-server": "^4.11.1" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", + "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.6", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", + "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==", + "dev": true, + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dev": true, + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.4.6", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz", + "integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "node_modules/@types/express": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", + "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.31", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", + "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "dev": true + }, + "node_modules/@types/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.7.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.19.tgz", + "integrity": "sha512-Sq1itGUKUX1ap7GgZlrzdBydjbsJL/NSQt/4wkAxUJ7/OS5c2WkoN6WSpWc2Yc5wtKMZOUA0VCs/j2XJadN3HA==", + "dev": true + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "node_modules/@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, + "dependencies": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@wasm-tool/wasm-pack-plugin": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@wasm-tool/wasm-pack-plugin/-/wasm-pack-plugin-1.6.0.tgz", + "integrity": "sha512-Iax4nEgIvVCZqrmuseJm7ln/muWpg7uT5fXMAT0crYo+k5JTuZE58DJvBQoeIAegA3IM9cZgfkcZjAOUCPsT1g==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "command-exists": "^1.2.7", + "watchpack": "^2.1.1", + "which": "^2.0.2" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.0.1.tgz", + "integrity": "sha512-njsdJXJSiS2iNbQVS0eT8A/KPnmyH4pv1APj2K0d1wrZcBLw+yppxOy4CGqa0OxDJkzfL/XELDhD8rocnIwB5A==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.1.tgz", + "integrity": "sha512-fE1UEWTwsAxRhrJNikE7v4EotYflkEhBL7EbajfkPlf6E37/2QshOy/D48Mw8G5XMFlQtS6YV42vtbG9zBpIQA==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.1.tgz", + "integrity": "sha512-0G7tNyS+yW8TdgHwZKlDWYXFA6OJQnoLCQvYKkQP0Q2X205PSQ6RNUj0M+1OB/9gRQaUZ/ccYfaxd0nhaWKfjw==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bonjour-service": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz", + "integrity": "sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==", + "dev": true, + "dependencies": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/bootstrap": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz", + "integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.6" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001410", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001410.tgz", + "integrity": "sha512-QoblBnuE+rG0lc3Ur9ltP5q47lbguipa/ncNMyyGuqPk44FxbScWAeEO+k5fSQ8WekdAK4mWqNs1rADDAiN5xQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/clean-css": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", + "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==", + "dev": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clean-webpack-plugin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz", + "integrity": "sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==", + "dev": true, + "dependencies": { + "del": "^4.1.1" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "webpack": ">=4.0.0 <6.0.0" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, + "node_modules/command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", + "dev": true + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-loader": { + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.3.tgz", + "integrity": "sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.19", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "dependencies": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/del/node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/globby/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", + "dev": true + }, + "node_modules/dns-packet": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", + "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "dev": true, + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.260", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.260.tgz", + "integrity": "sha512-1GxPM2Bdz1AjuNjho9/TqJfxM7KZ7R8s4vA5cbbIoVacQXfvZlV+d7Y1lu4BhGzEBfjjhakr3NXKqN0PxPXIsg==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-loader/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/file-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/file-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/globby": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", + "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "dev": true + }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dev": true, + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", + "dev": true, + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "webpack": "^5.20.0" + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "dependencies": { + "is-path-inside": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "dependencies": { + "path-is-inside": "^1.0.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz", + "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.7.tgz", + "integrity": "sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw==", + "dev": true, + "dependencies": { + "fs-monkey": "^1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/monaco-editor": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz", + "integrity": "sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ==" + }, + "node_modules/monaco-editor-webpack-plugin": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-7.0.1.tgz", + "integrity": "sha512-M8qIqizltrPlIbrb73cZdTWfU9sIsUVFvAZkL3KGjAHmVWEJ0hZKa/uad14JuOckc0GwnCaoGHvMoYtJjVyCzw==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.2" + }, + "peerDependencies": { + "monaco-editor": ">= 0.31.0", + "webpack": "^4.5.0 || 5.x" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss": { + "version": "8.4.19", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz", + "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/prettier": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz", + "integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dev": true, + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "node_modules/selfsigned": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "dev": true, + "dependencies": { + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/spdy-transport/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/spdy-transport/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/spdy/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/spdy/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/style-loader": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", + "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", + "integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", + "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.14", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "terser": "^5.14.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", + "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/webpack": { + "version": "5.75.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", + "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz", + "integrity": "sha512-S3KVAyfwUqr0Mo/ur3NzIp6jnerNpo7GUO6so51mxLi1spqsA17YcMXy0WOIJtBSnj748lthxC6XLbNKh/ZC+A==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^2.0.1", + "@webpack-cli/info": "^2.0.1", + "@webpack-cli/serve": "^2.0.1", + "colorette": "^2.0.14", + "commander": "^9.4.1", + "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", + "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz", + "integrity": "sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.9.0.tgz", + "integrity": "sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + }, + "dependencies": { + "@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true + }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", + "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", + "dev": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@popperjs/core": { + "version": "2.11.6", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", + "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==", + "dev": true, + "peer": true + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dev": true, + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "@types/eslint": { + "version": "8.4.6", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz", + "integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "@types/express": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", + "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.31", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", + "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "dev": true + }, + "@types/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true + }, + "@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true + }, + "@types/node": { + "version": "18.7.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.19.tgz", + "integrity": "sha512-Sq1itGUKUX1ap7GgZlrzdBydjbsJL/NSQt/4wkAxUJ7/OS5c2WkoN6WSpWc2Yc5wtKMZOUA0VCs/j2XJadN3HA==", + "dev": true + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@wasm-tool/wasm-pack-plugin": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@wasm-tool/wasm-pack-plugin/-/wasm-pack-plugin-1.6.0.tgz", + "integrity": "sha512-Iax4nEgIvVCZqrmuseJm7ln/muWpg7uT5fXMAT0crYo+k5JTuZE58DJvBQoeIAegA3IM9cZgfkcZjAOUCPsT1g==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "command-exists": "^1.2.7", + "watchpack": "^2.1.1", + "which": "^2.0.2" + } + }, + "@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webpack-cli/configtest": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.0.1.tgz", + "integrity": "sha512-njsdJXJSiS2iNbQVS0eT8A/KPnmyH4pv1APj2K0d1wrZcBLw+yppxOy4CGqa0OxDJkzfL/XELDhD8rocnIwB5A==", + "dev": true, + "requires": {} + }, + "@webpack-cli/info": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.1.tgz", + "integrity": "sha512-fE1UEWTwsAxRhrJNikE7v4EotYflkEhBL7EbajfkPlf6E37/2QshOy/D48Mw8G5XMFlQtS6YV42vtbG9zBpIQA==", + "dev": true, + "requires": {} + }, + "@webpack-cli/serve": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.1.tgz", + "integrity": "sha512-0G7tNyS+yW8TdgHwZKlDWYXFA6OJQnoLCQvYKkQP0Q2X205PSQ6RNUj0M+1OB/9gRQaUZ/ccYfaxd0nhaWKfjw==", + "dev": true, + "requires": {} + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true + }, + "acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "requires": {} + }, + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + } + } + }, + "bonjour-service": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz", + "integrity": "sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==", + "dev": true, + "requires": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "bootstrap": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz", + "integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==", + "dev": true, + "requires": {} + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "caniuse-lite": { + "version": "1.0.30001410", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001410.tgz", + "integrity": "sha512-QoblBnuE+rG0lc3Ur9ltP5q47lbguipa/ncNMyyGuqPk44FxbScWAeEO+k5fSQ8WekdAK4mWqNs1rADDAiN5xQ==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "clean-css": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", + "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + } + }, + "clean-webpack-plugin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz", + "integrity": "sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==", + "dev": true, + "requires": { + "del": "^4.1.1" + } + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, + "command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", + "dev": true + }, + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "dev": true, + "requires": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + } + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "css-loader": { + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.3.tgz", + "integrity": "sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==", + "dev": true, + "requires": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.19", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.8" + } + }, + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "dependencies": { + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true + } + } + } + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", + "dev": true + }, + "dns-packet": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", + "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "dev": true, + "requires": { + "@leichtgewicht/ip-codec": "^2.0.1" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "requires": { + "utila": "~0.4" + } + }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.4.260", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.260.tgz", + "integrity": "sha512-1GxPM2Bdz1AjuNjho9/TqJfxM7KZ7R8s4vA5cbbIoVacQXfvZlV+d7Y1lu4BhGzEBfjjhakr3NXKqN0PxPXIsg==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true + }, + "enhanced-resolve": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + }, + "envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true + }, + "es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "dev": true, + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + } + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true + }, + "fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "globby": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", + "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "dev": true, + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "dev": true + }, + "html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dev": true, + "requires": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + } + }, + "html-webpack-plugin": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", + "dev": true, + "requires": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + } + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "dev": true + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dev": true, + "requires": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "requires": {} + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true + }, + "ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "requires": { + "is-path-inside": "^2.1.0" + } + }, + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "requires": { + "path-is-inside": "^1.0.2" + } + }, + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "json5": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz", + "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true + }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true + }, + "memfs": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.7.tgz", + "integrity": "sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw==", + "dev": true, + "requires": { + "fs-monkey": "^1.0.3" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "monaco-editor": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz", + "integrity": "sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ==" + }, + "monaco-editor-webpack-plugin": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-7.0.1.tgz", + "integrity": "sha512-M8qIqizltrPlIbrb73cZdTWfU9sIsUVFvAZkL3KGjAHmVWEJ0hZKa/uad14JuOckc0GwnCaoGHvMoYtJjVyCzw==", + "dev": true, + "requires": { + "loader-utils": "^2.0.2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "requires": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + } + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true + }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + }, + "p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "requires": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "postcss": { + "version": "8.4.19", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz", + "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==", + "dev": true, + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "requires": {} + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "prettier": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz", + "integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==", + "dev": true + }, + "pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dev": true, + "requires": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "requires": { + "resolve": "^1.20.0" + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true + }, + "renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dev": true, + "requires": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "selfsigned": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "dev": true, + "requires": { + "node-forge": "^1" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + }, + "sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "style-loader": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", + "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", + "dev": true, + "requires": {} + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true + }, + "terser": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", + "integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", + "dev": true, + "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", + "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.14", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "terser": "^5.14.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", + "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true + }, + "watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webpack": { + "version": "5.75.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", + "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "webpack-cli": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz", + "integrity": "sha512-S3KVAyfwUqr0Mo/ur3NzIp6jnerNpo7GUO6so51mxLi1spqsA17YcMXy0WOIJtBSnj748lthxC6XLbNKh/ZC+A==", + "dev": true, + "requires": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^2.0.1", + "@webpack-cli/info": "^2.0.1", + "@webpack-cli/serve": "^2.0.1", + "colorette": "^2.0.14", + "commander": "^9.4.1", + "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^5.7.3" + }, + "dependencies": { + "commander": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", + "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", + "dev": true + } + } + }, + "webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "dev": true, + "requires": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + } + }, + "webpack-dev-server": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz", + "integrity": "sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==", + "dev": true, + "requires": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "ws": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.9.0.tgz", + "integrity": "sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg==", + "dev": true, + "requires": {} + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } +} diff --git a/javascript-engine/external/boa/package.json b/javascript-engine/external/boa/package.json new file mode 100644 index 0000000..f225587 --- /dev/null +++ b/javascript-engine/external/boa/package.json @@ -0,0 +1,26 @@ +{ + "scripts": { + "build": "webpack", + "serve": "webpack-dev-server", + "build:prod": "webpack --mode=production" + }, + "devDependencies": { + "@wasm-tool/wasm-pack-plugin": "^1.6.0", + "bootstrap": "^5.2.3", + "clean-webpack-plugin": "^4.0.0", + "copy-webpack-plugin": "^11.0.0", + "css-loader": "^6.7.3", + "file-loader": "^6.2.0", + "html-webpack-plugin": "^5.5.0", + "monaco-editor-webpack-plugin": "^7.0.1", + "prettier": "^2.8.3", + "style-loader": "^3.3.1", + "terser-webpack-plugin": "^5.3.6", + "webpack": "^5.75.0", + "webpack-cli": "^5.0.1", + "webpack-dev-server": "^4.11.1" + }, + "dependencies": { + "monaco-editor": "^0.34.1" + } +} diff --git a/javascript-engine/external/boa/test_ignore.toml b/javascript-engine/external/boa/test_ignore.toml new file mode 100644 index 0000000..bf74802 --- /dev/null +++ b/javascript-engine/external/boa/test_ignore.toml @@ -0,0 +1,45 @@ +# Not implemented yet: +flags = ["module"] + +features = [ + # Non-implemented features: + "json-modules", + "SharedArrayBuffer", + "resizable-arraybuffer", + "Temporal", + "tail-call-optimization", + "ShadowRealm", + "FinalizationRegistry", + "Atomics", + "dynamic_import", + "decorators", + "array-grouping", + "IsHTMLDDA", + + # Non-implemented Intl features + "intl-normative-optional", + "Intl.DurationFormat", + "Intl.DisplayNames", + "Intl.RelativeTimeFormat", + "Intl.Segmenter", + + # Stage 3 proposals + + # https://github.com/tc39/proposal-symbols-as-weakmap-keys + "symbols-as-weakmap-keys", + # https://github.com/tc39/proposal-intl-locale-info + "Intl.Locale-info", + # https://github.com/tc39/proposal-intl-enumeration + "Intl-enumeration", + + # Non-standard + "caller", + + # RegExp tests that check individual codepoints. + # They are not useful considering the cpu time they waste. + "regexp-unicode-property-escapes", +] + +# RegExp tests that check individual codepoints. +# They are not useful considering the cpu time they waste. +tests = ["CharacterClassEscapes", "NumberFormat"] diff --git a/javascript-engine/external/boa/webpack.config.js b/javascript-engine/external/boa/webpack.config.js new file mode 100644 index 0000000..0b7c2c5 --- /dev/null +++ b/javascript-engine/external/boa/webpack.config.js @@ -0,0 +1,83 @@ +const path = require("path"); +const fs = require("fs"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); +const { CleanWebpackPlugin } = require("clean-webpack-plugin"); +const CopyWebpackPlugin = require("copy-webpack-plugin"); +const webpack = require("webpack"); +const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin"); +const TerserPlugin = require("terser-webpack-plugin"); +const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin"); + +const outdir = path.resolve(__dirname, "./dist"); + +if (fs.existsSync(outdir)) { + fs.rmSync(outdir, { recursive: true }); +} + +module.exports = { + experiments: { + asyncWebAssembly: true, + }, + entry: { + app: "./index.js", + }, + output: { + path: outdir, + filename: "[name].js", + }, + plugins: [ + new MonacoWebpackPlugin({ + languages: ["javascript", "typescript"], + features: [ + "browser", + "find", + "inlayHints", + "documentSymbols", + "inlineCompletions", + "parameterHints", + "snippet", + "suggest", + "wordHighlighter", + "codelens", + "hover", + "bracketMatching", + ], + }), + new CleanWebpackPlugin(), + new HtmlWebpackPlugin({ template: "index.html" }), + new WasmPackPlugin({ + crateDirectory: path.resolve(__dirname, "./boa_wasm/"), + outDir: path.resolve(__dirname, "./boa_wasm/pkg/"), + forceMode: "production", + }), + new CopyWebpackPlugin({ + patterns: [ + { + from: "./assets/*", + to: ".", + }, + { + from: "./node_modules/bootstrap/dist/css/bootstrap.min.css", + to: "./assets", + }, + ], + }), + ], + module: { + rules: [ + { + test: /\.css$/, + use: ["style-loader", "css-loader"], + }, + { + test: /\.ttf$/, + use: ["file-loader"], + }, + ], + }, + optimization: { + minimize: true, + minimizer: [new TerserPlugin()], + }, + mode: "development", +}; diff --git a/javascript-engine/external/getrandom/.cargo/config b/javascript-engine/external/getrandom/.cargo/config new file mode 100644 index 0000000..be3061a --- /dev/null +++ b/javascript-engine/external/getrandom/.cargo/config @@ -0,0 +1,9 @@ +# Allow normal use of "cargo run" and "cargo test" on these wasm32 platforms. +[target.wasm32-unknown-unknown] +runner = 'wasm-bindgen-test-runner' +[target.wasm32-wasi] +runner = 'wasmtime' + +# Just run on node by default (that's where emscripten is tested) +[target.'cfg(target_os = "emscripten")'] +runner = 'node' diff --git a/javascript-engine/external/getrandom/.clippy.toml b/javascript-engine/external/getrandom/.clippy.toml new file mode 100644 index 0000000..992016c --- /dev/null +++ b/javascript-engine/external/getrandom/.clippy.toml @@ -0,0 +1 @@ +msrv = "1.36" diff --git a/javascript-engine/external/getrandom/.github/workflows/tests.yml b/javascript-engine/external/getrandom/.github/workflows/tests.yml new file mode 100644 index 0000000..76f61de --- /dev/null +++ b/javascript-engine/external/getrandom/.github/workflows/tests.yml @@ -0,0 +1,337 @@ +name: Tests + +on: + push: + branches: master + pull_request: + branches: master + schedule: + - cron: "0 12 * * 1" + +env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Dwarnings" + +jobs: + check-doc: + name: Docs, deadlinks, minimal dependencies + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly # Needed for -Z minimal-versions and doc_cfg + - name: Install precompiled cargo-deadlinks + run: | + VERSION=0.8.1 + URL="https://github.com/deadlinks/cargo-deadlinks/releases/download/${VERSION}/cargo-deadlinks-linux" + wget -O ~/.cargo/bin/cargo-deadlinks $URL + chmod +x ~/.cargo/bin/cargo-deadlinks + cargo deadlinks --version + - uses: Swatinem/rust-cache@v2 + - name: Generate Docs + env: + RUSTDOCFLAGS: --cfg docsrs + run: cargo deadlinks -- --features=custom,std + - run: | + cargo generate-lockfile -Z minimal-versions + cargo test --features=custom,std + + # TODO: add aarch64-based macOS runner when it's supported by Github Actions + main-tests: + name: Tier 1 Test + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-22.04, windows-2022] + toolchain: [nightly, beta, stable, 1.36] + # Only Test macOS on stable to reduce macOS CI jobs + include: + - os: macos-12 + toolchain: stable + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.toolchain }} + - uses: Swatinem/rust-cache@v2 + - run: cargo test + - run: cargo test --features=std + - run: cargo test --features=custom # custom should do nothing here + - if: ${{ matrix.toolchain == 'nightly' }} + run: cargo test --benches + + linux-tests: + name: Linux Test + runs-on: ubuntu-22.04 + strategy: + matrix: + target: [ + x86_64-unknown-linux-musl, + i686-unknown-linux-gnu, + i686-unknown-linux-musl, + ] + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + - name: Install multilib + run: sudo apt-get install gcc-multilib + - uses: Swatinem/rust-cache@v2 + - run: cargo test --target=${{ matrix.target }} --features=std + + ios-tests: + name: iOS Simulator Test + runs-on: macos-12 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + targets: x86_64-apple-ios + - name: Install precompiled cargo-dinghy + run: | + VERSION=0.6.2 + URL="https://github.com/sonos/dinghy/releases/download/${VERSION}/cargo-dinghy-macos-${VERSION}.tgz" + wget -O - $URL | tar -xz --strip-components=1 -C ~/.cargo/bin + cargo dinghy --version + - name: Setup Simulator + # Use the first installed iOS runtime and the first (i.e. oldest) supported iPhone device. + run: | + RUNTIME=$(xcrun simctl list runtimes --json | jq '.runtimes | map(select(.name | contains("iOS"))) | .[0]') + RUNTIME_ID=$(echo $RUNTIME | jq -r '.identifier') + echo "Using runtime:" $RUNTIME_ID + DEVICE_ID=$(echo $RUNTIME | jq -r '.supportedDeviceTypes | map(select(.productFamily == "iPhone")) | .[0].identifier') + echo "Using device:" $DEVICE_ID + SIM_ID=$(xcrun simctl create Test-iPhone $DEVICE_ID $RUNTIME_ID) + echo "Created simulator:" $SIM_ID + xcrun simctl boot $SIM_ID + echo "device=$SIM_ID" >> $GITHUB_ENV + - uses: Swatinem/rust-cache@v2 + - name: Run tests + run: cargo dinghy -d ${{ env.device }} test + + windows-tests: + name: Windows Test + runs-on: windows-2022 + strategy: + matrix: + toolchain: [ + stable-x86_64-gnu, + stable-i686-gnu, + stable-i686-msvc, + ] + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.toolchain }} + - uses: Swatinem/rust-cache@v2 + - run: cargo test --features=std + + cross-tests: + name: Cross Test + runs-on: ubuntu-22.04 + strategy: + matrix: + target: [ + aarch64-unknown-linux-gnu, + aarch64-linux-android, + mips-unknown-linux-gnu, + wasm32-unknown-emscripten, + ] + steps: + - uses: actions/checkout@v3 + - name: Install precompiled cross + run: | + VERSION=v0.2.4 + URL=https://github.com/cross-rs/cross/releases/download/${VERSION}/cross-x86_64-unknown-linux-gnu.tar.gz + wget -O - $URL | tar -xz -C ~/.cargo/bin + cross --version + - uses: Swatinem/rust-cache@v2 + - name: Test + run: cross test --no-fail-fast --target=${{ matrix.target }} --features=std + + macos-link: + name: macOS ARM64 Build/Link + runs-on: macos-12 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly + with: + targets: aarch64-apple-darwin, aarch64-apple-ios + components: rust-src + - uses: Swatinem/rust-cache@v2 + - run: cargo test --no-run --target=aarch64-apple-darwin --features=std + - run: cargo test --no-run --target=aarch64-apple-ios --features=std + - run: cargo test --no-run --target=aarch64-apple-watchos-sim -Zbuild-std --features=std + + cross-link: + name: Cross Build/Link + runs-on: ubuntu-22.04 + strategy: + matrix: + target: [ + sparcv9-sun-solaris, + # These jobs currently result in a linking error: + # https://github.com/rust-random/getrandom/actions/runs/3835874649/jobs/6529484986 + # x86_64-unknown-illumos, + # x86_64-unknown-freebsd, + # x86_64-unknown-netbsd, + ] + steps: + - uses: actions/checkout@v3 + - name: Install precompiled cross + run: | + VERSION=v0.2.4 + URL=https://github.com/cross-rs/cross/releases/download/${VERSION}/cross-x86_64-unknown-linux-gnu.tar.gz + wget -O - $URL | tar -xz -C ~/.cargo/bin + cross --version + - uses: Swatinem/rust-cache@v2 + - name: Build Tests + run: cross test --no-run --target=${{ matrix.target }} --features=std + + web-tests: + name: Web Test + strategy: + fail-fast: false + matrix: + include: + # Firefox isn't available on 22.04 yet, so we must use 20.04 + - os: ubuntu-20.04 + host: x86_64-unknown-linux-musl + - os: windows-2022 + host: x86_64-pc-windows-msvc + # Re-enable when Safari tests start working + # - os: macos-12 + # host: x86_64-apple-darwin + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + - run: choco install wget + if: runner.os == 'Windows' + - name: Install precompiled wasm-pack + shell: bash + run: | + VERSION=v0.10.3 + URL=https://github.com/rustwasm/wasm-pack/releases/download/${VERSION}/wasm-pack-${VERSION}-${{ matrix.host }}.tar.gz + wget -O - $URL | tar -xz --strip-components=1 -C ~/.cargo/bin + wasm-pack --version + - uses: Swatinem/rust-cache@v2 + - name: Test (Node) + run: wasm-pack test --node --features=js + - name: Test (Firefox) + run: wasm-pack test --headless --firefox --features=js,test-in-browser + - name: Test (Chrome) + run: wasm-pack test --headless --chrome --features=js,test-in-browser + - name: Test (Edge) + if: runner.os == 'Windows' + run: wasm-pack test --headless --chrome --chromedriver $Env:EDGEWEBDRIVER\msedgedriver.exe --features=js,test-in-browser + # Safari tests are broken: https://github.com/rustwasm/wasm-bindgen/issues/3004 + # - name: Test (Safari) + # if: runner.os == 'macOS' + # run: wasm-pack test --headless --safari --features=js,test-in-browser + - name: Test (custom getrandom) + run: wasm-pack test --node --features=custom + + wasm64-tests: + name: wasm64 Build/Link + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly # Need to build libstd + with: + components: rust-src + - uses: Swatinem/rust-cache@v2 + - name: Build and Link tests (build-std) + # This target is Tier 3, so we have to build libstd ourselves. + # We currently cannot run these tests because wasm-bindgen-test-runner + # does not yet support memory64. + run: cargo test --no-run -Z build-std=std,panic_abort --target=wasm64-unknown-unknown --features=js + + wasi-tests: + name: WASI Test + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-wasi + - name: Install precompiled wasmtime + run: | + VERSION=v2.0.0 + URL=https://github.com/bytecodealliance/wasmtime/releases/download/${VERSION}/wasmtime-${VERSION}-x86_64-linux.tar.xz + wget -O - $URL | tar -xJ --strip-components=1 -C ~/.cargo/bin + wasmtime --version + - uses: Swatinem/rust-cache@v2 + - run: cargo test --target wasm32-wasi + + build-tier2: + name: Tier 2 Build + runs-on: ubuntu-22.04 + strategy: + matrix: + target: [ + x86_64-fuchsia, + x86_64-unknown-redox, + x86_64-fortanix-unknown-sgx, + ] + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + - uses: Swatinem/rust-cache@v2 + - name: Build + run: cargo build --target=${{ matrix.target }} --features=std + + build-tier3: + name: Tier 3 Build + runs-on: ubuntu-22.04 + strategy: + matrix: + # Supported tier 3 targets without libstd support + target: [ + x86_64-unknown-hermit, + x86_64-wrs-vxworks, + aarch64-kmc-solid_asp3, + armv6k-nintendo-3ds, + riscv32imc-esp-espidf, + aarch64-unknown-nto-qnx710, + # `std` support still in progress. Can be moved up with the other + # apple targets after https://github.com/rust-lang/rust/pull/103503 + aarch64-apple-tvos, + ] + include: + # Supported tier 3 targets without libstd support + - target: x86_64-unknown-openbsd + features: ["std"] + - target: x86_64-unknown-dragonfly + features: ["std"] + - target: x86_64-unknown-haiku + features: ["std"] + # Unsupported tier 3 targets to test the rdrand feature + - target: x86_64-unknown-uefi + features: ["rdrand"] + - target: x86_64-unknown-l4re-uclibc + features: ["rdrand"] + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly # Required to build libcore + with: + components: rust-src + - uses: Swatinem/rust-cache@v2 + - run: cargo build -Z build-std=${{ contains(matrix.features, 'std') && 'std' || 'core'}} --target=${{ matrix.target }} --features="${{ join(matrix.features, ',') }}" + + clippy-fmt: + name: Clippy + rustfmt + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v1 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + - uses: Swatinem/rust-cache@v2 + - name: clippy + run: cargo clippy --all --features=custom,std + - name: fmt + run: cargo fmt --all -- --check diff --git a/javascript-engine/external/getrandom/.gitignore b/javascript-engine/external/getrandom/.gitignore new file mode 100644 index 0000000..435eeee --- /dev/null +++ b/javascript-engine/external/getrandom/.gitignore @@ -0,0 +1,6 @@ +/target +**/*.rs.bk +Cargo.lock +*.ts +*.js +*.wasm diff --git a/javascript-engine/external/getrandom/CHANGELOG.md b/javascript-engine/external/getrandom/CHANGELOG.md new file mode 100644 index 0000000..cd3f819 --- /dev/null +++ b/javascript-engine/external/getrandom/CHANGELOG.md @@ -0,0 +1,369 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased +### Added +- `getrandom_uninit` [#291] + +### Breaking Changes +- Update MSRV to 1.36 [#291] + +[#291]: https://github.com/rust-random/getrandom/pull/291 + +## [0.2.8] - 2022-10-20 +### Changed +- The [Web Cryptography API] will now be preferred on `wasm32-unknown-unknown` + when using the `"js"` feature, even on Node.js [#284] [#295] + +### Added +- Added benchmarks to track buffer initialization cost [#272] + +### Fixed +- Use `$crate` in `register_custom_getrandom!` [#270] + +### Documentation +- Add information about enabling `"js"` feature [#280] +- Fix link to `wasm-bindgen` [#278] +- Document the varied implementations for underlying randomness sources [#276] + +[Web Cryptography API]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API +[#284]: https://github.com/rust-random/getrandom/pull/284 +[#295]: https://github.com/rust-random/getrandom/pull/295 +[#272]: https://github.com/rust-random/getrandom/pull/272 +[#270]: https://github.com/rust-random/getrandom/pull/270 +[#280]: https://github.com/rust-random/getrandom/pull/280 +[#278]: https://github.com/rust-random/getrandom/pull/278 +[#276]: https://github.com/rust-random/getrandom/pull/276 + +## [0.2.7] - 2022-06-14 +### Changed +- Update `wasi` dependency to `0.11` [#253] + +### Fixed +- Use `AtomicPtr` instead of `AtomicUsize` for Strict Provenance compatibility. [#263] + +### Documentation +- Add comments explaining use of fallback mechanisms [#257] [#260] + +[#263]: https://github.com/rust-random/getrandom/pull/263 +[#260]: https://github.com/rust-random/getrandom/pull/260 +[#253]: https://github.com/rust-random/getrandom/pull/253 +[#257]: https://github.com/rust-random/getrandom/pull/257 + +## [0.2.6] - 2022-03-28 +### Added +- Nintendo 3DS (`armv6k-nintendo-3ds`) support [#248] + +### Changed +- Retry `open` when interrupted [#252] + +[#248]: https://github.com/rust-random/getrandom/pull/248 +[#252]: https://github.com/rust-random/getrandom/pull/252 + +## [0.2.5] - 2022-02-22 +### Added +- ESP-IDF targets (`*‑espidf`) support [#245] + +### Fixed +- Webpack warning caused by dynamic require [#234] +- Error checking on iOS for `SecRandomCopyBytes` [#244] + +[#234]: https://github.com/rust-random/getrandom/pull/234 +[#244]: https://github.com/rust-random/getrandom/pull/244 +[#245]: https://github.com/rust-random/getrandom/pull/245 + +## [0.2.4] - 2021-12-13 +### Changed +- Use explicit imports in the `js` backend [#220] +- Use `/dev/urandom` on Redox instead of `rand:` [#222] +- Use `NonZeroU32::new_unchecked` to convert wasi error [#233] + +### Added +- SOLID targets (`*-kmc-solid_*`) support [#235] +- Limited Hermit (`x86_64-unknown-hermit`) support [#236] + +[#220]: https://github.com/rust-random/getrandom/pull/220 +[#222]: https://github.com/rust-random/getrandom/pull/222 +[#233]: https://github.com/rust-random/getrandom/pull/233 +[#235]: https://github.com/rust-random/getrandom/pull/235 +[#236]: https://github.com/rust-random/getrandom/pull/236 + +## [0.2.3] - 2021-04-10 +### Changed +- Replace build.rs with link attributes. [#205] +- Add support for getrandom syscall on DragonFly BSD. [#210] +- Improve Node.js detection. [#215] + +[#205]: https://github.com/rust-random/getrandom/pull/205 +[#210]: https://github.com/rust-random/getrandom/pull/210 +[#215]: https://github.com/rust-random/getrandom/pull/215 + +## [0.2.2] - 2021-01-19 +### Changed +- Forward `rustc-dep-of-std` to dependencies. [#198] +- Highlight feature-dependent functionality in documentation using the `doc_cfg` feature. [#200] + +[#198]: https://github.com/rust-random/getrandom/pull/198 +[#200]: https://github.com/rust-random/getrandom/pull/200 + +## [0.2.1] - 2021-01-03 +### Changed +- Update `cfg-if` to v1.0. [#166] +- Update `wasi` to v0.10. [#167] + +### Fixed +- Multithreaded WASM support. [#165] + +### Removed +- Windows XP support. [#177] +- Direct `stdweb` support. [#178] +- CloudABI support. [#184] + +[#165]: https://github.com/rust-random/getrandom/pull/165 +[#166]: https://github.com/rust-random/getrandom/pull/166 +[#167]: https://github.com/rust-random/getrandom/pull/167 +[#177]: https://github.com/rust-random/getrandom/pull/177 +[#178]: https://github.com/rust-random/getrandom/pull/178 +[#184]: https://github.com/rust-random/getrandom/pull/184 + +## [0.2.0] - 2020-09-10 +### Features for using getrandom on unsupported targets + +The following (off by default) Cargo features have been added: +- `"rdrand"` - use the RDRAND instruction on `no_std` `x86`/`x86_64` targets [#133] +- `"js"` - use JavaScript calls on `wasm32-unknown-unknown` [#149] + - Replaces the `stdweb` and `wasm-bindgen` features (which are removed) +- `"custom"` - allows a user to specify a custom implementation [#109] + +### Breaking Changes +- Unsupported targets no longer compile [#107] +- Change/Add `Error` constants [#120] +- Only impl `std` traits when the `"std"` Cargo feature is specified [#106] +- Remove official support for Hermit, L4Re, and UEFI [#133] +- Remove optional `"log"` dependency [#131] +- Update minimum supported Linux kernel to 2.6.32 [#153] +- Update MSRV to 1.34 [#159] + +[#106]: https://github.com/rust-random/getrandom/pull/106 +[#107]: https://github.com/rust-random/getrandom/pull/107 +[#109]: https://github.com/rust-random/getrandom/pull/109 +[#120]: https://github.com/rust-random/getrandom/pull/120 +[#131]: https://github.com/rust-random/getrandom/pull/131 +[#133]: https://github.com/rust-random/getrandom/pull/133 +[#149]: https://github.com/rust-random/getrandom/pull/149 +[#153]: https://github.com/rust-random/getrandom/pull/153 +[#159]: https://github.com/rust-random/getrandom/pull/159 + +## [0.1.16] - 2020-12-31 +### Changed +- Update `cfg-if` to v1.0. [#173] +- Implement `std::error::Error` for the `Error` type on additional targets. [#169] + +### Fixed +- Multithreaded WASM support. [#171] + +[#173]: https://github.com/rust-random/getrandom/pull/173 +[#171]: https://github.com/rust-random/getrandom/pull/171 +[#169]: https://github.com/rust-random/getrandom/pull/169 + +## [0.1.15] - 2020-09-10 +### Changed +- Added support for Internet Explorer 11 [#139] +- Fix Webpack require warning with `wasm-bindgen` [#137] + +[#137]: https://github.com/rust-random/getrandom/pull/137 +[#139]: https://github.com/rust-random/getrandom/pull/139 + +## [0.1.14] - 2020-01-07 +### Changed +- Remove use of spin-locks in the `use_file` module. [#125] +- Update `wasi` to v0.9. [#126] +- Do not read errno value on DragonFlyBSD to fix compilation failure. [#129] + +[#125]: https://github.com/rust-random/getrandom/pull/125 +[#126]: https://github.com/rust-random/getrandom/pull/126 +[#129]: https://github.com/rust-random/getrandom/pull/129 + +## [0.1.13] - 2019-08-25 +### Added +- VxWorks targets support. [#86] + +### Changed +- If zero-length slice is passed to the `getrandom` function, always return +`Ok(())` immediately without doing any calls to the underlying operating +system. [#104] +- Use the `kern.arandom` sysctl on NetBSD. [#115] + +### Fixed +- Bump `cfg-if` minimum version from 0.1.0 to 0.1.2. [#112] +- Typos and bad doc links. [#117] + +[#86]: https://github.com/rust-random/getrandom/pull/86 +[#104]: https://github.com/rust-random/getrandom/pull/104 +[#112]: https://github.com/rust-random/getrandom/pull/112 +[#115]: https://github.com/rust-random/getrandom/pull/115 +[#117]: https://github.com/rust-random/getrandom/pull/117 + +## [0.1.12] - 2019-08-18 +### Changed +- Update wasi dependency from v0.5 to v0.7. [#100] + +[#100]: https://github.com/rust-random/getrandom/pull/100 + +## [0.1.11] - 2019-08-25 +### Fixed +- Implement `std`-dependent traits for selected targets even if `std` +feature is disabled. (backward compatibility with v0.1.8) [#96] + +[#96]: https://github.com/rust-random/getrandom/pull/96 + +## [0.1.10] - 2019-08-18 [YANKED] +### Changed +- Use the dummy implementation on `wasm32-unknown-unknown` even with the +disabled `dummy` feature. [#90] + +### Fixed +- Fix CSP error for `wasm-bindgen`. [#92] + +[#90]: https://github.com/rust-random/getrandom/pull/90 +[#92]: https://github.com/rust-random/getrandom/pull/92 + +## [0.1.9] - 2019-08-14 [YANKED] +### Changed +- Remove `std` dependency for opening and reading files. [#58] +- Use `wasi` instead of `libc` on WASI target. [#64] +- By default emit a compile-time error when built for an unsupported target. +This behaviour can be disabled by using the `dummy` feature. [#71] + +### Added +- Add support for UWP targets. [#69] +- Add unstable `rustc-dep-of-std` feature. [#78] + +[#58]: https://github.com/rust-random/getrandom/pull/58 +[#64]: https://github.com/rust-random/getrandom/pull/64 +[#69]: https://github.com/rust-random/getrandom/pull/69 +[#71]: https://github.com/rust-random/getrandom/pull/71 +[#78]: https://github.com/rust-random/getrandom/pull/78 + +## [0.1.8] - 2019-07-29 +### Changed +- Explicitly specify types to arguments of 'libc::syscall'. [#74] + +[#74]: https://github.com/rust-random/getrandom/pull/74 + +## [0.1.7] - 2019-07-29 +### Added +- Support for hermit and l4re. [#61] +- `Error::raw_os_error` method, `Error::INTERNAL_START` and +`Error::CUSTOM_START` constants. Use `libc` for retrieving OS error descriptions. [#54] + +### Changed +- Remove `lazy_static` dependency and use custom structures for lock-free +initialization. [#51] [#52] +- Try `getrandom()` first on FreeBSD. [#57] + +### Removed +- Bitrig support. [#56] + +### Deprecated +- `Error::UNKNOWN`, `Error::UNAVAILABLE`. [#54] + +[#51]: https://github.com/rust-random/getrandom/pull/51 +[#52]: https://github.com/rust-random/getrandom/pull/52 +[#54]: https://github.com/rust-random/getrandom/pull/54 +[#56]: https://github.com/rust-random/getrandom/pull/56 +[#57]: https://github.com/rust-random/getrandom/pull/57 +[#61]: https://github.com/rust-random/getrandom/pull/61 + +## [0.1.6] - 2019-06-30 +### Changed +- Minor change of RDRAND AMD bug handling. [#48] + +[#48]: https://github.com/rust-random/getrandom/pull/48 + +## [0.1.5] - 2019-06-29 +### Fixed +- Use shared `File` instead of shared file descriptor. [#44] +- Workaround for RDRAND hardware bug present on some AMD CPUs. [#43] + +### Changed +- Try `getentropy` and then fallback to `/dev/random` on macOS. [#38] + +[#38]: https://github.com/rust-random/getrandom/issues/38 +[#43]: https://github.com/rust-random/getrandom/pull/43 +[#44]: https://github.com/rust-random/getrandom/issues/44 + +## [0.1.4] - 2019-06-28 +### Added +- Add support for `x86_64-unknown-uefi` target by using RDRAND with CPUID +feature detection. [#30] + +### Fixed +- Fix long buffer issues on Windows and Linux. [#31] [#32] +- Check `EPERM` in addition to `ENOSYS` on Linux. [#37] + +### Changed +- Improve efficiency by sharing file descriptor across threads. [#13] +- Remove `cloudabi`, `winapi`, and `fuchsia-cprng` dependencies. [#40] +- Improve RDRAND implementation. [#24] +- Don't block during syscall detection on Linux. [#26] +- Increase consistency with libc implementation on FreeBSD. [#36] +- Apply `rustfmt`. [#39] + +[#30]: https://github.com/rust-random/getrandom/pull/30 +[#13]: https://github.com/rust-random/getrandom/issues/13 +[#40]: https://github.com/rust-random/getrandom/pull/40 +[#26]: https://github.com/rust-random/getrandom/pull/26 +[#24]: https://github.com/rust-random/getrandom/pull/24 +[#39]: https://github.com/rust-random/getrandom/pull/39 +[#36]: https://github.com/rust-random/getrandom/pull/36 +[#31]: https://github.com/rust-random/getrandom/issues/31 +[#32]: https://github.com/rust-random/getrandom/issues/32 +[#37]: https://github.com/rust-random/getrandom/issues/37 + +## [0.1.3] - 2019-05-15 +- Update for `wasm32-unknown-wasi` being renamed to `wasm32-wasi`, and for + WASI being categorized as an OS. + +## [0.1.2] - 2019-04-06 +- Add support for `wasm32-unknown-wasi` target. + +## [0.1.1] - 2019-04-05 +- Enable std functionality for CloudABI by default. + +## [0.1.0] - 2019-03-23 +Publish initial implementation. + +## [0.0.0] - 2019-01-19 +Publish an empty template library. + +[0.2.8]: https://github.com/rust-random/getrandom/compare/v0.2.7...v0.2.8 +[0.2.7]: https://github.com/rust-random/getrandom/compare/v0.2.6...v0.2.7 +[0.2.6]: https://github.com/rust-random/getrandom/compare/v0.2.5...v0.2.6 +[0.2.5]: https://github.com/rust-random/getrandom/compare/v0.2.4...v0.2.5 +[0.2.4]: https://github.com/rust-random/getrandom/compare/v0.2.3...v0.2.4 +[0.2.3]: https://github.com/rust-random/getrandom/compare/v0.2.2...v0.2.3 +[0.2.2]: https://github.com/rust-random/getrandom/compare/v0.2.1...v0.2.2 +[0.2.1]: https://github.com/rust-random/getrandom/compare/v0.2.0...v0.2.1 +[0.2.0]: https://github.com/rust-random/getrandom/compare/v0.1.15...v0.2.0 +[0.1.16]: https://github.com/rust-random/getrandom/compare/v0.1.15...v0.1.16 +[0.1.15]: https://github.com/rust-random/getrandom/compare/v0.1.14...v0.1.15 +[0.1.14]: https://github.com/rust-random/getrandom/compare/v0.1.13...v0.1.14 +[0.1.13]: https://github.com/rust-random/getrandom/compare/v0.1.12...v0.1.13 +[0.1.12]: https://github.com/rust-random/getrandom/compare/v0.1.11...v0.1.12 +[0.1.11]: https://github.com/rust-random/getrandom/compare/v0.1.10...v0.1.11 +[0.1.10]: https://github.com/rust-random/getrandom/compare/v0.1.9...v0.1.10 +[0.1.9]: https://github.com/rust-random/getrandom/compare/v0.1.8...v0.1.9 +[0.1.8]: https://github.com/rust-random/getrandom/compare/v0.1.7...v0.1.8 +[0.1.7]: https://github.com/rust-random/getrandom/compare/v0.1.6...v0.1.7 +[0.1.6]: https://github.com/rust-random/getrandom/compare/v0.1.5...v0.1.6 +[0.1.5]: https://github.com/rust-random/getrandom/compare/v0.1.4...v0.1.5 +[0.1.4]: https://github.com/rust-random/getrandom/compare/v0.1.3...v0.1.4 +[0.1.3]: https://github.com/rust-random/getrandom/compare/v0.1.2...v0.1.3 +[0.1.2]: https://github.com/rust-random/getrandom/compare/v0.1.1...v0.1.2 +[0.1.1]: https://github.com/rust-random/getrandom/compare/v0.1.0...v0.1.1 +[0.1.0]: https://github.com/rust-random/getrandom/compare/v0.0.0...v0.1.0 +[0.0.0]: https://github.com/rust-random/getrandom/releases/tag/v0.0.0 diff --git a/javascript-engine/external/getrandom/Cargo.toml b/javascript-engine/external/getrandom/Cargo.toml new file mode 100644 index 0000000..57457f7 --- /dev/null +++ b/javascript-engine/external/getrandom/Cargo.toml @@ -0,0 +1,53 @@ +[package] +name = "getrandom" +version = "0.2.8" # Also update html_root_url in lib.rs when bumping this +edition = "2018" +authors = ["The Rand Project Developers"] +license = "MIT OR Apache-2.0" +description = "A small cross-platform library for retrieving random data from system source" +documentation = "https://docs.rs/getrandom" +repository = "https://github.com/rust-random/getrandom" +categories = ["os", "no-std"] +exclude = [".*"] + +[dependencies] +cfg-if = "1" + +# When built as part of libstd +compiler_builtins = { version = "0.1", optional = true } +core = { version = "1.0", optional = true, package = "rustc-std-workspace-core" } + +[target.'cfg(unix)'.dependencies] +libc = { version = "0.2.139", default-features = false } + +[target.'cfg(target_os = "wasi")'.dependencies] +wasi = { version = "0.11", default-features = false } + +[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dependencies] +wasm-bindgen = { version = "0.2.62", default-features = false, optional = true } +js-sys = { version = "0.3", optional = true } +[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dev-dependencies] +wasm-bindgen-test = "0.3.18" + +[features] +# Implement std-only traits for getrandom::Error +std = [] +# Feature to enable fallback RDRAND-based implementation on x86/x86_64 +rdrand = [] +# Feature to enable JavaScript bindings on wasm*-unknown-unknown +js = ["wasm-bindgen", "js-sys"] +# Feature to enable custom RNG implementations +custom = [] +# Unstable feature to support being a libstd dependency +rustc-dep-of-std = [ + "compiler_builtins", + "core", + "libc/rustc-dep-of-std", + "wasi/rustc-dep-of-std", +] +# Unstable/test-only feature to run wasm-bindgen tests in a browser +test-in-browser = [] + +[package.metadata.docs.rs] +features = ["std", "custom"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/javascript-engine/external/getrandom/LICENSE-APACHE b/javascript-engine/external/getrandom/LICENSE-APACHE new file mode 100644 index 0000000..17d7468 --- /dev/null +++ b/javascript-engine/external/getrandom/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/javascript-engine/external/getrandom/LICENSE-MIT b/javascript-engine/external/getrandom/LICENSE-MIT new file mode 100644 index 0000000..d93b5ba --- /dev/null +++ b/javascript-engine/external/getrandom/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright 2018 Developers of the Rand project +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/javascript-engine/external/getrandom/README.md b/javascript-engine/external/getrandom/README.md new file mode 100644 index 0000000..404b383 --- /dev/null +++ b/javascript-engine/external/getrandom/README.md @@ -0,0 +1,64 @@ +# getrandom + +[![Build Status]][GitHub Actions] [![Crate]][crates.io] [![Documentation]][docs.rs] [![Dependency Status]][deps.rs] [![Downloads]][crates.io] [![License]][LICENSE-MIT] + +[GitHub Actions]: https://github.com/rust-random/getrandom/actions?query=workflow:Tests+branch:master +[Build Status]: https://github.com/rust-random/getrandom/workflows/Tests/badge.svg?branch=master +[crates.io]: https://crates.io/crates/getrandom +[Crate]: https://img.shields.io/crates/v/getrandom +[docs.rs]: https://docs.rs/getrandom +[Documentation]: https://docs.rs/getrandom/badge.svg +[deps.rs]: https://deps.rs/repo/github/rust-random/getrandom +[Dependency Status]: https://deps.rs/repo/github/rust-random/getrandom/status.svg +[Downloads]: https://img.shields.io/crates/d/getrandom +[LICENSE-MIT]: https://raw.githubusercontent.com/rust-random/getrandom/master/LICENSE-MIT +[License]: https://img.shields.io/crates/l/getrandom + + +A Rust library for retrieving random data from (operating) system sources. It is +assumed that the system always provides high-quality cryptographically secure random +data, ideally backed by hardware entropy sources. This crate derives its name +from Linux's `getrandom` function, but is cross-platform, roughly supporting +the same set of platforms as Rust's `std` lib. + +This is a low-level API. Most users should prefer using high-level random-number +library like [`rand`]. + +[`rand`]: https://crates.io/crates/rand + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +getrandom = "0.2" +``` + +Then invoke the `getrandom` function: + +```rust +fn get_random_buf() -> Result<[u8; 32], getrandom::Error> { + let mut buf = [0u8; 32]; + getrandom::getrandom(&mut buf)?; + Ok(buf) +} +``` + +For more information about supported targets, entropy sources, `no_std` targets, +crate features, WASM support and Custom RNGs see the +[`getrandom` documentation](https://docs.rs/getrandom/latest) and +[`getrandom::Error` documentation](https://docs.rs/getrandom/latest/getrandom/struct.Error.html). + +## Minimum Supported Rust Version + +This crate requires Rust 1.36.0 or later. + +# License + +The `getrandom` library is distributed under either of + + * [Apache License, Version 2.0](LICENSE-APACHE) + * [MIT license](LICENSE-MIT) + +at your option. diff --git a/javascript-engine/external/getrandom/benches/buffer.rs b/javascript-engine/external/getrandom/benches/buffer.rs new file mode 100644 index 0000000..b32be43 --- /dev/null +++ b/javascript-engine/external/getrandom/benches/buffer.rs @@ -0,0 +1,71 @@ +#![feature(test, maybe_uninit_uninit_array_transpose)] +extern crate test; + +use std::mem::MaybeUninit; + +// Call getrandom on a zero-initialized stack buffer +#[inline(always)] +fn bench_getrandom() { + let mut buf = [0u8; N]; + getrandom::getrandom(&mut buf).unwrap(); + test::black_box(&buf as &[u8]); +} + +// Call getrandom_uninit on an uninitialized stack buffer +#[inline(always)] +fn bench_getrandom_uninit() { + let mut uninit = [MaybeUninit::uninit(); N]; + let buf: &[u8] = getrandom::getrandom_uninit(&mut uninit).unwrap(); + test::black_box(buf); +} + +// We benchmark using #[inline(never)] "inner" functions for two reasons: +// - Avoiding inlining reduces a source of variance when running benchmarks. +// - It is _much_ easier to get the assembly or IR for the inner loop. +// +// For example, using cargo-show-asm (https://github.com/pacak/cargo-show-asm), +// we can get the assembly for a particular benchmark's inner loop by running: +// cargo asm --bench buffer --release buffer::p384::bench_getrandom::inner +macro_rules! bench { + ( $name:ident, $size:expr ) => { + pub mod $name { + #[bench] + pub fn bench_getrandom(b: &mut test::Bencher) { + #[inline(never)] + fn inner() { + super::bench_getrandom::<{ $size }>() + } + + b.bytes = $size as u64; + b.iter(inner); + } + #[bench] + pub fn bench_getrandom_uninit(b: &mut test::Bencher) { + #[inline(never)] + fn inner() { + super::bench_getrandom_uninit::<{ $size }>() + } + + b.bytes = $size as u64; + b.iter(inner); + } + } + }; +} + +// 16 bytes (128 bits) is the size of an 128-bit AES key/nonce. +bench!(aes128, 128 / 8); + +// 32 bytes (256 bits) is the seed sized used for rand::thread_rng +// and the `random` value in a ClientHello/ServerHello for TLS. +// This is also the size of a 256-bit AES/HMAC/P-256/Curve25519 key +// and/or nonce. +bench!(p256, 256 / 8); + +// A P-384/HMAC-384 key and/or nonce. +bench!(p384, 384 / 8); + +// Initializing larger buffers is not the primary use case of this library, as +// this should normally be done by a userspace CSPRNG. However, we have a test +// here to see the effects of a lower (amortized) syscall overhead. +bench!(page, 4096); diff --git a/javascript-engine/external/getrandom/src/3ds.rs b/javascript-engine/external/getrandom/src/3ds.rs new file mode 100644 index 0000000..87a32a1 --- /dev/null +++ b/javascript-engine/external/getrandom/src/3ds.rs @@ -0,0 +1,18 @@ +// Copyright 2021 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for Nintendo 3DS +use crate::util_libc::sys_fill_exact; +use crate::Error; +use core::mem::MaybeUninit; + +pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + sys_fill_exact(dest, |buf| unsafe { + libc::getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0) + }) +} diff --git a/javascript-engine/external/getrandom/src/apple-other.rs b/javascript-engine/external/getrandom/src/apple-other.rs new file mode 100644 index 0000000..8f90485 --- /dev/null +++ b/javascript-engine/external/getrandom/src/apple-other.rs @@ -0,0 +1,27 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for iOS +use crate::Error; +use core::{ffi::c_void, mem::MaybeUninit, ptr::null}; + +#[link(name = "Security", kind = "framework")] +extern "C" { + fn SecRandomCopyBytes(rnd: *const c_void, count: usize, bytes: *mut u8) -> i32; +} + +pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + // Apple's documentation guarantees kSecRandomDefault is a synonym for NULL. + let ret = unsafe { SecRandomCopyBytes(null(), dest.len(), dest.as_mut_ptr() as *mut u8) }; + // errSecSuccess (from SecBase.h) is always zero. + if ret != 0 { + Err(Error::IOS_SEC_RANDOM) + } else { + Ok(()) + } +} diff --git a/javascript-engine/external/getrandom/src/bsd_arandom.rs b/javascript-engine/external/getrandom/src/bsd_arandom.rs new file mode 100644 index 0000000..3539dd5 --- /dev/null +++ b/javascript-engine/external/getrandom/src/bsd_arandom.rs @@ -0,0 +1,55 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for FreeBSD and NetBSD +use crate::{util_libc::sys_fill_exact, Error}; +use core::{mem::MaybeUninit, ptr}; + +fn kern_arnd(buf: &mut [MaybeUninit]) -> libc::ssize_t { + static MIB: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_ARND]; + let mut len = buf.len(); + let ret = unsafe { + libc::sysctl( + MIB.as_ptr(), + MIB.len() as libc::c_uint, + buf.as_mut_ptr() as *mut _, + &mut len, + ptr::null(), + 0, + ) + }; + if ret == -1 { + -1 + } else { + len as libc::ssize_t + } +} + +pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + // getrandom(2) was introduced in FreeBSD 12.0 and NetBSD 10.0 + #[cfg(target_os = "freebsd")] + { + use crate::util_libc::Weak; + static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") }; + type GetRandomFn = + unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t; + + if let Some(fptr) = GETRANDOM.ptr() { + let func: GetRandomFn = unsafe { core::mem::transmute(fptr) }; + return sys_fill_exact(dest, |buf| unsafe { + func(buf.as_mut_ptr() as *mut u8, buf.len(), 0) + }); + } + } + // Both FreeBSD and NetBSD will only return up to 256 bytes at a time, and + // older NetBSD kernels will fail on longer buffers. + for chunk in dest.chunks_mut(256) { + sys_fill_exact(chunk, kern_arnd)? + } + Ok(()) +} diff --git a/javascript-engine/external/getrandom/src/custom.rs b/javascript-engine/external/getrandom/src/custom.rs new file mode 100644 index 0000000..35771f1 --- /dev/null +++ b/javascript-engine/external/getrandom/src/custom.rs @@ -0,0 +1,108 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! An implementation which calls out to an externally defined function. +use crate::{util::uninit_slice_fill_zero, Error}; +use core::{mem::MaybeUninit, num::NonZeroU32}; + +/// Register a function to be invoked by `getrandom` on unsupported targets. +/// +/// ## Writing a custom `getrandom` implementation +/// +/// The function to register must have the same signature as +/// [`getrandom::getrandom`](crate::getrandom). The function can be defined +/// wherever you want, either in root crate or a dependent crate. +/// +/// For example, if we wanted a `failure-getrandom` crate containing an +/// implementation that always fails, we would first depend on `getrandom` +/// (for the [`Error`] type) in `failure-getrandom/Cargo.toml`: +/// ```toml +/// [dependencies] +/// getrandom = "0.2" +/// ``` +/// Note that the crate containing this function does **not** need to enable the +/// `"custom"` Cargo feature. +/// +/// Next, in `failure-getrandom/src/lib.rs`, we define our function: +/// ```rust +/// use core::num::NonZeroU32; +/// use getrandom::Error; +/// +/// // Some application-specific error code +/// const MY_CUSTOM_ERROR_CODE: u32 = Error::CUSTOM_START + 42; +/// pub fn always_fail(buf: &mut [u8]) -> Result<(), Error> { +/// let code = NonZeroU32::new(MY_CUSTOM_ERROR_CODE).unwrap(); +/// Err(Error::from(code)) +/// } +/// ``` +/// +/// ## Registering a custom `getrandom` implementation +/// +/// Functions can only be registered in the root binary crate. Attempting to +/// register a function in a non-root crate will result in a linker error. +/// This is similar to +/// [`#[panic_handler]`](https://doc.rust-lang.org/nomicon/panic-handler.html) or +/// [`#[global_allocator]`](https://doc.rust-lang.org/edition-guide/rust-2018/platform-and-target-support/global-allocators.html), +/// where helper crates define handlers/allocators but only the binary crate +/// actually _uses_ the functionality. +/// +/// To register the function, we first depend on `failure-getrandom` _and_ +/// `getrandom` in `Cargo.toml`: +/// ```toml +/// [dependencies] +/// failure-getrandom = "0.1" +/// getrandom = { version = "0.2", features = ["custom"] } +/// ``` +/// +/// Then, we register the function in `src/main.rs`: +/// ```rust +/// # mod failure_getrandom { pub fn always_fail(_: &mut [u8]) -> Result<(), getrandom::Error> { unimplemented!() } } +/// use failure_getrandom::always_fail; +/// use getrandom::register_custom_getrandom; +/// +/// register_custom_getrandom!(always_fail); +/// ``` +/// +/// Now any user of `getrandom` (direct or indirect) on this target will use the +/// registered function. As noted in the +/// [top-level documentation](index.html#custom-implementations) this +/// registration only has an effect on unsupported targets. +#[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "custom")))] +macro_rules! register_custom_getrandom { + ($path:path) => { + // We use an extern "C" function to get the guarantees of a stable ABI. + #[no_mangle] + extern "C" fn __getrandom_custom(dest: *mut u8, len: usize) -> u32 { + let f: fn(&mut [u8]) -> Result<(), $crate::Error> = $path; + let slice = unsafe { ::core::slice::from_raw_parts_mut(dest, len) }; + match f(slice) { + Ok(()) => 0, + Err(e) => e.code().get(), + } + } + }; +} + +#[allow(dead_code)] +pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + extern "C" { + fn __getrandom_custom(dest: *mut u8, len: usize) -> u32; + } + // Previously we always passed a valid, initialized slice to + // `__getrandom_custom`. Ensure `dest` has been initialized for backward + // compatibility with implementations that rely on that (e.g. Rust + // implementations that construct a `&mut [u8]` slice from `dest` and + // `len`). + let dest = uninit_slice_fill_zero(dest); + let ret = unsafe { __getrandom_custom(dest.as_mut_ptr(), dest.len()) }; + match NonZeroU32::new(ret) { + None => Ok(()), + Some(code) => Err(Error::from(code)), + } +} diff --git a/javascript-engine/external/getrandom/src/dragonfly.rs b/javascript-engine/external/getrandom/src/dragonfly.rs new file mode 100644 index 0000000..d3ef00a --- /dev/null +++ b/javascript-engine/external/getrandom/src/dragonfly.rs @@ -0,0 +1,30 @@ +// Copyright 2021 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for DragonFly BSD +use crate::{ + use_file, + util_libc::{sys_fill_exact, Weak}, + Error, +}; +use core::mem::MaybeUninit; + +pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") }; + type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t; + + // getrandom(2) was introduced in DragonflyBSD 5.7 + if let Some(fptr) = GETRANDOM.ptr() { + let func: GetRandomFn = unsafe { core::mem::transmute(fptr) }; + return sys_fill_exact(dest, |buf| unsafe { + func(buf.as_mut_ptr() as *mut u8, buf.len(), 0) + }); + } else { + use_file::getrandom_inner(dest) + } +} diff --git a/javascript-engine/external/getrandom/src/emscripten.rs b/javascript-engine/external/getrandom/src/emscripten.rs new file mode 100644 index 0000000..a50372f --- /dev/null +++ b/javascript-engine/external/getrandom/src/emscripten.rs @@ -0,0 +1,18 @@ +//! Implementation for Emscripten +use crate::{util_libc::last_os_error, Error}; +use core::mem::MaybeUninit; + +// Not yet defined in libc crate. +extern "C" { + fn getentropy(buffer: *mut libc::c_void, length: usize) -> libc::c_int; +} + +pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + // Emscripten 2.0.5 added getentropy, so we can use it unconditionally. + // Unlike other getentropy implementations, there is no max buffer length. + let ret = unsafe { getentropy(dest.as_mut_ptr() as *mut libc::c_void, dest.len()) }; + if ret < 0 { + return Err(last_os_error()); + } + Ok(()) +} diff --git a/javascript-engine/external/getrandom/src/error.rs b/javascript-engine/external/getrandom/src/error.rs new file mode 100644 index 0000000..ab39a3c --- /dev/null +++ b/javascript-engine/external/getrandom/src/error.rs @@ -0,0 +1,191 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +use core::{fmt, num::NonZeroU32}; + +/// A small and `no_std` compatible error type +/// +/// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and +/// if so, which error code the OS gave the application. If such an error is +/// encountered, please consult with your system documentation. +/// +/// Internally this type is a NonZeroU32, with certain values reserved for +/// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`]. +/// +/// *If this crate's `"std"` Cargo feature is enabled*, then: +/// - [`getrandom::Error`][Error] implements +/// [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html) +/// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements +/// [`From`](https://doc.rust-lang.org/std/convert/trait.From.html). +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct Error(NonZeroU32); + +const fn internal_error(n: u16) -> Error { + // SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32. + let code = Error::INTERNAL_START + (n as u32); + Error(unsafe { NonZeroU32::new_unchecked(code) }) +} + +impl Error { + /// This target/platform is not supported by `getrandom`. + pub const UNSUPPORTED: Error = internal_error(0); + /// The platform-specific `errno` returned a non-positive value. + pub const ERRNO_NOT_POSITIVE: Error = internal_error(1); + /// Call to iOS [`SecRandomCopyBytes`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes) failed. + pub const IOS_SEC_RANDOM: Error = internal_error(3); + /// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed. + pub const WINDOWS_RTL_GEN_RANDOM: Error = internal_error(4); + /// RDRAND instruction failed due to a hardware issue. + pub const FAILED_RDRAND: Error = internal_error(5); + /// RDRAND instruction unsupported on this target. + pub const NO_RDRAND: Error = internal_error(6); + /// The environment does not support the Web Crypto API. + pub const WEB_CRYPTO: Error = internal_error(7); + /// Calling Web Crypto API `crypto.getRandomValues` failed. + pub const WEB_GET_RANDOM_VALUES: Error = internal_error(8); + /// On VxWorks, call to `randSecure` failed (random number generator is not yet initialized). + pub const VXWORKS_RAND_SECURE: Error = internal_error(11); + /// Node.js does not have the `crypto` CommonJS module. + pub const NODE_CRYPTO: Error = internal_error(12); + /// Calling Node.js function `crypto.randomFillSync` failed. + pub const NODE_RANDOM_FILL_SYNC: Error = internal_error(13); + /// Called from an ES module on Node.js. This is unsupported, see: + /// . + pub const NODE_ES_MODULE: Error = internal_error(14); + + /// Codes below this point represent OS Errors (i.e. positive i32 values). + /// Codes at or above this point, but below [`Error::CUSTOM_START`] are + /// reserved for use by the `rand` and `getrandom` crates. + pub const INTERNAL_START: u32 = 1 << 31; + + /// Codes at or above this point can be used by users to define their own + /// custom errors. + pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30); + + /// Extract the raw OS error code (if this error came from the OS) + /// + /// This method is identical to [`std::io::Error::raw_os_error()`][1], except + /// that it works in `no_std` contexts. If this method returns `None`, the + /// error value can still be formatted via the `Display` implementation. + /// + /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error + #[inline] + pub fn raw_os_error(self) -> Option { + if self.0.get() < Self::INTERNAL_START { + match () { + #[cfg(target_os = "solid_asp3")] + // On SOLID, negate the error code again to obtain the original + // error code. + () => Some(-(self.0.get() as i32)), + #[cfg(not(target_os = "solid_asp3"))] + () => Some(self.0.get() as i32), + } + } else { + None + } + } + + /// Extract the bare error code. + /// + /// This code can either come from the underlying OS, or be a custom error. + /// Use [`Error::raw_os_error()`] to disambiguate. + #[inline] + pub const fn code(self) -> NonZeroU32 { + self.0 + } +} + +cfg_if! { + if #[cfg(unix)] { + fn os_err(errno: i32, buf: &mut [u8]) -> Option<&str> { + let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char; + if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 { + return None; + } + + // Take up to trailing null byte + let n = buf.len(); + let idx = buf.iter().position(|&b| b == 0).unwrap_or(n); + core::str::from_utf8(&buf[..idx]).ok() + } + } else { + fn os_err(_errno: i32, _buf: &mut [u8]) -> Option<&str> { + None + } + } +} + +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut dbg = f.debug_struct("Error"); + if let Some(errno) = self.raw_os_error() { + dbg.field("os_error", &errno); + let mut buf = [0u8; 128]; + if let Some(err) = os_err(errno, &mut buf) { + dbg.field("description", &err); + } + } else if let Some(desc) = internal_desc(*self) { + dbg.field("internal_code", &self.0.get()); + dbg.field("description", &desc); + } else { + dbg.field("unknown_code", &self.0.get()); + } + dbg.finish() + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(errno) = self.raw_os_error() { + let mut buf = [0u8; 128]; + match os_err(errno, &mut buf) { + Some(err) => err.fmt(f), + None => write!(f, "OS Error: {}", errno), + } + } else if let Some(desc) = internal_desc(*self) { + f.write_str(desc) + } else { + write!(f, "Unknown Error: {}", self.0.get()) + } + } +} + +impl From for Error { + fn from(code: NonZeroU32) -> Self { + Self(code) + } +} + +fn internal_desc(error: Error) -> Option<&'static str> { + match error { + Error::UNSUPPORTED => Some("getrandom: this target is not supported"), + Error::ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"), + Error::IOS_SEC_RANDOM => Some("SecRandomCopyBytes: iOS Security framework failure"), + Error::WINDOWS_RTL_GEN_RANDOM => Some("RtlGenRandom: Windows system function failure"), + Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"), + Error::NO_RDRAND => Some("RDRAND: instruction not supported"), + Error::WEB_CRYPTO => Some("Web Crypto API is unavailable"), + Error::WEB_GET_RANDOM_VALUES => Some("Calling Web API crypto.getRandomValues failed"), + Error::VXWORKS_RAND_SECURE => Some("randSecure: VxWorks RNG module is not initialized"), + Error::NODE_CRYPTO => Some("Node.js crypto CommonJS module is unavailable"), + Error::NODE_RANDOM_FILL_SYNC => Some("Calling Node.js API crypto.randomFillSync failed"), + Error::NODE_ES_MODULE => Some("Node.js ES modules are not directly supported, see https://docs.rs/getrandom#nodejs-es-module-support"), + _ => None, + } +} + +#[cfg(test)] +mod tests { + use super::Error; + use core::mem::size_of; + + #[test] + fn test_size() { + assert_eq!(size_of::(), 4); + assert_eq!(size_of::>(), 4); + } +} diff --git a/javascript-engine/external/getrandom/src/error_impls.rs b/javascript-engine/external/getrandom/src/error_impls.rs new file mode 100644 index 0000000..61f46d2 --- /dev/null +++ b/javascript-engine/external/getrandom/src/error_impls.rs @@ -0,0 +1,24 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![cfg_attr(docsrs, doc(cfg(feature = "std")))] +extern crate std; + +use crate::Error; +use core::convert::From; +use std::io; + +impl From for io::Error { + fn from(err: Error) -> Self { + match err.raw_os_error() { + Some(errno) => io::Error::from_raw_os_error(errno), + None => io::Error::new(io::ErrorKind::Other, err), + } + } +} + +impl std::error::Error for Error {} diff --git a/javascript-engine/external/getrandom/src/espidf.rs b/javascript-engine/external/getrandom/src/espidf.rs new file mode 100644 index 0000000..d074dc4 --- /dev/null +++ b/javascript-engine/external/getrandom/src/espidf.rs @@ -0,0 +1,26 @@ +// Copyright 2021 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for ESP-IDF +use crate::Error; +use core::{ffi::c_void, mem::MaybeUninit}; + +extern "C" { + fn esp_fill_random(buf: *mut c_void, len: usize) -> u32; +} + +pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + // Not that NOT enabling WiFi, BT, or the voltage noise entropy source (via `bootloader_random_enable`) + // will cause ESP-IDF to return pseudo-random numbers based on the voltage noise entropy, after the initial boot process: + // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html + // + // However tracking if some of these entropy sources is enabled is way too difficult to implement here + unsafe { esp_fill_random(dest.as_mut_ptr().cast(), dest.len()) }; + + Ok(()) +} diff --git a/javascript-engine/external/getrandom/src/fuchsia.rs b/javascript-engine/external/getrandom/src/fuchsia.rs new file mode 100644 index 0000000..5a135f3 --- /dev/null +++ b/javascript-engine/external/getrandom/src/fuchsia.rs @@ -0,0 +1,21 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for Fuchsia Zircon +use crate::Error; +use core::mem::MaybeUninit; + +#[link(name = "zircon")] +extern "C" { + fn zx_cprng_draw(buffer: *mut u8, length: usize); +} + +pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + unsafe { zx_cprng_draw(dest.as_mut_ptr() as *mut u8, dest.len()) } + Ok(()) +} diff --git a/javascript-engine/external/getrandom/src/js.rs b/javascript-engine/external/getrandom/src/js.rs new file mode 100644 index 0000000..d031282 --- /dev/null +++ b/javascript-engine/external/getrandom/src/js.rs @@ -0,0 +1,161 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +use crate::Error; + +extern crate std; +use std::{mem::MaybeUninit, thread_local}; + +use js_sys::{global, Function, Uint8Array}; +use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue}; + +// Size of our temporary Uint8Array buffer used with WebCrypto methods +// Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues +const WEB_CRYPTO_BUFFER_SIZE: usize = 256; +// Node.js's crypto.randomFillSync requires the size to be less than 2**31. +const NODE_MAX_BUFFER_SIZE: usize = (1 << 31) - 1; + +enum RngSource { + Node(NodeCrypto), + Web(WebCrypto, Uint8Array), +} + +// JsValues are always per-thread, so we initialize RngSource for each thread. +// See: https://github.com/rustwasm/wasm-bindgen/pull/955 +thread_local!( + static RNG_SOURCE: Result = getrandom_init(); +); + +pub(crate) fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + RNG_SOURCE.with(|result| { + let source = result.as_ref().map_err(|&e| e)?; + + match source { + RngSource::Node(n) => { + for chunk in dest.chunks_mut(NODE_MAX_BUFFER_SIZE) { + // SAFETY: chunk is never used directly, the memory is only + // modified via the Uint8Array view, which is passed + // directly to JavaScript. Also, crypto.randomFillSync does + // not resize the buffer. We know the length is less than + // u32::MAX because of the chunking above. + // Note that this uses the fact that JavaScript doesn't + // have a notion of "uninitialized memory", this is purely + // a Rust/C/C++ concept. + let res = n.random_fill_sync(unsafe { + Uint8Array::view_mut_raw(chunk.as_mut_ptr() as *mut u8, chunk.len()) + }); + if res.is_err() { + return Err(Error::NODE_RANDOM_FILL_SYNC); + } + } + } + RngSource::Web(crypto, buf) => { + // getRandomValues does not work with all types of WASM memory, + // so we initially write to browser memory to avoid exceptions. + for chunk in dest.chunks_mut(WEB_CRYPTO_BUFFER_SIZE) { + // The chunk can be smaller than buf's length, so we call to + // JS to create a smaller view of buf without allocation. + let sub_buf = buf.subarray(0, chunk.len() as u32); + + if crypto.get_random_values(&sub_buf).is_err() { + return Err(Error::WEB_GET_RANDOM_VALUES); + } + + // SAFETY: `sub_buf`'s length is the same length as `chunk` + unsafe { sub_buf.raw_copy_to_ptr(chunk.as_mut_ptr() as *mut u8) }; + } + } + }; + Ok(()) + }) +} + +fn getrandom_init() -> Result { + let global: Global = global().unchecked_into(); + + // Get the Web Crypto interface if we are in a browser, Web Worker, Deno, + // or another environment that supports the Web Cryptography API. This + // also allows for user-provided polyfills in unsupported environments. + let crypto = match global.crypto() { + // Standard Web Crypto interface + c if c.is_object() => c, + // Node.js CommonJS Crypto module + _ if is_node(&global) => { + // If module.require isn't a valid function, we are in an ES module. + match Module::require_fn().and_then(JsCast::dyn_into::) { + Ok(require_fn) => match require_fn.call1(&global, &JsValue::from_str("crypto")) { + Ok(n) => return Ok(RngSource::Node(n.unchecked_into())), + Err(_) => return Err(Error::NODE_CRYPTO), + }, + Err(_) => return Err(Error::NODE_ES_MODULE), + } + } + // IE 11 Workaround + _ => match global.ms_crypto() { + c if c.is_object() => c, + _ => return Err(Error::WEB_CRYPTO), + }, + }; + + let buf = Uint8Array::new_with_length(WEB_CRYPTO_BUFFER_SIZE as u32); + Ok(RngSource::Web(crypto, buf)) +} + +// Taken from https://www.npmjs.com/package/browser-or-node +fn is_node(global: &Global) -> bool { + let process = global.process(); + if process.is_object() { + let versions = process.versions(); + if versions.is_object() { + return versions.node().is_string(); + } + } + false +} + +#[wasm_bindgen] +extern "C" { + // Return type of js_sys::global() + type Global; + + // Web Crypto API: Crypto interface (https://www.w3.org/TR/WebCryptoAPI/) + type WebCrypto; + // Getters for the WebCrypto API + #[wasm_bindgen(method, getter)] + fn crypto(this: &Global) -> WebCrypto; + #[wasm_bindgen(method, getter, js_name = msCrypto)] + fn ms_crypto(this: &Global) -> WebCrypto; + // Crypto.getRandomValues() + #[wasm_bindgen(method, js_name = getRandomValues, catch)] + fn get_random_values(this: &WebCrypto, buf: &Uint8Array) -> Result<(), JsValue>; + + // Node JS crypto module (https://nodejs.org/api/crypto.html) + type NodeCrypto; + // crypto.randomFillSync() + #[wasm_bindgen(method, js_name = randomFillSync, catch)] + fn random_fill_sync(this: &NodeCrypto, buf: Uint8Array) -> Result<(), JsValue>; + + // Ideally, we would just use `fn require(s: &str)` here. However, doing + // this causes a Webpack warning. So we instead return the function itself + // and manually invoke it using call1. This also lets us to check that the + // function actually exists, allowing for better error messages. See: + // https://github.com/rust-random/getrandom/issues/224 + // https://github.com/rust-random/getrandom/issues/256 + type Module; + #[wasm_bindgen(getter, static_method_of = Module, js_class = module, js_name = require, catch)] + fn require_fn() -> Result; + + // Node JS process Object (https://nodejs.org/api/process.html) + #[wasm_bindgen(method, getter)] + fn process(this: &Global) -> Process; + type Process; + #[wasm_bindgen(method, getter)] + fn versions(this: &Process) -> Versions; + type Versions; + #[wasm_bindgen(method, getter)] + fn node(this: &Versions) -> JsValue; +} diff --git a/javascript-engine/external/getrandom/src/lib.rs b/javascript-engine/external/getrandom/src/lib.rs new file mode 100644 index 0000000..0c8d759 --- /dev/null +++ b/javascript-engine/external/getrandom/src/lib.rs @@ -0,0 +1,340 @@ +// Copyright 2019 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Interface to the operating system's random number generator. +//! +//! # Supported targets +//! +//! | Target | Target Triple | Implementation +//! | ----------------- | ------------------ | -------------- +//! | Linux, Android | `*‑linux‑*` | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after successfully polling `/dev/random` +//! | Windows | `*‑windows‑*` | [`BCryptGenRandom`] +//! | macOS | `*‑apple‑darwin` | [`getentropy`][3] if available, otherwise [`/dev/urandom`][4] (identical to `/dev/random`) +//! | iOS, tvOS, watchOS | `*‑apple‑ios`, `*-apple-tvos`, `*-apple-watchos` | [`SecRandomCopyBytes`] +//! | FreeBSD | `*‑freebsd` | [`getrandom`][5] if available, otherwise [`kern.arandom`][6] +//! | OpenBSD | `*‑openbsd` | [`getentropy`][7] +//! | NetBSD | `*‑netbsd` | [`kern.arandom`][8] +//! | Dragonfly BSD | `*‑dragonfly` | [`getrandom`][9] if available, otherwise [`/dev/urandom`][10] (identical to `/dev/random`) +//! | Solaris, illumos | `*‑solaris`, `*‑illumos` | [`getrandom`][11] if available, otherwise [`/dev/random`][12] +//! | Fuchsia OS | `*‑fuchsia` | [`cprng_draw`] +//! | Redox | `*‑redox` | `/dev/urandom` +//! | Haiku | `*‑haiku` | `/dev/urandom` (identical to `/dev/random`) +//! | Hermit | `x86_64-*-hermit` | [`RDRAND`] +//! | SGX | `x86_64‑*‑sgx` | [`RDRAND`] +//! | VxWorks | `*‑wrs‑vxworks‑*` | `randABytes` after checking entropy pool initialization with `randSecure` +//! | ESP-IDF | `*‑espidf` | [`esp_fill_random`] +//! | Emscripten | `*‑emscripten` | [`getentropy`][13] +//! | WASI | `wasm32‑wasi` | [`random_get`] +//! | Web Browser and Node.js | `wasm*‑*‑unknown` | [`Crypto.getRandomValues`] if available, then [`crypto.randomFillSync`] if on Node.js, see [WebAssembly support] +//! | SOLID | `*-kmc-solid_*` | `SOLID_RNG_SampleRandomBytes` +//! | Nintendo 3DS | `armv6k-nintendo-3ds` | [`getrandom`][1] +//! | QNX Neutrino | `*‑nto-qnx*` | [`/dev/urandom`][14] (identical to `/dev/random`) +//! | AIX | `*-ibm-aix` | [`/dev/urandom`][15] +//! +//! There is no blanket implementation on `unix` targets that reads from +//! `/dev/urandom`. This ensures all supported targets are using the recommended +//! interface and respect maximum buffer sizes. +//! +//! Pull Requests that add support for new targets to `getrandom` are always welcome. +//! +//! ## Unsupported targets +//! +//! By default, `getrandom` will not compile on unsupported targets, but certain +//! features allow a user to select a "fallback" implementation if no supported +//! implementation exists. +//! +//! All of the below mechanisms only affect unsupported +//! targets. Supported targets will _always_ use their supported implementations. +//! This prevents a crate from overriding a secure source of randomness +//! (either accidentally or intentionally). +//! +//! ### RDRAND on x86 +//! +//! *If the `rdrand` Cargo feature is enabled*, `getrandom` will fallback to using +//! the [`RDRAND`] instruction to get randomness on `no_std` `x86`/`x86_64` +//! targets. This feature has no effect on other CPU architectures. +//! +//! ### WebAssembly support +//! +//! This crate fully supports the +//! [`wasm32-wasi`](https://github.com/CraneStation/wasi) and +//! [`wasm32-unknown-emscripten`](https://www.hellorust.com/setup/emscripten/) +//! targets. However, the `wasm32-unknown-unknown` target (i.e. the target used +//! by `wasm-pack`) is not automatically +//! supported since, from the target name alone, we cannot deduce which +//! JavaScript interface is in use (or if JavaScript is available at all). +//! +//! Instead, *if the `js` Cargo feature is enabled*, this crate will assume +//! that you are building for an environment containing JavaScript, and will +//! call the appropriate methods. Both web browser (main window and Web Workers) +//! and Node.js environments are supported, invoking the methods +//! [described above](#supported-targets) using the [`wasm-bindgen`] toolchain. +//! +//! To enable the `js` Cargo feature, add the following to the `dependencies` +//! section in your `Cargo.toml` file: +//! ```toml +//! [dependencies] +//! getrandom = { version = "0.2", features = ["js"] } +//! ``` +//! +//! This can be done even if `getrandom` is not a direct dependency. Cargo +//! allows crates to enable features for indirect dependencies. +//! +//! This feature should only be enabled for binary, test, or benchmark crates. +//! Library crates should generally not enable this feature, leaving such a +//! decision to *users* of their library. Also, libraries should not introduce +//! their own `js` features *just* to enable `getrandom`'s `js` feature. +//! +//! This feature has no effect on targets other than `wasm32-unknown-unknown`. +//! +//! #### Node.js ES module support +//! +//! Node.js supports both [CommonJS modules] and [ES modules]. Due to +//! limitations in wasm-bindgen's [`module`] support, we cannot directly +//! support ES Modules running on Node.js. However, on Node v15 and later, the +//! module author can add a simple shim to support the Web Cryptography API: +//! ```js +//! import { webcrypto } from 'node:crypto' +//! globalThis.crypto = webcrypto +//! ``` +//! This crate will then use the provided `webcrypto` implementation. +//! +//! ### Custom implementations +//! +//! The [`register_custom_getrandom!`] macro allows a user to mark their own +//! function as the backing implementation for [`getrandom`]. See the macro's +//! documentation for more information about writing and registering your own +//! custom implementations. +//! +//! Note that registering a custom implementation only has an effect on targets +//! that would otherwise not compile. Any supported targets (including those +//! using `rdrand` and `js` Cargo features) continue using their normal +//! implementations even if a function is registered. +//! +//! ## Early boot +//! +//! Sometimes, early in the boot process, the OS has not collected enough +//! entropy to securely seed its RNG. This is especially common on virtual +//! machines, where standard "random" events are hard to come by. +//! +//! Some operating system interfaces always block until the RNG is securely +//! seeded. This can take anywhere from a few seconds to more than a minute. +//! A few (Linux, NetBSD and Solaris) offer a choice between blocking and +//! getting an error; in these cases, we always choose to block. +//! +//! On Linux (when the `getrandom` system call is not available), reading from +//! `/dev/urandom` never blocks, even when the OS hasn't collected enough +//! entropy yet. To avoid returning low-entropy bytes, we first poll +//! `/dev/random` and only switch to `/dev/urandom` once this has succeeded. +//! +//! On OpenBSD, this kind of entropy accounting isn't available, and on +//! NetBSD, blocking on it is discouraged. On these platforms, nonblocking +//! interfaces are used, even when reliable entropy may not be available. +//! On the platforms where it is used, the reliability of entropy accounting +//! itself isn't free from controversy. This library provides randomness +//! sourced according to the platform's best practices, but each platform has +//! its own limits on the grade of randomness it can promise in environments +//! with few sources of entropy. +//! +//! ## Error handling +//! +//! We always choose failure over returning known insecure "random" bytes. In +//! general, on supported platforms, failure is highly unlikely, though not +//! impossible. If an error does occur, then it is likely that it will occur +//! on every call to `getrandom`, hence after the first successful call one +//! can be reasonably confident that no errors will occur. +//! +//! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html +//! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html +//! [3]: https://www.unix.com/man-page/mojave/2/getentropy/ +//! [4]: https://www.unix.com/man-page/mojave/4/urandom/ +//! [5]: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable +//! [6]: https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4 +//! [7]: https://man.openbsd.org/getentropy.2 +//! [8]: https://man.netbsd.org/sysctl.7 +//! [9]: https://leaf.dragonflybsd.org/cgi/web-man?command=getrandom +//! [10]: https://leaf.dragonflybsd.org/cgi/web-man?command=random§ion=4 +//! [11]: https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html +//! [12]: https://docs.oracle.com/cd/E86824_01/html/E54777/random-7d.html +//! [13]: https://github.com/emscripten-core/emscripten/pull/12240 +//! [14]: https://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.utilities/topic/r/random.html +//! [15]: https://www.ibm.com/docs/en/aix/7.3?topic=files-random-urandom-devices +//! +//! [`BCryptGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom +//! [`Crypto.getRandomValues`]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues +//! [`RDRAND`]: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide +//! [`SecRandomCopyBytes`]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc +//! [`cprng_draw`]: https://fuchsia.dev/fuchsia-src/zircon/syscalls/cprng_draw +//! [`crypto.randomFillSync`]: https://nodejs.org/api/crypto.html#cryptorandomfillsyncbuffer-offset-size +//! [`esp_fill_random`]: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html#_CPPv415esp_fill_randomPv6size_t +//! [`random_get`]: https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#-random_getbuf-pointeru8-buf_len-size---errno +//! [WebAssembly support]: #webassembly-support +//! [`wasm-bindgen`]: https://github.com/rustwasm/wasm-bindgen +//! [`module`]: https://rustwasm.github.io/wasm-bindgen/reference/attributes/on-js-imports/module.html +//! [CommonJS modules]: https://nodejs.org/api/modules.html +//! [ES modules]: https://nodejs.org/api/esm.html + +#![doc( + html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", + html_favicon_url = "https://www.rust-lang.org/favicon.ico", + html_root_url = "https://docs.rs/getrandom/0.2.8" +)] +#![no_std] +#![warn(rust_2018_idioms, unused_lifetimes, missing_docs)] +#![cfg_attr(docsrs, feature(doc_cfg))] + +#[macro_use] +extern crate cfg_if; + +use crate::util::{slice_as_uninit_mut, slice_assume_init_mut}; +use core::mem::MaybeUninit; + +mod error; +mod util; +// To prevent a breaking change when targets are added, we always export the +// register_custom_getrandom macro, so old Custom RNG crates continue to build. +#[cfg(feature = "custom")] +mod custom; +#[cfg(feature = "std")] +mod error_impls; + +pub use crate::error::Error; + +// System-specific implementations. +// +// These should all provide getrandom_inner with the signature +// `fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error>`. +// The function MUST fully initialize `dest` when `Ok(())` is returned. +// The function MUST NOT ever write uninitialized bytes into `dest`, +// regardless of what value it returns. +cfg_if! { + if #[cfg(any(target_os = "haiku", target_os = "redox", target_os = "nto", target_os = "aix"))] { + mod util_libc; + #[path = "use_file.rs"] mod imp; + } else if #[cfg(any(target_os = "android", target_os = "linux"))] { + mod util_libc; + mod use_file; + #[path = "linux_android.rs"] mod imp; + } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] { + mod util_libc; + mod use_file; + #[path = "solaris_illumos.rs"] mod imp; + } else if #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] { + mod util_libc; + #[path = "bsd_arandom.rs"] mod imp; + } else if #[cfg(target_os = "dragonfly")] { + mod util_libc; + mod use_file; + #[path = "dragonfly.rs"] mod imp; + } else if #[cfg(target_os = "fuchsia")] { + #[path = "fuchsia.rs"] mod imp; + } else if #[cfg(any(target_os = "ios", target_os = "watchos", target_os = "tvos"))] { + #[path = "apple-other.rs"] mod imp; + } else if #[cfg(target_os = "macos")] { + mod util_libc; + mod use_file; + #[path = "macos.rs"] mod imp; + } else if #[cfg(target_os = "openbsd")] { + mod util_libc; + #[path = "openbsd.rs"] mod imp; + } else if #[cfg(all(target_arch = "wasm32", target_os = "wasi"))] { + #[path = "wasi.rs"] mod imp; + } else if #[cfg(all(target_arch = "x86_64", target_os = "hermit"))] { + #[path = "rdrand.rs"] mod imp; + } else if #[cfg(target_os = "vxworks")] { + mod util_libc; + #[path = "vxworks.rs"] mod imp; + } else if #[cfg(target_os = "solid_asp3")] { + #[path = "solid.rs"] mod imp; + } else if #[cfg(target_os = "espidf")] { + #[path = "espidf.rs"] mod imp; + } else if #[cfg(windows)] { + #[path = "windows.rs"] mod imp; + } else if #[cfg(all(target_os = "horizon", target_arch = "arm"))] { + // We check for target_arch = "arm" because the Nintendo Switch also + // uses Horizon OS (it is aarch64). + mod util_libc; + #[path = "3ds.rs"] mod imp; + } else if #[cfg(target_os = "emscripten")] { + mod util_libc; + #[path = "emscripten.rs"] mod imp; + } else if #[cfg(all(target_arch = "x86_64", target_env = "sgx"))] { + #[path = "rdrand.rs"] mod imp; + } else if #[cfg(all(feature = "rdrand", + any(target_arch = "x86_64", target_arch = "x86")))] { + #[path = "rdrand.rs"] mod imp; + } else if #[cfg(all(feature = "js", + any(target_arch = "wasm32", target_arch = "wasm64"), + target_os = "unknown"))] { + #[path = "js.rs"] mod imp; + } else if #[cfg(feature = "custom")] { + use custom as imp; + } else if #[cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), + target_os = "unknown"))] { + compile_error!("the wasm*-unknown-unknown targets are not supported by \ + default, you may need to enable the \"js\" feature. \ + For more information see: \ + https://docs.rs/getrandom/#webassembly-support"); + } else { + compile_error!("target is not supported, for more information see: \ + https://docs.rs/getrandom/#unsupported-targets"); + } +} + +/// Fill `dest` with random bytes from the system's preferred random number +/// source. +/// +/// This function returns an error on any failure, including partial reads. We +/// make no guarantees regarding the contents of `dest` on error. If `dest` is +/// empty, `getrandom` immediately returns success, making no calls to the +/// underlying operating system. +/// +/// Blocking is possible, at least during early boot; see module documentation. +/// +/// In general, `getrandom` will be fast enough for interactive usage, though +/// significantly slower than a user-space CSPRNG; for the latter consider +/// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html). +#[inline] +pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { + // SAFETY: The `&mut MaybeUninit<_>` reference doesn't escape, and + // `getrandom_uninit` guarantees it will never de-initialize any part of + // `dest`. + getrandom_uninit(unsafe { slice_as_uninit_mut(dest) })?; + Ok(()) +} + +/// Version of the `getrandom` function which fills `dest` with random bytes +/// returns a mutable reference to those bytes. +/// +/// On successful completion this function is guaranteed to return a slice +/// which points to the same memory as `dest` and has the same length. +/// In other words, it's safe to assume that `dest` is initialized after +/// this function has returned `Ok`. +/// +/// No part of `dest` will ever be de-initialized at any point, regardless +/// of what is returned. +/// +/// # Examples +/// +/// ```ignore +/// # // We ignore this test since `uninit_array` is unstable. +/// #![feature(maybe_uninit_uninit_array)] +/// # fn main() -> Result<(), getrandom::Error> { +/// let mut buf = core::mem::MaybeUninit::uninit_array::<1024>(); +/// let buf: &mut [u8] = getrandom::getrandom_uninit(&mut buf)?; +/// # Ok(()) } +/// ``` +#[inline] +pub fn getrandom_uninit(dest: &mut [MaybeUninit]) -> Result<&mut [u8], Error> { + if !dest.is_empty() { + imp::getrandom_inner(dest)?; + } + // SAFETY: `dest` has been fully initialized by `imp::getrandom_inner` + // since it returned `Ok`. + Ok(unsafe { slice_assume_init_mut(dest) }) +} diff --git a/javascript-engine/external/getrandom/src/linux_android.rs b/javascript-engine/external/getrandom/src/linux_android.rs new file mode 100644 index 0000000..e81f1e1 --- /dev/null +++ b/javascript-engine/external/getrandom/src/linux_android.rs @@ -0,0 +1,48 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for Linux / Android +use crate::{ + util::LazyBool, + util_libc::{last_os_error, sys_fill_exact}, + {use_file, Error}, +}; +use core::mem::MaybeUninit; + +pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + // getrandom(2) was introduced in Linux 3.17 + static HAS_GETRANDOM: LazyBool = LazyBool::new(); + if HAS_GETRANDOM.unsync_init(is_getrandom_available) { + sys_fill_exact(dest, |buf| unsafe { + getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0) + }) + } else { + use_file::getrandom_inner(dest) + } +} + +fn is_getrandom_available() -> bool { + let res = unsafe { getrandom(core::ptr::null_mut(), 0, libc::GRND_NONBLOCK) }; + if res < 0 { + match last_os_error().raw_os_error() { + Some(libc::ENOSYS) => false, // No kernel support + Some(libc::EPERM) => false, // Blocked by seccomp + _ => true, + } + } else { + true + } +} + +unsafe fn getrandom( + buf: *mut libc::c_void, + buflen: libc::size_t, + flags: libc::c_uint, +) -> libc::ssize_t { + libc::syscall(libc::SYS_getrandom, buf, buflen, flags) as libc::ssize_t +} diff --git a/javascript-engine/external/getrandom/src/macos.rs b/javascript-engine/external/getrandom/src/macos.rs new file mode 100644 index 0000000..312f9b2 --- /dev/null +++ b/javascript-engine/external/getrandom/src/macos.rs @@ -0,0 +1,36 @@ +// Copyright 2019 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for macOS +use crate::{ + use_file, + util_libc::{last_os_error, Weak}, + Error, +}; +use core::mem::{self, MaybeUninit}; + +type GetEntropyFn = unsafe extern "C" fn(*mut u8, libc::size_t) -> libc::c_int; + +pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + // getentropy(2) was added in 10.12, Rust supports 10.7+ + static GETENTROPY: Weak = unsafe { Weak::new("getentropy\0") }; + if let Some(fptr) = GETENTROPY.ptr() { + let func: GetEntropyFn = unsafe { mem::transmute(fptr) }; + for chunk in dest.chunks_mut(256) { + let ret = unsafe { func(chunk.as_mut_ptr() as *mut u8, chunk.len()) }; + if ret != 0 { + return Err(last_os_error()); + } + } + Ok(()) + } else { + // We fallback to reading from /dev/random instead of SecRandomCopyBytes + // to avoid high startup costs and linking the Security framework. + use_file::getrandom_inner(dest) + } +} diff --git a/javascript-engine/external/getrandom/src/openbsd.rs b/javascript-engine/external/getrandom/src/openbsd.rs new file mode 100644 index 0000000..7a76f61 --- /dev/null +++ b/javascript-engine/external/getrandom/src/openbsd.rs @@ -0,0 +1,22 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for OpenBSD +use crate::{util_libc::last_os_error, Error}; +use core::mem::MaybeUninit; + +pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + // getentropy(2) was added in OpenBSD 5.6, so we can use it unconditionally. + for chunk in dest.chunks_mut(256) { + let ret = unsafe { libc::getentropy(chunk.as_mut_ptr() as *mut libc::c_void, chunk.len()) }; + if ret == -1 { + return Err(last_os_error()); + } + } + Ok(()) +} diff --git a/javascript-engine/external/getrandom/src/rdrand.rs b/javascript-engine/external/getrandom/src/rdrand.rs new file mode 100644 index 0000000..910b535 --- /dev/null +++ b/javascript-engine/external/getrandom/src/rdrand.rs @@ -0,0 +1,97 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for SGX using RDRAND instruction +use crate::{util::slice_as_uninit, Error}; +use core::mem::{self, MaybeUninit}; + +cfg_if! { + if #[cfg(target_arch = "x86_64")] { + use core::arch::x86_64 as arch; + use arch::_rdrand64_step as rdrand_step; + } else if #[cfg(target_arch = "x86")] { + use core::arch::x86 as arch; + use arch::_rdrand32_step as rdrand_step; + } +} + +// Recommendation from "Intel® Digital Random Number Generator (DRNG) Software +// Implementation Guide" - Section 5.2.1 and "Intel® 64 and IA-32 Architectures +// Software Developer’s Manual" - Volume 1 - Section 7.3.17.1. +const RETRY_LIMIT: usize = 10; +const WORD_SIZE: usize = mem::size_of::(); + +#[target_feature(enable = "rdrand")] +unsafe fn rdrand() -> Result<[u8; WORD_SIZE], Error> { + for _ in 0..RETRY_LIMIT { + let mut el = mem::zeroed(); + if rdrand_step(&mut el) == 1 { + // AMD CPUs from families 14h to 16h (pre Ryzen) sometimes fail to + // set CF on bogus random data, so we check these values explicitly. + // See https://github.com/systemd/systemd/issues/11810#issuecomment-489727505 + // We perform this check regardless of target to guard against + // any implementation that incorrectly fails to set CF. + if el != 0 && el != !0 { + return Ok(el.to_ne_bytes()); + } + // Keep looping in case this was a false positive. + } + } + Err(Error::FAILED_RDRAND) +} + +// "rdrand" target feature requires "+rdrnd" flag, see https://github.com/rust-lang/rust/issues/49653. +#[cfg(all(target_env = "sgx", not(target_feature = "rdrand")))] +compile_error!( + "SGX targets require 'rdrand' target feature. Enable by using -C target-feature=+rdrnd." +); + +#[cfg(target_feature = "rdrand")] +fn is_rdrand_supported() -> bool { + true +} + +// TODO use is_x86_feature_detected!("rdrand") when that works in core. See: +// https://github.com/rust-lang-nursery/stdsimd/issues/464 +#[cfg(not(target_feature = "rdrand"))] +fn is_rdrand_supported() -> bool { + use crate::util::LazyBool; + + // SAFETY: All Rust x86 targets are new enough to have CPUID, and if CPUID + // is supported, CPUID leaf 1 is always supported. + const FLAG: u32 = 1 << 30; + static HAS_RDRAND: LazyBool = LazyBool::new(); + HAS_RDRAND.unsync_init(|| unsafe { (arch::__cpuid(1).ecx & FLAG) != 0 }) +} + +pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + if !is_rdrand_supported() { + return Err(Error::NO_RDRAND); + } + + // SAFETY: After this point, rdrand is supported, so calling the rdrand + // functions is not undefined behavior. + unsafe { rdrand_exact(dest) } +} + +#[target_feature(enable = "rdrand")] +unsafe fn rdrand_exact(dest: &mut [MaybeUninit]) -> Result<(), Error> { + // We use chunks_exact_mut instead of chunks_mut as it allows almost all + // calls to memcpy to be elided by the compiler. + let mut chunks = dest.chunks_exact_mut(WORD_SIZE); + for chunk in chunks.by_ref() { + chunk.copy_from_slice(slice_as_uninit(&rdrand()?)); + } + + let tail = chunks.into_remainder(); + let n = tail.len(); + if n > 0 { + tail.copy_from_slice(slice_as_uninit(&rdrand()?[..n])); + } + Ok(()) +} diff --git a/javascript-engine/external/getrandom/src/solaris_illumos.rs b/javascript-engine/external/getrandom/src/solaris_illumos.rs new file mode 100644 index 0000000..501c610 --- /dev/null +++ b/javascript-engine/external/getrandom/src/solaris_illumos.rs @@ -0,0 +1,49 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for the Solaris family +//! +//! `/dev/random` uses the Hash_DRBG with SHA512 algorithm from NIST SP 800-90A. +//! `/dev/urandom` uses the FIPS 186-2 algorithm, which is considered less +//! secure. We choose to read from `/dev/random` (and use GRND_RANDOM). +//! +//! Solaris 11.3 and late-2018 illumos added the getrandom(2) libc function. +//! To make sure we can compile on both Solaris and its derivatives, as well as +//! function, we check for the existence of getrandom(2) in libc by calling +//! libc::dlsym. +use crate::{ + use_file, + util_libc::{sys_fill_exact, Weak}, + Error, +}; +use core::mem::{self, MaybeUninit}; + +static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") }; +type GetRandomFn = + unsafe extern "C" fn(*mut libc::c_void, libc::size_t, libc::c_uint) -> libc::ssize_t; + +pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + if let Some(fptr) = GETRANDOM.ptr() { + let func: GetRandomFn = unsafe { mem::transmute(fptr) }; + // 256 bytes is the lowest common denominator across all the Solaris + // derived platforms for atomically obtaining random data. + for chunk in dest.chunks_mut(256) { + sys_fill_exact(chunk, |buf| unsafe { + // A cast is needed for the flags as libc uses the wrong type. + func( + buf.as_mut_ptr() as *mut libc::c_void, + buf.len(), + libc::GRND_RANDOM as libc::c_uint, + ) + })? + } + Ok(()) + } else { + use_file::getrandom_inner(dest) + } +} diff --git a/javascript-engine/external/getrandom/src/solid.rs b/javascript-engine/external/getrandom/src/solid.rs new file mode 100644 index 0000000..aeccc4e --- /dev/null +++ b/javascript-engine/external/getrandom/src/solid.rs @@ -0,0 +1,26 @@ +// Copyright 2021 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for SOLID +use crate::Error; +use core::{mem::MaybeUninit, num::NonZeroU32}; + +extern "C" { + pub fn SOLID_RNG_SampleRandomBytes(buffer: *mut u8, length: usize) -> i32; +} + +pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + let ret = unsafe { SOLID_RNG_SampleRandomBytes(dest.as_mut_ptr() as *mut u8, dest.len()) }; + if ret >= 0 { + Ok(()) + } else { + // ITRON error numbers are always negative, so we negate it so that it + // falls in the dedicated OS error range (1..INTERNAL_START). + Err(NonZeroU32::new((-ret) as u32).unwrap().into()) + } +} diff --git a/javascript-engine/external/getrandom/src/use_file.rs b/javascript-engine/external/getrandom/src/use_file.rs new file mode 100644 index 0000000..a6ef0d2 --- /dev/null +++ b/javascript-engine/external/getrandom/src/use_file.rs @@ -0,0 +1,138 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementations that just need to read from a file +use crate::{ + util::LazyUsize, + util_libc::{open_readonly, sys_fill_exact}, + Error, +}; +use core::{ + cell::UnsafeCell, + mem::MaybeUninit, + sync::atomic::{AtomicUsize, Ordering::Relaxed}, +}; + +// We prefer using /dev/urandom and only use /dev/random if the OS +// documentation indicates that /dev/urandom is insecure. +// On Solaris/Illumos, see src/solaris_illumos.rs +// On Dragonfly, Haiku, macOS, and QNX Neutrino the devices are identical. +#[cfg(any(target_os = "solaris", target_os = "illumos"))] +const FILE_PATH: &str = "/dev/random\0"; +#[cfg(any( + target_os = "aix", + target_os = "android", + target_os = "linux", + target_os = "redox", + target_os = "dragonfly", + target_os = "haiku", + target_os = "macos", + target_os = "nto", +))] +const FILE_PATH: &str = "/dev/urandom\0"; + +pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + let fd = get_rng_fd()?; + sys_fill_exact(dest, |buf| unsafe { + libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void, buf.len()) + }) +} + +// Returns the file descriptor for the device file used to retrieve random +// bytes. The file will be opened exactly once. All subsequent calls will +// return the same file descriptor. This file descriptor is never closed. +fn get_rng_fd() -> Result { + static FD: AtomicUsize = AtomicUsize::new(LazyUsize::UNINIT); + fn get_fd() -> Option { + match FD.load(Relaxed) { + LazyUsize::UNINIT => None, + val => Some(val as libc::c_int), + } + } + + // Use double-checked locking to avoid acquiring the lock if possible. + if let Some(fd) = get_fd() { + return Ok(fd); + } + + // SAFETY: We use the mutex only in this method, and we always unlock it + // before returning, making sure we don't violate the pthread_mutex_t API. + static MUTEX: Mutex = Mutex::new(); + unsafe { MUTEX.lock() }; + let _guard = DropGuard(|| unsafe { MUTEX.unlock() }); + + if let Some(fd) = get_fd() { + return Ok(fd); + } + + // On Linux, /dev/urandom might return insecure values. + #[cfg(any(target_os = "android", target_os = "linux"))] + wait_until_rng_ready()?; + + let fd = unsafe { open_readonly(FILE_PATH)? }; + // The fd always fits in a usize without conflicting with UNINIT. + debug_assert!(fd >= 0 && (fd as usize) < LazyUsize::UNINIT); + FD.store(fd as usize, Relaxed); + + Ok(fd) +} + +// Succeeds once /dev/urandom is safe to read from +#[cfg(any(target_os = "android", target_os = "linux"))] +fn wait_until_rng_ready() -> Result<(), Error> { + // Poll /dev/random to make sure it is ok to read from /dev/urandom. + let fd = unsafe { open_readonly("/dev/random\0")? }; + let mut pfd = libc::pollfd { + fd, + events: libc::POLLIN, + revents: 0, + }; + let _guard = DropGuard(|| unsafe { + libc::close(fd); + }); + + loop { + // A negative timeout means an infinite timeout. + let res = unsafe { libc::poll(&mut pfd, 1, -1) }; + if res >= 0 { + debug_assert_eq!(res, 1); // We only used one fd, and cannot timeout. + return Ok(()); + } + let err = crate::util_libc::last_os_error(); + match err.raw_os_error() { + Some(libc::EINTR) | Some(libc::EAGAIN) => continue, + _ => return Err(err), + } + } +} + +struct Mutex(UnsafeCell); + +impl Mutex { + const fn new() -> Self { + Self(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER)) + } + unsafe fn lock(&self) { + let r = libc::pthread_mutex_lock(self.0.get()); + debug_assert_eq!(r, 0); + } + unsafe fn unlock(&self) { + let r = libc::pthread_mutex_unlock(self.0.get()); + debug_assert_eq!(r, 0); + } +} + +unsafe impl Sync for Mutex {} + +struct DropGuard(F); + +impl Drop for DropGuard { + fn drop(&mut self) { + self.0() + } +} diff --git a/javascript-engine/external/getrandom/src/util.rs b/javascript-engine/external/getrandom/src/util.rs new file mode 100644 index 0000000..3162afa --- /dev/null +++ b/javascript-engine/external/getrandom/src/util.rs @@ -0,0 +1,101 @@ +// Copyright 2019 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![allow(dead_code)] +use core::{ + mem::MaybeUninit, + ptr, + sync::atomic::{AtomicUsize, Ordering::Relaxed}, +}; + +// This structure represents a lazily initialized static usize value. Useful +// when it is preferable to just rerun initialization instead of locking. +// Both unsync_init and sync_init will invoke an init() function until it +// succeeds, then return the cached value for future calls. +// +// Both methods support init() "failing". If the init() method returns UNINIT, +// that value will be returned as normal, but will not be cached. +// +// Users should only depend on the _value_ returned by init() functions. +// Specifically, for the following init() function: +// fn init() -> usize { +// a(); +// let v = b(); +// c(); +// v +// } +// the effects of c() or writes to shared memory will not necessarily be +// observed and additional synchronization methods with be needed. +pub struct LazyUsize(AtomicUsize); + +impl LazyUsize { + pub const fn new() -> Self { + Self(AtomicUsize::new(Self::UNINIT)) + } + + // The initialization is not completed. + pub const UNINIT: usize = usize::max_value(); + + // Runs the init() function at least once, returning the value of some run + // of init(). Multiple callers can run their init() functions in parallel. + // init() should always return the same value, if it succeeds. + pub fn unsync_init(&self, init: impl FnOnce() -> usize) -> usize { + // Relaxed ordering is fine, as we only have a single atomic variable. + let mut val = self.0.load(Relaxed); + if val == Self::UNINIT { + val = init(); + self.0.store(val, Relaxed); + } + val + } +} + +// Identical to LazyUsize except with bool instead of usize. +pub struct LazyBool(LazyUsize); + +impl LazyBool { + pub const fn new() -> Self { + Self(LazyUsize::new()) + } + + pub fn unsync_init(&self, init: impl FnOnce() -> bool) -> bool { + self.0.unsync_init(|| init() as usize) != 0 + } +} + +/// Polyfill for `maybe_uninit_slice` feature's +/// `MaybeUninit::slice_assume_init_mut`. Every element of `slice` must have +/// been initialized. +#[inline(always)] +pub unsafe fn slice_assume_init_mut(slice: &mut [MaybeUninit]) -> &mut [T] { + // SAFETY: `MaybeUninit` is guaranteed to be layout-compatible with `T`. + &mut *(slice as *mut [MaybeUninit] as *mut [T]) +} + +#[inline] +pub fn uninit_slice_fill_zero(slice: &mut [MaybeUninit]) -> &mut [u8] { + unsafe { ptr::write_bytes(slice.as_mut_ptr(), 0, slice.len()) }; + unsafe { slice_assume_init_mut(slice) } +} + +#[inline(always)] +pub fn slice_as_uninit(slice: &[T]) -> &[MaybeUninit] { + // SAFETY: `MaybeUninit` is guaranteed to be layout-compatible with `T`. + // There is no risk of writing a `MaybeUninit` into the result since + // the result isn't mutable. + unsafe { &*(slice as *const [T] as *const [MaybeUninit]) } +} + +/// View an mutable initialized array as potentially-uninitialized. +/// +/// This is unsafe because it allows assigning uninitialized values into +/// `slice`, which would be undefined behavior. +#[inline(always)] +pub unsafe fn slice_as_uninit_mut(slice: &mut [T]) -> &mut [MaybeUninit] { + // SAFETY: `MaybeUninit` is guaranteed to be layout-compatible with `T`. + &mut *(slice as *mut [T] as *mut [MaybeUninit]) +} diff --git a/javascript-engine/external/getrandom/src/util_libc.rs b/javascript-engine/external/getrandom/src/util_libc.rs new file mode 100644 index 0000000..bd9c7de --- /dev/null +++ b/javascript-engine/external/getrandom/src/util_libc.rs @@ -0,0 +1,157 @@ +// Copyright 2019 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![allow(dead_code)] +use crate::Error; +use core::{ + mem::MaybeUninit, + num::NonZeroU32, + ptr::NonNull, + sync::atomic::{fence, AtomicPtr, Ordering}, +}; +use libc::c_void; + +cfg_if! { + if #[cfg(any(target_os = "netbsd", target_os = "openbsd", target_os = "android"))] { + use libc::__errno as errno_location; + } else if #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "redox"))] { + use libc::__errno_location as errno_location; + } else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] { + use libc::___errno as errno_location; + } else if #[cfg(any(target_os = "macos", target_os = "freebsd"))] { + use libc::__error as errno_location; + } else if #[cfg(target_os = "haiku")] { + use libc::_errnop as errno_location; + } else if #[cfg(target_os = "nto")] { + use libc::__get_errno_ptr as errno_location; + } else if #[cfg(all(target_os = "horizon", target_arch = "arm"))] { + extern "C" { + // Not provided by libc: https://github.com/rust-lang/libc/issues/1995 + fn __errno() -> *mut libc::c_int; + } + use __errno as errno_location; + } else if #[cfg(target_os = "aix")] { + use libc::_Errno as errno_location; + } +} + +cfg_if! { + if #[cfg(target_os = "vxworks")] { + use libc::errnoGet as get_errno; + } else if #[cfg(target_os = "dragonfly")] { + // Until rust-lang/rust#29594 is stable, we cannot get the errno value + // on DragonFlyBSD. So we just return an out-of-range errno. + unsafe fn get_errno() -> libc::c_int { -1 } + } else { + unsafe fn get_errno() -> libc::c_int { *errno_location() } + } +} + +pub fn last_os_error() -> Error { + let errno = unsafe { get_errno() }; + if errno > 0 { + Error::from(NonZeroU32::new(errno as u32).unwrap()) + } else { + Error::ERRNO_NOT_POSITIVE + } +} + +// Fill a buffer by repeatedly invoking a system call. The `sys_fill` function: +// - should return -1 and set errno on failure +// - should return the number of bytes written on success +pub fn sys_fill_exact( + mut buf: &mut [MaybeUninit], + sys_fill: impl Fn(&mut [MaybeUninit]) -> libc::ssize_t, +) -> Result<(), Error> { + while !buf.is_empty() { + let res = sys_fill(buf); + if res < 0 { + let err = last_os_error(); + // We should try again if the call was interrupted. + if err.raw_os_error() != Some(libc::EINTR) { + return Err(err); + } + } else { + // We don't check for EOF (ret = 0) as the data we are reading + // should be an infinite stream of random bytes. + buf = &mut buf[(res as usize)..]; + } + } + Ok(()) +} + +// A "weak" binding to a C function that may or may not be present at runtime. +// Used for supporting newer OS features while still building on older systems. +// Based off of the DlsymWeak struct in libstd: +// https://github.com/rust-lang/rust/blob/1.61.0/library/std/src/sys/unix/weak.rs#L84 +// except that the caller must manually cast self.ptr() to a function pointer. +pub struct Weak { + name: &'static str, + addr: AtomicPtr, +} + +impl Weak { + // A non-null pointer value which indicates we are uninitialized. This + // constant should ideally not be a valid address of a function pointer. + // However, if by chance libc::dlsym does return UNINIT, there will not + // be undefined behavior. libc::dlsym will just be called each time ptr() + // is called. This would be inefficient, but correct. + // TODO: Replace with core::ptr::invalid_mut(1) when that is stable. + const UNINIT: *mut c_void = 1 as *mut c_void; + + // Construct a binding to a C function with a given name. This function is + // unsafe because `name` _must_ be null terminated. + pub const unsafe fn new(name: &'static str) -> Self { + Self { + name, + addr: AtomicPtr::new(Self::UNINIT), + } + } + + // Return the address of a function if present at runtime. Otherwise, + // return None. Multiple callers can call ptr() concurrently. It will + // always return _some_ value returned by libc::dlsym. However, the + // dlsym function may be called multiple times. + pub fn ptr(&self) -> Option> { + // Despite having only a single atomic variable (self.addr), we still + // cannot always use Ordering::Relaxed, as we need to make sure a + // successful call to dlsym() is "ordered before" any data read through + // the returned pointer (which occurs when the function is called). + // Our implementation mirrors that of the one in libstd, meaning that + // the use of non-Relaxed operations is probably unnecessary. + match self.addr.load(Ordering::Relaxed) { + Self::UNINIT => { + let symbol = self.name.as_ptr() as *const _; + let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, symbol) }; + // Synchronizes with the Acquire fence below + self.addr.store(addr, Ordering::Release); + NonNull::new(addr) + } + addr => { + let func = NonNull::new(addr)?; + fence(Ordering::Acquire); + Some(func) + } + } + } +} + +// SAFETY: path must be null terminated, FD must be manually closed. +pub unsafe fn open_readonly(path: &str) -> Result { + debug_assert_eq!(path.as_bytes().last(), Some(&0)); + loop { + let fd = libc::open(path.as_ptr() as *const _, libc::O_RDONLY | libc::O_CLOEXEC); + if fd >= 0 { + return Ok(fd); + } + let err = last_os_error(); + // We should try again if open() was interrupted. + if err.raw_os_error() != Some(libc::EINTR) { + return Err(err); + } + } +} diff --git a/javascript-engine/external/getrandom/src/vxworks.rs b/javascript-engine/external/getrandom/src/vxworks.rs new file mode 100644 index 0000000..9b2090f --- /dev/null +++ b/javascript-engine/external/getrandom/src/vxworks.rs @@ -0,0 +1,37 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for VxWorks +use crate::{util_libc::last_os_error, Error}; +use core::{ + mem::MaybeUninit, + sync::atomic::{AtomicBool, Ordering::Relaxed}, +}; + +pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + static RNG_INIT: AtomicBool = AtomicBool::new(false); + while !RNG_INIT.load(Relaxed) { + let ret = unsafe { libc::randSecure() }; + if ret < 0 { + return Err(Error::VXWORKS_RAND_SECURE); + } else if ret > 0 { + RNG_INIT.store(true, Relaxed); + break; + } + unsafe { libc::usleep(10) }; + } + + // Prevent overflow of i32 + for chunk in dest.chunks_mut(i32::max_value() as usize) { + let ret = unsafe { libc::randABytes(chunk.as_mut_ptr() as *mut u8, chunk.len() as i32) }; + if ret != 0 { + return Err(last_os_error()); + } + } + Ok(()) +} diff --git a/javascript-engine/external/getrandom/src/wasi.rs b/javascript-engine/external/getrandom/src/wasi.rs new file mode 100644 index 0000000..9276ee7 --- /dev/null +++ b/javascript-engine/external/getrandom/src/wasi.rs @@ -0,0 +1,25 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for WASI +use crate::Error; +use core::{ + mem::MaybeUninit, + num::{NonZeroU16, NonZeroU32}, +}; +use wasi::random_get; + +pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + unsafe { random_get(dest.as_mut_ptr() as *mut u8, dest.len()) }.map_err(|e| { + // The WASI errno will always be non-zero, but we check just in case. + match NonZeroU16::new(e.raw()) { + Some(r) => Error::from(NonZeroU32::from(r)), + None => Error::ERRNO_NOT_POSITIVE, + } + }) +} diff --git a/javascript-engine/external/getrandom/src/windows.rs b/javascript-engine/external/getrandom/src/windows.rs new file mode 100644 index 0000000..e5a626c --- /dev/null +++ b/javascript-engine/external/getrandom/src/windows.rs @@ -0,0 +1,49 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::Error; +use core::{ffi::c_void, mem::MaybeUninit, num::NonZeroU32, ptr}; + +const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002; + +#[link(name = "bcrypt")] +extern "system" { + fn BCryptGenRandom( + hAlgorithm: *mut c_void, + pBuffer: *mut u8, + cbBuffer: u32, + dwFlags: u32, + ) -> u32; +} + +pub fn getrandom_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + // Prevent overflow of u32 + for chunk in dest.chunks_mut(u32::max_value() as usize) { + // BCryptGenRandom was introduced in Windows Vista + let ret = unsafe { + BCryptGenRandom( + ptr::null_mut(), + chunk.as_mut_ptr() as *mut u8, + chunk.len() as u32, + BCRYPT_USE_SYSTEM_PREFERRED_RNG, + ) + }; + // NTSTATUS codes use the two highest bits for severity status. + if ret >> 30 == 0b11 { + // We zeroize the highest bit, so the error code will reside + // inside the range designated for OS codes. + let code = ret ^ (1 << 31); + // SAFETY: the second highest bit is always equal to one, + // so it's impossible to get zero. Unfortunately the type + // system does not have a way to express this yet. + let code = unsafe { NonZeroU32::new_unchecked(code) }; + return Err(Error::from(code)); + } + } + Ok(()) +} diff --git a/javascript-engine/external/getrandom/tests/common/mod.rs b/javascript-engine/external/getrandom/tests/common/mod.rs new file mode 100644 index 0000000..666f7f5 --- /dev/null +++ b/javascript-engine/external/getrandom/tests/common/mod.rs @@ -0,0 +1,100 @@ +use super::getrandom_impl; + +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +use wasm_bindgen_test::wasm_bindgen_test as test; + +#[cfg(feature = "test-in-browser")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + +#[test] +fn test_zero() { + // Test that APIs are happy with zero-length requests + getrandom_impl(&mut [0u8; 0]).unwrap(); +} + +// Return the number of bits in which s1 and s2 differ +#[cfg(not(feature = "custom"))] +fn num_diff_bits(s1: &[u8], s2: &[u8]) -> usize { + assert_eq!(s1.len(), s2.len()); + s1.iter() + .zip(s2.iter()) + .map(|(a, b)| (a ^ b).count_ones() as usize) + .sum() +} + +// Tests the quality of calling getrandom on two large buffers +#[test] +#[cfg(not(feature = "custom"))] +fn test_diff() { + let mut v1 = [0u8; 1000]; + getrandom_impl(&mut v1).unwrap(); + + let mut v2 = [0u8; 1000]; + getrandom_impl(&mut v2).unwrap(); + + // Between 3.5 and 4.5 bits per byte should differ. Probability of failure: + // ~ 2^(-94) = 2 * CDF[BinomialDistribution[8000, 0.5], 3500] + let d = num_diff_bits(&v1, &v2); + assert!(d > 3500); + assert!(d < 4500); +} + +// Tests the quality of calling getrandom repeatedly on small buffers +#[test] +#[cfg(not(feature = "custom"))] +fn test_small() { + // For each buffer size, get at least 256 bytes and check that between + // 3 and 5 bits per byte differ. Probability of failure: + // ~ 2^(-91) = 64 * 2 * CDF[BinomialDistribution[8*256, 0.5], 3*256] + for size in 1..=64 { + let mut num_bytes = 0; + let mut diff_bits = 0; + while num_bytes < 256 { + let mut s1 = vec![0u8; size]; + getrandom_impl(&mut s1).unwrap(); + let mut s2 = vec![0u8; size]; + getrandom_impl(&mut s2).unwrap(); + + num_bytes += size; + diff_bits += num_diff_bits(&s1, &s2); + } + assert!(diff_bits > 3 * num_bytes); + assert!(diff_bits < 5 * num_bytes); + } +} + +#[test] +fn test_huge() { + let mut huge = [0u8; 100_000]; + getrandom_impl(&mut huge).unwrap(); +} + +// On WASM, the thread API always fails/panics +#[cfg(not(target_arch = "wasm32"))] +#[test] +fn test_multithreading() { + extern crate std; + use std::{sync::mpsc::channel, thread, vec}; + + let mut txs = vec![]; + for _ in 0..20 { + let (tx, rx) = channel(); + txs.push(tx); + + thread::spawn(move || { + // wait until all the tasks are ready to go. + rx.recv().unwrap(); + let mut v = [0u8; 1000]; + + for _ in 0..100 { + getrandom_impl(&mut v).unwrap(); + thread::yield_now(); + } + }); + } + + // start all the tasks + for tx in txs.iter() { + tx.send(()).unwrap(); + } +} diff --git a/javascript-engine/external/getrandom/tests/custom.rs b/javascript-engine/external/getrandom/tests/custom.rs new file mode 100644 index 0000000..b085094 --- /dev/null +++ b/javascript-engine/external/getrandom/tests/custom.rs @@ -0,0 +1,54 @@ +// Test that a custom handler works on wasm32-unknown-unknown +#![cfg(all( + target_arch = "wasm32", + target_os = "unknown", + feature = "custom", + not(feature = "js") +))] + +use wasm_bindgen_test::wasm_bindgen_test as test; + +use core::num::NonZeroU32; +use getrandom::{getrandom, register_custom_getrandom, Error}; + +fn len7_err() -> Error { + NonZeroU32::new(Error::INTERNAL_START + 7).unwrap().into() +} + +fn super_insecure_rng(buf: &mut [u8]) -> Result<(), Error> { + // `getrandom` guarantees it will not call any implementation if the output + // buffer is empty. + assert!(!buf.is_empty()); + // Length 7 buffers return a custom error + if buf.len() == 7 { + return Err(len7_err()); + } + // Otherwise, fill bytes based on input length + let mut start = buf.len() as u8; + for b in buf { + *b = start; + start = start.wrapping_mul(3); + } + Ok(()) +} + +register_custom_getrandom!(super_insecure_rng); + +use getrandom::getrandom as getrandom_impl; +mod common; + +#[test] +fn custom_rng_output() { + let mut buf = [0u8; 4]; + assert_eq!(getrandom(&mut buf), Ok(())); + assert_eq!(buf, [4, 12, 36, 108]); + + let mut buf = [0u8; 3]; + assert_eq!(getrandom(&mut buf), Ok(())); + assert_eq!(buf, [3, 9, 27]); +} + +#[test] +fn rng_err_output() { + assert_eq!(getrandom(&mut [0; 7]), Err(len7_err())); +} diff --git a/javascript-engine/external/getrandom/tests/normal.rs b/javascript-engine/external/getrandom/tests/normal.rs new file mode 100644 index 0000000..5fff13b --- /dev/null +++ b/javascript-engine/external/getrandom/tests/normal.rs @@ -0,0 +1,11 @@ +// Don't test on custom wasm32-unknown-unknown +#![cfg(not(all( + target_arch = "wasm32", + target_os = "unknown", + feature = "custom", + not(feature = "js") +)))] + +// Use the normal getrandom implementation on this architecture. +use getrandom::getrandom as getrandom_impl; +mod common; diff --git a/javascript-engine/external/getrandom/tests/rdrand.rs b/javascript-engine/external/getrandom/tests/rdrand.rs new file mode 100644 index 0000000..2567868 --- /dev/null +++ b/javascript-engine/external/getrandom/tests/rdrand.rs @@ -0,0 +1,20 @@ +// We only test the RDRAND-based RNG source on supported architectures. +#![cfg(any(target_arch = "x86_64", target_arch = "x86"))] + +// rdrand.rs expects to be part of the getrandom main crate, so we need these +// additional imports to get rdrand.rs to compile. +use getrandom::Error; +#[macro_use] +extern crate cfg_if; +#[path = "../src/rdrand.rs"] +mod rdrand; +#[path = "../src/util.rs"] +mod util; + +// The rdrand implementation has the signature of getrandom_uninit(), but our +// tests expect getrandom_impl() to have the signature of getrandom(). +fn getrandom_impl(dest: &mut [u8]) -> Result<(), Error> { + rdrand::getrandom_inner(unsafe { util::slice_as_uninit_mut(dest) })?; + Ok(()) +} +mod common; diff --git a/javascript-engine/external/iana-time-zone/.clang-format b/javascript-engine/external/iana-time-zone/.clang-format new file mode 100644 index 0000000..444c17f --- /dev/null +++ b/javascript-engine/external/iana-time-zone/.clang-format @@ -0,0 +1,9 @@ +--- +BasedOnStyle: Google +IndentWidth: 4 +ColumnLimit: 100 +--- +Language: Cpp +SortIncludes: false +TabWidth: 4 +UseTab: Never diff --git a/javascript-engine/external/iana-time-zone/.github/dependabot.yml b/javascript-engine/external/iana-time-zone/.github/dependabot.yml new file mode 100644 index 0000000..5d6d689 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: +- package-ecosystem: cargo + directory: "/" + schedule: + interval: daily + time: "04:00" + open-pull-requests-limit: 10 + ignore: + - dependency-name: winrt + versions: + - 0.8.0 diff --git a/javascript-engine/external/iana-time-zone/.github/workflows/rust.yml b/javascript-engine/external/iana-time-zone/.github/workflows/rust.yml new file mode 100644 index 0000000..b91b4f5 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/.github/workflows/rust.yml @@ -0,0 +1,383 @@ +name: build + +on: + push: + branches: + - main + pull_request: + branches: [ '**' ] + schedule: + # At 23:25 on Thursday. + - cron: "25 23 * * 4" + +jobs: + test: + strategy: + fail-fast: false + matrix: + runs-on: [ubuntu-20.04, windows-2022, macos-12] + toolchain: + - "1.48" + - stable + - nightly + versions: + - "" + - "-Zminimal-versions" + runs-on: ${{ matrix.runs-on }} + steps: + - name: Checkout + uses: actions/checkout@v3 + - uses: maxim-lobanov/setup-xcode@v1 + if: matrix.runs-on == 'macos-12' && matrix.toolchain == '1.48' + with: + xcode-version: "13.4.1" + - name: Install Rust + id: actions-rs + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.toolchain }} + override: true + - name: Update lockfile + run: cargo generate-lockfile ${{ matrix.versions }} + env: + RUSTC_BOOTSTRAP: 1 + - run: cargo test --all-targets + - run: cargo run --example stress-test + + build-wasm: + strategy: + fail-fast: false + matrix: + versions: + - "" + - "-Zminimal-versions" + toolchain: + - stable + - nightly + include: + # Without `-Zminimal-versions` a too recent bumpalo version is selected. Newer versions use `edition = "2021"`. + - versions: "-Zminimal-versions" + - toolchain: "1.48" + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Rust + id: actions-rs + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.toolchain }} + override: true + target: wasm32-unknown-unknown + - name: Update lockfile + run: cargo generate-lockfile ${{ matrix.versions }} + env: + RUSTC_BOOTSTRAP: 1 + - run: cargo build --lib + + test-wasm: + strategy: + fail-fast: false + matrix: + versions: + - "" + - "-Zminimal-versions" + toolchain: + - stable + - nightly + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Rust + id: actions-rs + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.toolchain }} + override: true + target: wasm32-unknown-unknown + - name: Install Node + uses: actions/setup-node@v3 + with: + node-version: 14 + - name: Update lockfile + run: cargo generate-lockfile ${{ matrix.versions }} + env: + RUSTC_BOOTSTRAP: 1 + - run: which wasm-pack || cargo +stable install wasm-pack + - run: wasm-pack test --node + + build-cross: + strategy: + fail-fast: false + matrix: + target: + - x86_64-unknown-freebsd + - x86_64-unknown-illumos + - x86_64-unknown-netbsd + - x86_64-linux-android + - i686-linux-android + - arm-linux-androideabi + - aarch64-linux-android + - sparcv9-sun-solaris + versions: + - "" + - "-Zminimal-versions" + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Rust + id: actions-rs + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + target: ${{ matrix.target }} + - name: Update lockfile + run: cargo generate-lockfile ${{ matrix.versions }} + env: + RUSTC_BOOTSTRAP: 1 + + # The command is derived from `cargo-quickinstall`'s description: + # https://github.com/cargo-bins/cargo-quickinstall/blob/49f09436/README.md#use-in-ci-systems + # + # I.e. Download the file into STDOUT, follow redirects, don't give status reports, except + # if there are HTTP errors, and in case of HTTP errors don't pipe out anything at all. + # Extract everything into Cargo's `cargo install` destination, which is in your $PATH already. + # + # Updated pre-compiled binaries can be found in + # by searching for their project name. + - name: Install "cross" + run: curl --location --silent --show-error --fail https://github.com/cargo-bins/cargo-quickinstall/releases/download/cross-0.2.4-x86_64-unknown-linux-gnu/cross-0.2.4-x86_64-unknown-linux-gnu.tar.gz | tar -xzvvf - -C $HOME/.cargo/bin + + - run: cross build --target ${{ matrix.target }} --examples + + build-ios-cross: + strategy: + fail-fast: false + matrix: + toolchain: + - "1.48" + - stable + target: + - aarch64-apple-ios-sim + - aarch64-apple-ios + - x86_64-apple-ios + versions: + - "" + - "-Zminimal-versions" + exclude: + # Support for this target was added quite recently. + - target: aarch64-apple-ios-sim + - toolchain: "1.48" + runs-on: macos-12 + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Rust + id: actions-rs + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.toolchain }} + override: true + target: ${{ matrix.target }} + - name: Update lockfile + run: cargo generate-lockfile ${{ matrix.versions }} + env: + RUSTC_BOOTSTRAP: 1 + - name: Install "cross" + run: curl --location --silent --show-error --fail https://github.com/cargo-bins/cargo-quickinstall/releases/download/cross-0.2.4-x86_64-apple-darwin/cross-0.2.4-x86_64-apple-darwin.tar.gz | tar -xzvvf - -C $HOME/.cargo/bin + - run: cross build --target ${{ matrix.target }} --examples + + check: + strategy: + fail-fast: false + matrix: + toolchain: + - "1.48" + - stable + - nightly + versions: + - "" + - "-Zminimal-versions" + runs-on: ubuntu-latest + env: + RUSTFLAGS: -D warnings + RUST_BACKTRACE: 1 + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Rust + id: actions-rs + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.toolchain }} + override: true + components: clippy + - name: Update lockfile + run: cargo generate-lockfile ${{ matrix.versions }} + env: + RUSTC_BOOTSTRAP: 1 + - run: cargo check --all-targets + - run: cargo clippy --all-targets + + no-docker-image-check-only: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Rust + id: actions-rs + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true + components: rust-src + - run: cargo +nightly check --target x86_64-unknown-haiku -Z build-std --examples + + doc: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Rust + id: actions-rs + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true + - run: RUSTDOCFLAGS="-D warnings" cargo doc --all-features + + audit: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Audit + uses: actions-rs/audit-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + fallback: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Rust + id: actions-rs + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + target: x86_64-fortanix-unknown-sgx + override: true + # Should fail (outcome is negated): + - run: if cargo build --lib --target x86_64-fortanix-unknown-sgx; then exit 1; fi + # Should succeed: + - run: cargo build --lib --target x86_64-fortanix-unknown-sgx --features fallback + + c: + name: Lint and format C + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Setup Node.js runtime + uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Install npm + run: npm i -f -g npm@8.16.0 + + - name: Lint and check formatting with clang-format + run: npx github:artichoke/clang-format --check + + test-haiku: + strategy: + fail-fast: false + matrix: + runs-on: [ubuntu-20.04, windows-2022, macos-12] + toolchain: + - stable + versions: + - "" + runs-on: ${{ matrix.runs-on }} + env: + RUSTFLAGS: -D warnings + RUST_BACKTRACE: 1 + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Rust + id: actions-rs + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.toolchain }} + override: true + - name: Update lockfile + run: cargo generate-lockfile ${{ matrix.versions }} + working-directory: haiku + env: + RUSTC_BOOTSTRAP: 1 + - run: cargo test --all-targets + working-directory: haiku + + check-haiku: + strategy: + fail-fast: false + matrix: + toolchain: + - stable + versions: + - "" + runs-on: ubuntu-latest + env: + RUSTFLAGS: -D warnings + RUST_BACKTRACE: 1 + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Rust + id: actions-rs + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.toolchain }} + override: true + components: clippy + - name: Update lockfile + run: cargo generate-lockfile ${{ matrix.versions }} + working-directory: haiku + env: + RUSTC_BOOTSTRAP: 1 + - run: cargo check --all-targets + working-directory: haiku + - run: cargo clippy --all-targets + working-directory: haiku + + check-all-versions: + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Rust + id: actions-rs + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - run: which cargo-hack || cargo +stable install cargo-hack + - run: cargo hack check --version-range 1.36.. diff --git a/javascript-engine/external/iana-time-zone/.gitignore b/javascript-engine/external/iana-time-zone/.gitignore new file mode 100644 index 0000000..a9d37c5 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/javascript-engine/external/iana-time-zone/CHANGELOG.md b/javascript-engine/external/iana-time-zone/CHANGELOG.md new file mode 100644 index 0000000..b00eb4e --- /dev/null +++ b/javascript-engine/external/iana-time-zone/CHANGELOG.md @@ -0,0 +1,297 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.1.54] - 2022-12-21 +### Changed +- replace `winapi` dependency with `windows-sys` ([#91](https://github.com/strawlab/iana-time-zone/pull/91)) +- bump msrv to 1.48 ([#91](https://github.com/strawlab/iana-time-zone/pull/91)) + +## [0.1.53] - 2022-10-28 +### Fixed +- remove lint causing breakage on rust 1.45-1.51 ([#84](https://github.com/strawlab/iana-time-zone/pull/84)) + +## [0.1.52] - 2022-10-28 +### Fixed +- fix for NixOS ([#81](https://github.com/strawlab/iana-time-zone/pull/81)) + +### Changed +- allow building the haiku crate on other hosts([#75](https://github.com/strawlab/iana-time-zone/pull/75)) +- various improvements in continuous integration and source quality + ([#76](https://github.com/strawlab/iana-time-zone/pull/76)), + ([#77](https://github.com/strawlab/iana-time-zone/pull/77)), + ([#78](https://github.com/strawlab/iana-time-zone/pull/78)), + ([#81](https://github.com/strawlab/iana-time-zone/pull/81)) + +## [0.1.51] - 2022-10-08 +### Changed +- bump MSRV to 1.38 ([#70](https://github.com/strawlab/iana-time-zone/pull/70)) +- Refactor Android property key CStr construction to add tests ([#69](https://github.com/strawlab/iana-time-zone/pull/69)) +- Refactor MacOS implementation a lot ([#67](https://github.com/strawlab/iana-time-zone/pull/67)) + +### Added +- Implement for Haiku ([#66](https://github.com/strawlab/iana-time-zone/pull/66)) + +### Fixed +- Fix spelling of 'initialized' in sync::Once statics ([#63](https://github.com/strawlab/iana-time-zone/pull/63)) + +## [0.1.50] - 2022-09-23 +### Fixed +- Reduce MSRV for Android again ([#62](https://github.com/strawlab/iana-time-zone/pull/62)) + +## [0.1.49] - 2022-09-22 +### Changed +- `once_cell` dependency is not needed ([#61](https://github.com/strawlab/iana-time-zone/pull/61)) + +## [0.1.48] - 2022-09-12 +### Changed +- Downgrade requirements for WASM dependencies ([#58](https://github.com/strawlab/iana-time-zone/pull/58)) +- Reduce MSRV for Tier 1 platforms to 1.31 ([#59](https://github.com/strawlab/iana-time-zone/pull/59)) + +## [0.1.47] - 2022-08-30 +### Changed +- Update `android_system_properties` to v0.1.5 to run 9786% faster (YMMV) ([#56](https://github.com/strawlab/iana-time-zone/pull/56)) + +## [0.1.46] - 2022-08-18 +### Added +- Implement for Solaris ([#55](https://github.com/strawlab/iana-time-zone/pull/55)) + +## [0.1.45] - 2022-08-16 +### Fixed +- Fix potential use after free in MacOS / iOS ([#54](https://github.com/strawlab/iana-time-zone/pull/54), [RUSTSEC-2022-0049](https://rustsec.org/advisories/RUSTSEC-2022-0049.html)) +- Fix typos in README ([#53](https://github.com/strawlab/iana-time-zone/pull/53)) + +## [0.1.44] - 2022-08-11 +### Fixed +- "/etc/localtime" may be relative link ([#49](https://github.com/strawlab/iana-time-zone/pull/49)) + +## [0.1.43] - 2022-08-11 +### Changed +- Use `core-foundation-sys` instead of `core-foundation` ([#50](https://github.com/strawlab/iana-time-zone/pull/50)) + +## [0.1.42] - 2022-08-10 +### Fixed +- Fix implementation for Redhat based distros ([#48](https://github.com/strawlab/iana-time-zone/pull/48)) + +## [0.1.41] - 2022-08-02 +### Added +- Add `fallback` feature ([#46](https://github.com/strawlab/iana-time-zone/pull/46)) + +## [0.1.40] - 2022-07-29 +### Added +- Implement for Android ([#45](https://github.com/strawlab/iana-time-zone/pull/45)) + +## [0.1.38] - 2022-07-27 +### Added +- Implement illumos ([#44](https://github.com/strawlab/iana-time-zone/pull/44)) +### Changed +- Update examples in README + +## [0.1.37] - 2022-07-23 +### Added +- Support iOS ([#41](https://github.com/strawlab/iana-time-zone/pull/41)) +### Changed +- Implement `std::err::source()`, format `IoError` ([#42](https://github.com/strawlab/iana-time-zone/pull/42)) + +## [0.1.36] - 2022-07-21 +### Fixed +- Fail to compile for WASI ([#40](https://github.com/strawlab/iana-time-zone/pull/40)) + +## [0.1.35] - 2022-06-29 +### Added +- Implement for FreeBSD, NetBSD, OpenBSD and Dragonfly ([#39](https://github.com/strawlab/iana-time-zone/pull/39)) + +## [0.1.34] - 2022-06-29 +### Added +- Implement for wasm32 ([#38](https://github.com/strawlab/iana-time-zone/pull/38)) + +## [0.1.33] - 2022-04-15 +### Changed +- Use `winapi` crate instead of `windows` crate ([#35](https://github.com/strawlab/iana-time-zone/pull/35)) + +## [0.1.32] - 2022-04-06 +### Changed +- Update `windows` requirement from 0.34 to 0.35 ([#34](https://github.com/strawlab/iana-time-zone/pull/34)) + +## [0.1.31] - 2022-03-16 +### Changed +- Update `windows` requirement from 0.33 to 0.34 ([#33](https://github.com/strawlab/iana-time-zone/pull/33)) + +## [0.1.30] - 2022-02-28 +### Changed +- Fewer string allocations ([#32](https://github.com/strawlab/iana-time-zone/pull/32)) + +## [0.1.29] - 2022-02-25 +### Changed +- Update `windows` requirement from 0.32 to 0.33 ([#31](https://github.com/strawlab/iana-time-zone/pull/31)) + +## [0.1.28] - 2022-02-04 +### Changed +- Update `windows` requirement from 0.30 to 0.32 ([#30](https://github.com/strawlab/iana-time-zone/pull/30)) + +## [0.1.27] - 2022-01-14 +### Changed +- Update `windows` requirement from 0.29 to 0.30 ([#29](https://github.com/strawlab/iana-time-zone/pull/29)) + +## [0.1.26] - 2021-12-23 +### Changed +- Update `windows` requirement from 0.28 to 0.29 ([#28](https://github.com/strawlab/iana-time-zone/pull/28)) + +## [0.1.25] - 2021-11-18 +### Changed +- Update `windows` requirement from 0.27 to 0.28 ([#27](https://github.com/strawlab/iana-time-zone/pull/27)) + +## [0.1.24] - 2021-11-16 +### Changed +- Update `windows` requirement from 0.26 to 0.27 ([#26](https://github.com/strawlab/iana-time-zone/pull/26)) + +## [0.1.23] - 2021-11-12 +### Changed +- Update `windows` requirement from 0.25 to 0.26 ([#25](https://github.com/strawlab/iana-time-zone/pull/25)) + +## [0.1.22] - 2021-11-08 +### Changed +- Update `windows` requirement from 0.24 to 0.25 ([#24](https://github.com/strawlab/iana-time-zone/pull/24)) + +## [0.1.21] - 2021-11-02 +### Changed +- Update `windows` requirement from 0.23 to 0.24 ([#23](https://github.com/strawlab/iana-time-zone/pull/23)) + +## [0.1.20] - 2021-10-29 +### Changed +- Update `windows` requirement from 0.21 to 0.23 ([#22](https://github.com/strawlab/iana-time-zone/pull/22)) + +## [0.1.19] - 2021-09-27 +### Changed +- Update `windows` requirement from 0.19 to 0.21 ([#18](https://github.com/strawlab/iana-time-zone/pull/18), [#20](https://github.com/strawlab/iana-time-zone/pull/20)) +- Update `chrono-tz` requirement from 0.5 to 0.6 ([#19](https://github.com/strawlab/iana-time-zone/pull/19)) + +## [0.1.18] - 2021-08-23 +### Changed +- Update `windows` requirement from 0.18 to 0.19 ([#17](https://github.com/strawlab/iana-time-zone/pull/17)) + +## [0.1.16] - 2021-07-26 +### Changed +- Update `windows` requirement from 0.17 to 0.18 ([#16](https://github.com/strawlab/iana-time-zone/pull/16)) + +## [0.1.15] - 2021-07-08 +### Changed +- Update `windows` requirement from 0.14 to 0.17 ([#15](https://github.com/strawlab/iana-time-zone/pull/15)) + +## [0.1.14] - 2021-07-07 +### Changed +- Update `windows` requirement from 0.13 to 0.14 ([#14](https://github.com/strawlab/iana-time-zone/pull/14)) + +## [0.1.13] - 2021-06-28 +### Changed +- Update `windows` requirement from 0.12 to 0.13 ([#13](https://github.com/strawlab/iana-time-zone/pull/13)) + +## [0.1.12] - 2021-06-28 +### Changed +- Update `windows` requirement from 0.11 to 0.12 ([#12](https://github.com/strawlab/iana-time-zone/pull/12)) + +## [0.1.11] - 2021-06-12 +### Changed +- Update `windows` requirement from 0.10 to 0.11 ([#11](https://github.com/strawlab/iana-time-zone/pull/11)) + +## [0.1.10] - 2021-05-13 +### Changed +- Update `windows` requirement from 0.9 to 0.10 ([#10](https://github.com/strawlab/iana-time-zone/pull/10)) + +## [0.1.9] - 2021-04-28 +### Changed +- Update `windows` requirement from 0.8 to 0.9 ([#8](https://github.com/strawlab/iana-time-zone/pull/8)) + +## [0.1.8] - 2021-04-13 +### Changed +- Update `windows` requirement from 0.7 to 0.8 ([#7](https://github.com/strawlab/iana-time-zone/pull/7)) + +## [0.1.7] - 2021-03-30 +### Changed +- Update `windows` requirement from 0.6 to 0.7 ([#6](https://github.com/strawlab/iana-time-zone/pull/6)) + +## [0.1.6] - 2021-03-24 +### Changed +- Update `windows` requirement from 0.5 to 0.6 ([#5](https://github.com/strawlab/iana-time-zone/pull/5)) + +## [0.1.5] - 2021-03-20 +### Changed +- Update `windows` requirement from 0.4 to 0.5 ([#4](https://github.com/strawlab/iana-time-zone/pull/4)) + +## [0.1.4] - 2021-03-11 +### Changed +- Update `windows` requirement from 0.3 to 0.4 ([#3](https://github.com/strawlab/iana-time-zone/pull/3)) + +## [0.1.3] - 2021-02-22 +### Changed +- Use `windows` crate instead of `winrt` + +## [0.1.2] - 2020-10-09 +### Changed +- Update `core-foundation` requirement from 0.7 to 0.9 ([#1](https://github.com/strawlab/iana-time-zone/pull/1)) + +## [0.1.1] - 2020-06-27 +### Changed +- Update `core-foundation` requirement from 0.5 to 0.7 + +## [0.1.0] - 2020-06-27 +### Added +- Implement for Linux, Windows, MacOS + +[0.1.54]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.54 +[0.1.53]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.53 +[0.1.52]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.52 +[0.1.51]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.51 +[0.1.50]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.50 +[0.1.49]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.49 +[0.1.48]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.48 +[0.1.47]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.47 +[0.1.46]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.46 +[0.1.45]: https://github.com/strawlab/iana-time-zone/releases/tag/v0.1.45 +[0.1.44]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.44 +[0.1.43]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.43 +[0.1.42]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.42 +[0.1.41]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.41 +[0.1.40]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.40 +[0.1.39]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.39 +[0.1.38]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.38 +[0.1.37]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.37 +[0.1.36]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.36 +[0.1.35]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.35 +[0.1.34]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.34 +[0.1.33]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.33 +[0.1.32]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.32 +[0.1.31]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.31 +[0.1.30]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.30 +[0.1.29]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.29 +[0.1.28]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.28 +[0.1.27]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.27 +[0.1.26]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.26 +[0.1.25]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.25 +[0.1.24]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.24 +[0.1.23]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.23 +[0.1.22]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.22 +[0.1.21]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.21 +[0.1.20]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.20 +[0.1.19]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.19 +[0.1.18]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.18 +[0.1.17]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.17 +[0.1.16]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.16 +[0.1.15]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.15 +[0.1.14]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.14 +[0.1.13]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.13 +[0.1.12]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.12 +[0.1.11]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.11 +[0.1.10]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.10 +[0.1.9]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.9 +[0.1.8]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.8 +[0.1.7]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.7 +[0.1.6]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.6 +[0.1.5]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.5 +[0.1.4]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.4 +[0.1.3]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.3 +[0.1.2]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.2 +[0.1.1]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.1 +[0.1.0]: https://github.com/strawlab/iana-time-zone/releases/tag/0.1.0 diff --git a/javascript-engine/external/iana-time-zone/Cargo.toml b/javascript-engine/external/iana-time-zone/Cargo.toml new file mode 100644 index 0000000..37532b9 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "iana-time-zone" +description = "get the IANA time zone for the current system" +# Origin version is: 0.1.54, modify the version to 0.1.53 +version = "0.1.53" +authors = [ + "Andrew Straw ", + "René Kijewski ", + "Ryan Lopopolo ", + ] +repository = "https://github.com/strawlab/iana-time-zone" +license = "MIT OR Apache-2.0" +keywords = ["IANA", "time"] +categories = ["date-and-time", "internationalization", "os"] +readme = "README.md" +edition = "2018" + +[features] +# When enabled, the library will succeed to compile for unknown target platforms, and return an `Err(GetTimezoneError::OsError)` at runtime. +fallback = [] + +[target.'cfg(target_os = "android")'.dependencies] +android_system_properties = "0.1.5" + +[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] +core-foundation-sys = "0.8.3" + +[target.'cfg(target_os = "windows")'.dependencies] +windows-sys = { version = "0.42.0", features = ["Win32_Globalization", "Win32_System_Com", "Win32_System_WinRT"] } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +js-sys = "0.3.50" +wasm-bindgen = "0.2.70" + +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +wasm-bindgen-test = "0.3" + +[target.'cfg(target_os = "haiku")'.dependencies] +iana-time-zone-haiku = { version = "0.1.1", path = "haiku" } + +[workspace] +members = [".", "haiku"] +default-members = ["."] diff --git a/javascript-engine/external/iana-time-zone/LICENSE-APACHE b/javascript-engine/external/iana-time-zone/LICENSE-APACHE new file mode 100644 index 0000000..18af3f6 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2020 Andrew Straw + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/javascript-engine/external/iana-time-zone/LICENSE-MIT b/javascript-engine/external/iana-time-zone/LICENSE-MIT new file mode 100644 index 0000000..8f8dd90 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2020 Andrew D. Straw + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/javascript-engine/external/iana-time-zone/README.md b/javascript-engine/external/iana-time-zone/README.md new file mode 100644 index 0000000..f9e80e2 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/README.md @@ -0,0 +1,47 @@ +# iana-time-zone - get the IANA time zone for the current system + +[![Crates.io](https://img.shields.io/crates/v/iana-time-zone.svg)](https://crates.io/crates/iana-time-zone) +[![Documentation](https://docs.rs/iana-time-zone/badge.svg)](https://docs.rs/iana-time-zone/) +[![Crate License](https://img.shields.io/crates/l/iana-time-zone.svg)](https://crates.io/crates/iana-time-zone) +[![build](https://github.com/strawlab/iana-time-zone/workflows/build/badge.svg?branch=main)](https://github.com/strawlab/iana-time-zone/actions?query=branch%3Amain) + +This small utility crate gets the IANA time zone for the current system. +This is also known the [tz database](https://en.wikipedia.org/wiki/Tz_database), +tzdata, the zoneinfo database, and the Olson database. + +Example: + +```rust +// Get the current time zone as a string. +let tz_str = iana_time_zone::get_timezone()?; +println!("The current time zone is: {}", tz_str); +``` + +You can test this is working on your platform with: + +``` +cargo run --example get_timezone +``` + +## Minimum supported rust version policy + +This crate has a minimum supported rust version (MSRV) of 1.48 +for [Tier 1](https://doc.rust-lang.org/1.63.0/rustc/platform-support.html) platforms. + +Updates to the MSRV are sometimes necessary due to the MSRV of dependencies. MSRV updates will +not be indicated as a breaking change to the semver version. + +## License + +Licensed under either of + +* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +* MIT license ([LICENSE-MIT](LICENSE-MIT) or + ) + +at your option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/javascript-engine/external/iana-time-zone/examples/get_timezone.rs b/javascript-engine/external/iana-time-zone/examples/get_timezone.rs new file mode 100644 index 0000000..0f5119f --- /dev/null +++ b/javascript-engine/external/iana-time-zone/examples/get_timezone.rs @@ -0,0 +1,6 @@ +use iana_time_zone::{get_timezone, GetTimezoneError}; + +fn main() -> Result<(), GetTimezoneError> { + println!("{}", get_timezone()?); + Ok(()) +} diff --git a/javascript-engine/external/iana-time-zone/examples/stress-test.rs b/javascript-engine/external/iana-time-zone/examples/stress-test.rs new file mode 100644 index 0000000..73df86f --- /dev/null +++ b/javascript-engine/external/iana-time-zone/examples/stress-test.rs @@ -0,0 +1,25 @@ +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::thread::spawn; + +use iana_time_zone::get_timezone; + +const THREADS: usize = 10; +const ITERATIONS: usize = 100_000; + +static COUNT: AtomicUsize = AtomicUsize::new(0); + +fn main() { + let mut threads = Vec::with_capacity(THREADS); + for _ in 0..THREADS { + threads.push(spawn(|| { + for _ in 0..ITERATIONS { + get_timezone().unwrap(); + COUNT.fetch_add(1, Ordering::Relaxed); + } + })); + } + for thread in threads { + thread.join().unwrap(); + } + assert_eq!(COUNT.load(Ordering::SeqCst), THREADS * ITERATIONS); +} diff --git a/javascript-engine/external/iana-time-zone/haiku/Cargo.toml b/javascript-engine/external/iana-time-zone/haiku/Cargo.toml new file mode 100644 index 0000000..8edb7f0 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/haiku/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "iana-time-zone-haiku" +description = "iana-time-zone support crate for Haiku OS" +version = "0.1.1" +authors = ["René Kijewski "] +repository = "https://github.com/strawlab/iana-time-zone" +license = "MIT OR Apache-2.0" +keywords = ["IANA", "time"] +categories = ["date-and-time", "internationalization", "os"] +readme = "README.md" +edition = "2018" + +[dependencies] +cxx = "1.0.34" + +[build-dependencies] +cxx-build = "1.0.34" diff --git a/javascript-engine/external/iana-time-zone/haiku/LICENSE-APACHE b/javascript-engine/external/iana-time-zone/haiku/LICENSE-APACHE new file mode 120000 index 0000000..965b606 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/haiku/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/javascript-engine/external/iana-time-zone/haiku/LICENSE-MIT b/javascript-engine/external/iana-time-zone/haiku/LICENSE-MIT new file mode 120000 index 0000000..76219eb --- /dev/null +++ b/javascript-engine/external/iana-time-zone/haiku/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/javascript-engine/external/iana-time-zone/haiku/README.md b/javascript-engine/external/iana-time-zone/haiku/README.md new file mode 100644 index 0000000..87450cd --- /dev/null +++ b/javascript-engine/external/iana-time-zone/haiku/README.md @@ -0,0 +1,8 @@ +# iana-time-zone-haiku + +[![Crates.io](https://img.shields.io/crates/v/iana-time-zone-haiku.svg)](https://crates.io/crates/iana-time-zone-haiku) +[![Documentation](https://docs.rs/iana-time-zone/badge.svg)](https://docs.rs/iana-time-zone/) +[![Crate License](https://img.shields.io/crates/l/iana-time-zone-haiku.svg)](https://crates.io/crates/iana-time-zone-haiku) +[![build](https://github.com/strawlab/iana-time-zone/workflows/build/badge.svg?branch=main)](https://github.com/strawlab/iana-time-zone/actions?query=branch%3Amain) + +[iana-time-zone](https://github.com/strawlab/iana-time-zone) support crate for Haiku OS. diff --git a/javascript-engine/external/iana-time-zone/haiku/build.rs b/javascript-engine/external/iana-time-zone/haiku/build.rs new file mode 100644 index 0000000..085e8d0 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/haiku/build.rs @@ -0,0 +1,20 @@ +use std::env; + +fn main() { + cxx_build::bridge("src/lib.rs") + .file("src/implementation.cc") + .flag_if_supported("-std=c++11") + .compile("tz_haiku"); + + println!("cargo:rerun-if-changed=src/lib.rs"); + println!("cargo:rerun-if-changed=src/implementation.cc"); + println!("cargo:rerun-if-changed=src/interface.h"); + + let target = env::var_os("TARGET").expect("cargo should set TARGET env var"); + let target = target + .to_str() + .expect("TARGET env var should be valid UTF-8"); + if target.contains("haiku") { + println!("cargo:rustc-link-lib=be"); + } +} diff --git a/javascript-engine/external/iana-time-zone/haiku/src/implementation.cc b/javascript-engine/external/iana-time-zone/haiku/src/implementation.cc new file mode 100644 index 0000000..35e1305 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/haiku/src/implementation.cc @@ -0,0 +1,66 @@ +#include "iana-time-zone-haiku/src/interface.h" +#include "iana-time-zone-haiku/src/lib.rs.h" + +#ifdef __HAIKU__ + +#include + +#include +#include +#include +#include + +namespace iana_time_zone_haiku { +size_t get_tz(rust::Slice buf) { + try { + static_assert(sizeof(char) == sizeof(uint8_t), "Illegal char size"); + + if (buf.empty()) { + return 0; + } + + // `BLocaleRoster::Default()` returns a reference to a statically allocated object. + // https://github.com/haiku/haiku/blob/8f16317/src/kits/locale/LocaleRoster.cpp#L143-L147 + BLocaleRoster *locale_roster(BLocaleRoster::Default()); + if (!locale_roster) { + return 0; + } + + BTimeZone tz(NULL, NULL); + if (locale_roster->GetDefaultTimeZone(&tz) != B_OK) { + return 0; + } + + BString bname(tz.ID()); + int32_t ilength(bname.Length()); + if (ilength <= 0) { + return 0; + } + + size_t length(ilength); + if (length > buf.size()) { + return 0; + } + + // BString::String() returns a borrowed string. + // https://www.haiku-os.org/docs/api/classBString.html#ae4fe78b06c8e3310093b80305e14ba87 + const char *sname(bname.String()); + if (!sname) { + return 0; + } + + std::memcpy(buf.data(), sname, length); + return length; + } catch (...) { + return 0; + } +} +} // namespace iana_time_zone_haiku + +#else + +namespace iana_time_zone_haiku { +size_t get_tz(rust::Slice) { return 0; } +} // namespace iana_time_zone_haiku + +#endif diff --git a/javascript-engine/external/iana-time-zone/haiku/src/interface.h b/javascript-engine/external/iana-time-zone/haiku/src/interface.h new file mode 100644 index 0000000..5913a40 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/haiku/src/interface.h @@ -0,0 +1,9 @@ +#pragma once + +#include "rust/cxx.h" + +#include + +namespace iana_time_zone_haiku { +size_t get_tz(rust::Slice buf); +} diff --git a/javascript-engine/external/iana-time-zone/haiku/src/lib.rs b/javascript-engine/external/iana-time-zone/haiku/src/lib.rs new file mode 100644 index 0000000..ebf5dc8 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/haiku/src/lib.rs @@ -0,0 +1,68 @@ +#![warn(clippy::all)] +#![warn(clippy::cargo)] +#![warn(clippy::undocumented_unsafe_blocks)] +#![allow(unknown_lints)] +#![warn(missing_copy_implementations)] +#![warn(missing_debug_implementations)] +#![warn(missing_docs)] +#![warn(rust_2018_idioms)] +#![warn(trivial_casts, trivial_numeric_casts)] +#![warn(unsafe_op_in_unsafe_fn)] +#![warn(unused_qualifications)] +#![warn(variant_size_differences)] + +//! # iana-time-zone-haiku +//! +//! [![Crates.io](https://img.shields.io/crates/v/iana-time-zone-haiku.svg)](https://crates.io/crates/iana-time-zone-haiku) +//! [![Documentation](https://docs.rs/iana-time-zone/badge.svg)](https://docs.rs/iana-time-zone/) +//! [![Crate License](https://img.shields.io/crates/l/iana-time-zone-haiku-haiku.svg)](https://crates.io/crates/iana-time-zone-haiku) +//! [![build](https://github.com/strawlab/iana-time-zone/workflows/build/badge.svg?branch=main)](https://github.com/strawlab/iana-time-zone/actions?query=branch%3Amain) +//! +//! [iana-time-zone](https://github.com/strawlab/iana-time-zone) support crate for Haiku OS. + +#[cxx::bridge(namespace = "iana_time_zone_haiku")] +mod ffi { + // SAFETY: in here "unsafe" is simply part of the syntax + unsafe extern "C++" { + include!("iana-time-zone-haiku/src/interface.h"); + + fn get_tz(buf: &mut [u8]) -> usize; + } +} + +/// Get the current IANA time zone as a string. +/// +/// On Haiku platforms this function will return [`Some`] with the timezone string +/// or [`None`] if an error occurs. On all other platforms, [`None`] is returned. +/// +/// # Examples +/// +/// ``` +/// let timezone = iana_time_zone_haiku::get_timezone(); +/// ``` +pub fn get_timezone() -> Option { + // The longest name in the IANA time zone database is 25 ASCII characters long. + let mut buf = [0u8; 32]; + let len = ffi::get_tz(&mut buf); + // The name should not be empty, or excessively long. + match buf.get(..len)? { + b"" => None, + s => Some(std::str::from_utf8(s).ok()?.to_owned()), + } +} + +#[cfg(test)] +mod tests { + #[test] + #[cfg(not(target_os = "haiku"))] + fn test_fallback_on_non_haiku_platforms() { + assert!(super::get_timezone().is_none()); + } + + #[test] + #[cfg(target_os = "haiku")] + fn test_retrieve_time_zone_on_haiku_platforms() { + let timezone = super::get_timezone().unwrap(); + assert!(!timezone.is_empty()); + } +} diff --git a/javascript-engine/external/iana-time-zone/src/ffi_utils.rs b/javascript-engine/external/iana-time-zone/src/ffi_utils.rs new file mode 100644 index 0000000..842b850 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/src/ffi_utils.rs @@ -0,0 +1,49 @@ +//! Cross platform FFI helpers. + +use std::ffi::CStr; + +// The system property named 'persist.sys.timezone' contains the name of the +// current timezone. +// +// From https://android.googlesource.com/platform/bionic/+/gingerbread-release/libc/docs/OVERVIEW.TXT#79: +// +// > The name of the current timezone is taken from the TZ environment variable, +// > if defined. Otherwise, the system property named 'persist.sys.timezone' is +// > checked instead. +const ANDROID_TIMEZONE_PROPERTY_NAME: &[u8] = b"persist.sys.timezone\0"; + +/// Return a [`CStr`] to access the timezone from an Android system properties +/// environment. +pub(crate) fn android_timezone_property_name() -> &'static CStr { + // In tests or debug mode, opt into extra runtime checks. + if cfg!(any(test, debug_assertions)) { + return CStr::from_bytes_with_nul(ANDROID_TIMEZONE_PROPERTY_NAME).unwrap(); + } + + // SAFETY: the key is NUL-terminated and there are no other NULs, this + // invariant is checked in tests. + unsafe { CStr::from_bytes_with_nul_unchecked(ANDROID_TIMEZONE_PROPERTY_NAME) } +} + +#[cfg(test)] +mod tests { + use std::ffi::CStr; + + use super::{android_timezone_property_name, ANDROID_TIMEZONE_PROPERTY_NAME}; + + #[test] + fn test_android_timezone_property_name_is_valid_cstr() { + CStr::from_bytes_with_nul(ANDROID_TIMEZONE_PROPERTY_NAME).unwrap(); + + let mut invalid_property_name = ANDROID_TIMEZONE_PROPERTY_NAME.to_owned(); + invalid_property_name.push(b'\0'); + CStr::from_bytes_with_nul(&invalid_property_name).unwrap_err(); + } + + #[test] + fn test_android_timezone_property_name_getter() { + let key = android_timezone_property_name().to_bytes_with_nul(); + assert_eq!(key, ANDROID_TIMEZONE_PROPERTY_NAME); + std::str::from_utf8(key).unwrap(); + } +} diff --git a/javascript-engine/external/iana-time-zone/src/lib.rs b/javascript-engine/external/iana-time-zone/src/lib.rs new file mode 100644 index 0000000..81c321f --- /dev/null +++ b/javascript-engine/external/iana-time-zone/src/lib.rs @@ -0,0 +1,113 @@ +#![warn(clippy::all)] +#![warn(clippy::cargo)] +#![warn(clippy::undocumented_unsafe_blocks)] +#![allow(unknown_lints)] +#![warn(missing_copy_implementations)] +#![warn(missing_debug_implementations)] +#![warn(missing_docs)] +#![warn(rust_2018_idioms)] +#![warn(trivial_casts, trivial_numeric_casts)] +#![warn(unused_qualifications)] +#![warn(variant_size_differences)] + +//! get the IANA time zone for the current system +//! +//! This small utility crate provides the +//! [`get_timezone()`](fn.get_timezone.html) function. +//! +//! ```rust +//! // Get the current time zone as a string. +//! let tz_str = iana_time_zone::get_timezone()?; +//! println!("The current time zone is: {}", tz_str); +//! # Ok::<(), iana_time_zone::GetTimezoneError>(()) +//! ``` +//! +//! The resulting string can be parsed to a +//! [`chrono-tz::Tz`](https://docs.rs/chrono-tz/latest/chrono_tz/enum.Tz.html) +//! variant like this: +//! ```ignore +//! let tz_str = iana_time_zone::get_timezone()?; +//! let tz: chrono_tz::Tz = tz_str.parse()?; +//! ``` + +#[allow(dead_code)] +mod ffi_utils; + +#[cfg_attr(target_os = "linux", path = "tz_linux.rs")] +#[cfg_attr(target_os = "windows", path = "tz_windows.rs")] +#[cfg_attr(any(target_os = "macos", target_os = "ios"), path = "tz_macos.rs")] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + path = "tz_wasm32.rs" +)] +#[cfg_attr( + any(target_os = "freebsd", target_os = "dragonfly"), + path = "tz_freebsd.rs" +)] +#[cfg_attr( + any(target_os = "netbsd", target_os = "openbsd"), + path = "tz_netbsd.rs" +)] +#[cfg_attr( + any(target_os = "illumos", target_os = "solaris"), + path = "tz_illumos.rs" +)] +#[cfg_attr(target_os = "android", path = "tz_android.rs")] +#[cfg_attr(target_os = "haiku", path = "tz_haiku.rs")] +mod platform; + +/// Error types +#[derive(Debug)] +pub enum GetTimezoneError { + /// Failed to parse + FailedParsingString, + /// Wrapped IO error + IoError(std::io::Error), + /// Platform-specific error from the operating system + OsError, +} + +impl std::error::Error for GetTimezoneError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + GetTimezoneError::FailedParsingString => None, + GetTimezoneError::IoError(err) => Some(err), + GetTimezoneError::OsError => None, + } + } +} + +impl std::fmt::Display for GetTimezoneError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + f.write_str(match self { + GetTimezoneError::FailedParsingString => "GetTimezoneError::FailedParsingString", + GetTimezoneError::IoError(err) => return err.fmt(f), + GetTimezoneError::OsError => "OsError", + }) + } +} + +impl From for GetTimezoneError { + fn from(orig: std::io::Error) -> Self { + GetTimezoneError::IoError(orig) + } +} + +/// Get the current IANA time zone as a string. +/// +/// See the module-level documentatation for a usage example and more details +/// about this function. +#[inline] +pub fn get_timezone() -> Result { + platform::get_timezone_inner() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn get_current() { + println!("current: {}", get_timezone().unwrap()); + } +} diff --git a/javascript-engine/external/iana-time-zone/src/platform.rs b/javascript-engine/external/iana-time-zone/src/platform.rs new file mode 100644 index 0000000..5992bf3 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/src/platform.rs @@ -0,0 +1,9 @@ +pub fn get_timezone_inner() -> std::result::Result { + Err(crate::GetTimezoneError::OsError) +} + +#[cfg(not(feature = "fallback"))] +compile_error!( + "iana-time-zone is currently implemented for Linux, Window, MacOS, FreeBSD, NetBSD, \ + OpenBSD, Dragonfly, WebAssembly (browser), iOS, Illumos, Android, Solaris and Haiku.", +); diff --git a/javascript-engine/external/iana-time-zone/src/tz_android.rs b/javascript-engine/external/iana-time-zone/src/tz_android.rs new file mode 100644 index 0000000..27255b5 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/src/tz_android.rs @@ -0,0 +1,27 @@ +use std::sync::Once; + +use android_system_properties::AndroidSystemProperties; + +use crate::ffi_utils::android_timezone_property_name; + +pub(crate) fn get_timezone_inner() -> Result { + let key = android_timezone_property_name(); + + get_properties() + .and_then(|properties| properties.get_from_cstr(key)) + .ok_or(crate::GetTimezoneError::OsError) +} + +fn get_properties() -> Option<&'static AndroidSystemProperties> { + static INITIALIZED: Once = Once::new(); + static mut PROPERTIES: Option = None; + + INITIALIZED.call_once(|| { + let properties = AndroidSystemProperties::new(); + // SAFETY: `INITIALIZED` is synchronizing. The variable is only assigned to once. + unsafe { PROPERTIES = Some(properties) }; + }); + + // SAFETY: `INITIALIZED` is synchronizing. The variable is only assigned to once. + unsafe { PROPERTIES.as_ref() } +} diff --git a/javascript-engine/external/iana-time-zone/src/tz_freebsd.rs b/javascript-engine/external/iana-time-zone/src/tz_freebsd.rs new file mode 100644 index 0000000..4d55e15 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/src/tz_freebsd.rs @@ -0,0 +1,7 @@ +pub(crate) fn get_timezone_inner() -> Result { + // see https://gitlab.gnome.org/GNOME/evolution-data-server/-/issues/19 + let mut contents = std::fs::read_to_string("/var/db/zoneinfo")?; + // Trim to the correct length without allocating. + contents.truncate(contents.trim_end().len()); + Ok(contents) +} diff --git a/javascript-engine/external/iana-time-zone/src/tz_haiku.rs b/javascript-engine/external/iana-time-zone/src/tz_haiku.rs new file mode 100644 index 0000000..d78372b --- /dev/null +++ b/javascript-engine/external/iana-time-zone/src/tz_haiku.rs @@ -0,0 +1,3 @@ +pub(crate) fn get_timezone_inner() -> Result { + iana_time_zone_haiku::get_timezone().ok_or(crate::GetTimezoneError::OsError) +} diff --git a/javascript-engine/external/iana-time-zone/src/tz_illumos.rs b/javascript-engine/external/iana-time-zone/src/tz_illumos.rs new file mode 100644 index 0000000..17b099b --- /dev/null +++ b/javascript-engine/external/iana-time-zone/src/tz_illumos.rs @@ -0,0 +1,22 @@ +use std::fs::OpenOptions; +use std::io::{BufRead, BufReader}; + +pub(crate) fn get_timezone_inner() -> Result { + // https://illumos.org/man/5/TIMEZONE + // https://docs.oracle.com/cd/E23824_01/html/821-1473/uc-timezone-4.html + + let file = OpenOptions::new().read(true).open("/etc/default/init")?; + let mut reader = BufReader::with_capacity(1536, file); + let mut line = String::with_capacity(80); + loop { + line.clear(); + let count = reader.read_line(&mut line)?; + if count == 0 { + return Err(crate::GetTimezoneError::FailedParsingString); + } else if line.starts_with("TZ=") { + line.truncate(line.trim_end().len()); + line.replace_range(..3, ""); + return Ok(line); + } + } +} diff --git a/javascript-engine/external/iana-time-zone/src/tz_linux.rs b/javascript-engine/external/iana-time-zone/src/tz_linux.rs new file mode 100644 index 0000000..c1133e1 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/src/tz_linux.rs @@ -0,0 +1,45 @@ +use std::fs::{read_link, read_to_string}; + +pub(crate) fn get_timezone_inner() -> Result { + etc_localtime().or_else(|_| etc_timezone()) +} + +fn etc_timezone() -> Result { + // see https://stackoverflow.com/a/12523283 + let mut contents = read_to_string("/etc/timezone")?; + // Trim to the correct length without allocating. + contents.truncate(contents.trim_end().len()); + Ok(contents) +} + +fn etc_localtime() -> Result { + // Per : + // “ The /etc/localtime file configures the system-wide timezone of the local system that is + // used by applications for presentation to the user. It should be an absolute or relative + // symbolic link pointing to /usr/share/zoneinfo/, followed by a timezone identifier such as + // "Europe/Berlin" or "Etc/UTC". The resulting link should lead to the corresponding binary + // tzfile(5) timezone data for the configured timezone. ” + + // Systemd does not canonicalize the link, but only checks if it is prefixed by + // "/usr/share/zoneinfo/" or "../usr/share/zoneinfo/". So we do the same. + // + + const PREFIXES: &[&str] = &[ + "/usr/share/zoneinfo/", // absolute path + "../usr/share/zoneinfo/", // relative path + "/etc/zoneinfo/", // absolute path for NixOS + "../etc/zoneinfo/", // relative path for NixOS + ]; + let mut s = read_link("/etc/localtime")? + .into_os_string() + .into_string() + .map_err(|_| crate::GetTimezoneError::FailedParsingString)?; + for &prefix in PREFIXES { + if s.starts_with(prefix) { + // Trim to the correct length without allocating. + s.replace_range(..prefix.len(), ""); + return Ok(s); + } + } + Err(crate::GetTimezoneError::FailedParsingString) +} diff --git a/javascript-engine/external/iana-time-zone/src/tz_macos.rs b/javascript-engine/external/iana-time-zone/src/tz_macos.rs new file mode 100644 index 0000000..70c39e8 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/src/tz_macos.rs @@ -0,0 +1,144 @@ +pub(crate) fn get_timezone_inner() -> Result { + get_timezone().ok_or(crate::GetTimezoneError::OsError) +} + +#[inline] +fn get_timezone() -> Option { + // The longest name in the IANA time zone database is 25 ASCII characters long. + const MAX_LEN: usize = 32; + let mut buf = [0; MAX_LEN]; + + // Get system time zone, and borrow its name. + let tz = system_time_zone::SystemTimeZone::new()?; + let name = tz.name()?; + + // If the name is encoded in UTF-8, copy it directly. + let name = if let Some(name) = name.as_utf8() { + name + } else { + // Otherwise convert the name to UTF-8. + name.to_utf8(&mut buf)? + }; + + if name.is_empty() || name.len() >= MAX_LEN { + // The name should not be empty, or excessively long. + None + } else { + Some(name.to_owned()) + } +} + +mod system_time_zone { + //! create a safe wrapper around `CFTimeZoneRef` + + use core_foundation_sys::base::{CFRelease, CFTypeRef}; + use core_foundation_sys::timezone::{CFTimeZoneCopySystem, CFTimeZoneGetName, CFTimeZoneRef}; + + pub(crate) struct SystemTimeZone(CFTimeZoneRef); + + impl Drop for SystemTimeZone { + fn drop(&mut self) { + // SAFETY: `SystemTimeZone` is only ever created with a valid `CFTimeZoneRef`. + unsafe { CFRelease(self.0 as CFTypeRef) }; + } + } + + impl SystemTimeZone { + pub(crate) fn new() -> Option { + // SAFETY: No invariants to uphold. We'll release the pointer when we don't need it anymore. + let v: CFTimeZoneRef = unsafe { CFTimeZoneCopySystem() }; + if v.is_null() { + None + } else { + Some(SystemTimeZone(v)) + } + } + + /// Get the time zone name as a [super::string_ref::StringRef]. + /// + /// The lifetime of the `StringRef` is bound to our lifetime. Mutable + /// access is also prevented by taking a reference to `self`. + pub(crate) fn name(&self) -> Option> { + // SAFETY: `SystemTimeZone` is only ever created with a valid `CFTimeZoneRef`. + let string = unsafe { CFTimeZoneGetName(self.0) }; + if string.is_null() { + None + } else { + // SAFETY: here we ensure that `string` is a valid pointer. + Some(unsafe { super::string_ref::StringRef::new(string, self) }) + } + } + } +} + +mod string_ref { + //! create safe wrapper around `CFStringRef` + + use std::convert::TryInto; + + use core_foundation_sys::base::{Boolean, CFRange}; + use core_foundation_sys::string::{ + kCFStringEncodingUTF8, CFStringGetBytes, CFStringGetCStringPtr, CFStringGetLength, + CFStringRef, + }; + + pub(crate) struct StringRef<'a, T> { + string: CFStringRef, + // We exclude mutable access to the parent by taking a reference to the + // parent (rather than, for example, just using a marker to enforce the + // parent's lifetime). + _parent: &'a T, + } + + impl<'a, T> StringRef<'a, T> { + // SAFETY: `StringRef` must be valid pointer + pub(crate) unsafe fn new(string: CFStringRef, _parent: &'a T) -> Self { + Self { string, _parent } + } + + pub(crate) fn as_utf8(&self) -> Option<&'a str> { + // SAFETY: `StringRef` is only ever created with a valid `CFStringRef`. + let v = unsafe { CFStringGetCStringPtr(self.string, kCFStringEncodingUTF8) }; + if !v.is_null() { + // SAFETY: `CFStringGetCStringPtr()` returns NUL-terminated strings. + let v = unsafe { std::ffi::CStr::from_ptr(v) }; + if let Ok(v) = v.to_str() { + return Some(v); + } + } + None + } + + pub(crate) fn to_utf8<'b>(&self, buf: &'b mut [u8]) -> Option<&'b str> { + // SAFETY: `StringRef` is only ever created with a valid `CFStringRef`. + let length = unsafe { CFStringGetLength(self.string) }; + + let mut buf_bytes = 0; + let range = CFRange { + location: 0, + length, + }; + + let converted_bytes = unsafe { + // SAFETY: `StringRef` is only ever created with a valid `CFStringRef`. + CFStringGetBytes( + self.string, + range, + kCFStringEncodingUTF8, + b'\0', + false as Boolean, + buf.as_mut_ptr(), + buf.len() as isize, + &mut buf_bytes, + ) + }; + if converted_bytes != length { + return None; + } + + let len = buf_bytes.try_into().ok()?; + let s = buf.get(..len)?; + std::str::from_utf8(s).ok() + } + } +} diff --git a/javascript-engine/external/iana-time-zone/src/tz_netbsd.rs b/javascript-engine/external/iana-time-zone/src/tz_netbsd.rs new file mode 100644 index 0000000..84cf8b0 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/src/tz_netbsd.rs @@ -0,0 +1,25 @@ +use std::fs::read_link; + +pub(crate) fn get_timezone_inner() -> Result { + // see https://www.cyberciti.biz/faq/openbsd-time-zone-howto/ + + // This is a backport of the Linux implementation. + // NetBSDs is less than thorough how the softlink should be set up. + + const PREFIXES: &[&str] = &[ + "/usr/share/zoneinfo/", // absolute path + "../usr/share/zoneinfo/", // relative path + ]; + let mut s = read_link("/etc/localtime")? + .into_os_string() + .into_string() + .map_err(|_| crate::GetTimezoneError::FailedParsingString)?; + for &prefix in PREFIXES { + if s.starts_with(prefix) { + // Trim to the correct length without allocating. + s.replace_range(..prefix.len(), ""); + return Ok(s); + } + } + Err(crate::GetTimezoneError::FailedParsingString) +} diff --git a/javascript-engine/external/iana-time-zone/src/tz_wasm32.rs b/javascript-engine/external/iana-time-zone/src/tz_wasm32.rs new file mode 100644 index 0000000..69c36b5 --- /dev/null +++ b/javascript-engine/external/iana-time-zone/src/tz_wasm32.rs @@ -0,0 +1,21 @@ +use js_sys::{Array, Intl, Object, Reflect}; +use wasm_bindgen::JsValue; + +pub(crate) fn get_timezone_inner() -> Result { + let intl = Intl::DateTimeFormat::new(&Array::new(), &Object::new()).resolved_options(); + Reflect::get(&intl, &JsValue::from_str("timeZone")) + .ok() + .and_then(|tz| tz.as_string()) + .ok_or(crate::GetTimezoneError::OsError) +} + +#[cfg(test)] +mod tests { + use wasm_bindgen_test::*; + + #[wasm_bindgen_test] + fn pass() { + let tz = super::get_timezone_inner().unwrap(); + console_log!("tz={:?}", tz); + } +} diff --git a/javascript-engine/external/iana-time-zone/src/tz_windows.rs b/javascript-engine/external/iana-time-zone/src/tz_windows.rs new file mode 100644 index 0000000..64f777b --- /dev/null +++ b/javascript-engine/external/iana-time-zone/src/tz_windows.rs @@ -0,0 +1,312 @@ +use std::mem::zeroed; + +use windows_sys::core::{GUID, HRESULT}; +use windows_sys::Win32::System::Com::CoIncrementMTAUsage; +use windows_sys::Win32::System::WinRT::HSTRING_HEADER; + +use self::hstring::HString; +use self::instance::Instance; +use self::tz_on_calendar::TzOnCalendar; + +macro_rules! wstring { + ($($letters:tt)+) => { + [ $($letters as _,)+ ] + }; +} + +const WINDOWS_GLOBALIZATION_CALENDAR: &[u16] = &wstring!( + 'W' 'i' 'n' 'd' 'o' 'w' 's' '.' + 'G' 'l' 'o' 'b' 'a' 'l' 'i' 'z' 'a' 't' 'i' 'o' 'n' '.' + 'C' 'a' 'l' 'e' 'n' 'd' 'a' 'r' + 0 +); + +const TIMEZONE_ON_CALENDAR_GUID: GUID = GUID { + data1: 0xbb3c25e5, + data2: 0x46cf, + data3: 0x4317, + data4: [0xa3, 0xf5, 0x02, 0x62, 0x1a, 0xd5, 0x44, 0x78], +}; + +const CO_E_NOTINITIALIZED: HRESULT = -2147221008; + +impl From for crate::GetTimezoneError { + fn from(orig: HRESULT) -> Self { + std::io::Error::from_raw_os_error(orig).into() + } +} + +pub(crate) fn get_timezone_inner() -> Result { + // Get HSTRING for "Windows.Globalization.Calendar". + // SAFETY: An `HSTRING_HEADER` actually does not need initialization when used with + // `WindowsCreateStringReference()`, but zeroing it is as good a initial value as any. + let mut string_header: HSTRING_HEADER = unsafe { zeroed() }; + let class_name = HString::create(WINDOWS_GLOBALIZATION_CALENDAR, &mut string_header)?; + + // Create new "Windows.Globalization.Calendar" instance. + let calendar = Instance::activate(&class_name).or_else(|result| { + // Some other library could have called CoIncrementMTAUsage() or CoInitializeEx(), so we + // only call CoIncrementMTAUsage() if RoActivateInstance() tells us that multithreading + // was not initialized, yet. + + // No need to check the error. The only conceivable error code this function returns is + // E_OUTOFMEMORY, and the program is about to get OOM killed anyway in this case. + // Windows-rs does not check the result, either. + + if result != CO_E_NOTINITIALIZED { + return Err(result); + } + let mut cookie = 0; + // SAFETY: "Don't call CoIncrementMTAUsage during process shutdown or inside dllmain." + // Using the function is `fn main()` is totally fine. If you go really low level + // and implement an "fn wWinMain()" somehow, then all bets are off anyway. + let _ = unsafe { CoIncrementMTAUsage(&mut cookie) }; + + Instance::activate(&class_name) + })?; + + // Query ITimeZoneOnCalendar of the calendar instance. + let tz = TzOnCalendar::query(&calendar)?; + + // Get the name of the time zone. + let name = HString::from_tz_on_calendar(&tz)?; + + // Convert to Rust String + Ok(name.to_string()) +} + +mod hstring { + use std::ptr::null_mut; + + use windows_sys::core::{HRESULT, HSTRING}; + use windows_sys::Win32::System::WinRT::WindowsDeleteString; + use windows_sys::Win32::System::WinRT::{ + WindowsCreateStringReference, WindowsGetStringRawBuffer, HSTRING_HEADER, + }; + + use super::tz_on_calendar::TzOnCalendar; + + pub struct HString<'a> { + string: HSTRING, + _header: Option<&'a mut HSTRING_HEADER>, + } + + impl<'a> HString<'a> { + // `source` must be null-terminated. Windows tests if the the terminator is missing and + // returns an error if it is absent. + pub fn create(source: &'a [u16], header: &'a mut HSTRING_HEADER) -> Result { + let mut string = null_mut(); + // SAFETY: `source` is a valid reference. If its contents are not a valid wide string, + // then the call will return an error code. We keep a reference to the `source` + // and `header`, so they stay valid until the `HSTRING` is released. + let result = unsafe { + WindowsCreateStringReference( + source.as_ptr(), + (source.len().saturating_sub(1)) as u32, + header, + &mut string, + ) + }; + if result < 0 || string.is_null() { + Err(result) + } else { + Ok(Self { + string, + _header: Some(header), + }) + } + } + + pub fn from_tz_on_calendar(instance: &'a TzOnCalendar) -> Result { + let mut string = null_mut(); + // SAFETY: A `TzOnCalendar` is only ever created with a valid instance. + let result = unsafe { + let instance = instance.as_ptr(); + ((**instance).GetTimeZone)(instance, &mut string) + }; + if result < 0 || string.is_null() { + Err(result) + } else { + Ok(Self { + string, + _header: None, + }) + } + } + + /// SAFETY: You are not allowed to release the returned pointer. + pub unsafe fn as_ptr(&self) -> HSTRING { + self.string + } + } + + impl ToString for HString<'_> { + fn to_string(&self) -> String { + let mut len = 0; + // SAFETY: An `HString` is only ever created with a valid `HSTRING`. + // It keeps a reference to `HSTRING_HEADER` if needed. + let buf = unsafe { WindowsGetStringRawBuffer(self.string, &mut len) }; + if len == 0 || buf.is_null() { + return String::new(); + } + + // SAFETY: `WindowsGetStringRawBuffer` returns a valid pointer to a wide string. + let slice = unsafe { std::slice::from_raw_parts(buf, len as usize) }; + String::from_utf16_lossy(slice) + } + } + + impl Drop for HString<'_> { + fn drop(&mut self) { + // SAFETY: An `HString` is only ever created with a valid `HSTRING`. + unsafe { WindowsDeleteString(self.string) }; + } + } +} + +mod instance { + use std::ptr::null_mut; + + use windows_sys::core::HRESULT; + use windows_sys::Win32::System::WinRT::RoActivateInstance; + + use super::hstring::HString; + use super::interfaces::IUnknown; + + pub struct Instance(IUnknown); + + impl Instance { + pub fn activate(class_id: &HString<'_>) -> Result { + let mut instance = null_mut(); + // SAFETY: An `HString` is only ever crated with a valid `HSTRING`. + let result = unsafe { RoActivateInstance(class_id.as_ptr(), &mut instance) }; + if result < 0 || instance.is_null() { + Err(result) + } else { + Ok(Self(instance.cast())) + } + } + + /// SAFETY: You are not allowed to release the returned pointer. + pub unsafe fn as_ptr(&self) -> IUnknown { + self.0 + } + } + + impl Drop for Instance { + fn drop(&mut self) { + // SAFETY: An `Instance` is only ever created with a valid `IUnknown`. + unsafe { ((**self.0).Release)(self.0) }; + } + } +} + +mod tz_on_calendar { + use std::ptr::null_mut; + + use windows_sys::core::HRESULT; + + use super::instance::Instance; + use super::interfaces::{ITimeZoneOnCalendar, IUnknown}; + use super::TIMEZONE_ON_CALENDAR_GUID; + + pub struct TzOnCalendar(ITimeZoneOnCalendar); + + impl TzOnCalendar { + pub fn query(source: &Instance) -> Result { + let mut tz = null_mut(); + // SAFETY: An `Instance` is only ever created with a valid `IUnknown`. + let result = unsafe { + let source = source.as_ptr(); + ((**source).QueryInterface)(source, &TIMEZONE_ON_CALENDAR_GUID, &mut tz) + }; + if result < 0 || tz.is_null() { + Err(result) + } else { + Ok(Self(tz.cast())) + } + } + + /// SAFETY: You are not allowed to release the returned pointer. + pub unsafe fn as_ptr(&self) -> ITimeZoneOnCalendar { + self.0 + } + } + + impl Drop for TzOnCalendar { + fn drop(&mut self) { + let v: IUnknown = self.0.cast(); + // SAFETY: `TzOnCalendar` is only ever created with a valid `ITimeZoneOnCalendar`. + unsafe { ((**v).Release)(v) }; + } + } +} + +#[allow(non_snake_case)] +#[allow(non_camel_case_types)] +mod interfaces { + use std::ops::Deref; + + use windows_sys::core::{GUID, HRESULT, HSTRING}; + + pub type IUnknown = *mut *const IUnknown_Vtbl; + pub type IInspectable = *mut *const IInspectable_Vtbl; + pub type ITimeZoneOnCalendar = *mut *const ITimeZoneOnCalendar_Vtbl; + + #[repr(C)] + pub struct IUnknown_Vtbl { + pub QueryInterface: unsafe extern "system" fn( + this: IUnknown, + iid: &GUID, + interface: &mut IUnknown, + ) -> HRESULT, + pub AddRef: unsafe extern "system" fn(this: IUnknown) -> u32, + pub Release: unsafe extern "system" fn(this: IUnknown) -> u32, + } + + #[repr(C)] + pub struct IInspectable_Vtbl { + pub base: IUnknown_Vtbl, + pub GetIids: unsafe extern "system" fn( + this: IInspectable, + count: &mut u32, + values: &mut &mut GUID, + ) -> HRESULT, + pub GetRuntimeClassName: + unsafe extern "system" fn(this: IInspectable, value: &mut HSTRING) -> HRESULT, + pub GetTrustLevel: + unsafe extern "system" fn(this: IInspectable, value: &mut i32) -> HRESULT, + } + + #[repr(C)] + pub struct ITimeZoneOnCalendar_Vtbl { + pub base: IInspectable_Vtbl, + pub GetTimeZone: + unsafe extern "system" fn(this: ITimeZoneOnCalendar, result: &mut HSTRING) -> HRESULT, + pub ChangeTimeZone: + unsafe extern "system" fn(this: ITimeZoneOnCalendar, timezoneid: HSTRING) -> HRESULT, + pub TimeZoneAsFullString: + unsafe extern "system" fn(this: ITimeZoneOnCalendar, result: *mut HSTRING) -> HRESULT, + pub TimeZoneAsString: unsafe extern "system" fn( + this: ITimeZoneOnCalendar, + ideallength: i32, + result: &mut HSTRING, + ) -> HRESULT, + } + + impl Deref for IInspectable_Vtbl { + type Target = IUnknown_Vtbl; + + fn deref(&self) -> &Self::Target { + &self.base + } + } + + impl Deref for ITimeZoneOnCalendar_Vtbl { + type Target = IInspectable_Vtbl; + + fn deref(&self) -> &Self::Target { + &self.base + } + } +} diff --git a/javascript-engine/justfile b/javascript-engine/justfile index 1f3cd23..c8837ff 100644 --- a/javascript-engine/justfile +++ b/javascript-engine/justfile @@ -4,3 +4,6 @@ _: build: cargo build --target wasm32-unknown-unknown +tree: + cargo tree --target wasm32-unknown-unknown + diff --git a/javascript-engine/src/lib.rs b/javascript-engine/src/lib.rs index c17691e..de91705 100644 --- a/javascript-engine/src/lib.rs +++ b/javascript-engine/src/lib.rs @@ -1,11 +1,2 @@ -wit_bindgen_guest_rust::generate!("../interface.wit"); -struct Exports; - -export_smoke!(Exports); - -impl smoke::Smoke for Exports { - fn thunk() { - imports::thunk(); - } -} +mod mem; diff --git a/javascript-engine/src/mem.rs b/javascript-engine/src/mem.rs new file mode 100644 index 0000000..1e41c50 --- /dev/null +++ b/javascript-engine/src/mem.rs @@ -0,0 +1,13 @@ +#[no_mangle] +pub unsafe fn malloc(len: usize) -> *mut u8 { + let align = std::mem::align_of::(); + let layout = std::alloc::Layout::from_size_align_unchecked(len, align); + std::alloc::alloc(layout) +} + +#[no_mangle] +pub unsafe fn free(ptr: *mut u8, len: usize) { + let align = std::mem::align_of::(); + let layout = std::alloc::Layout::from_size_align_unchecked(len, align); + std::alloc::dealloc(ptr, layout) +}