From fdac8ece5b689bedf1253c805c81351a9470423f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Juli=C3=A1n=20Espina?= Date: Fri, 11 Nov 2022 17:21:07 +0000 Subject: [PATCH 01/24] Fix some Date tests (#2431) Found some tests on the `Date` builtin that were failing for incorrect length attributes and missing checks. --- boa_engine/src/builtins/date/mod.rs | 48 +++++++++++++---------------- boa_engine/src/value/integer.rs | 23 ++++++++++++++ boa_engine/src/value/mod.rs | 19 ++---------- 3 files changed, 47 insertions(+), 43 deletions(-) diff --git a/boa_engine/src/builtins/date/mod.rs b/boa_engine/src/builtins/date/mod.rs index b23877a7fc3..2319ce053be 100644 --- a/boa_engine/src/builtins/date/mod.rs +++ b/boa_engine/src/builtins/date/mod.rs @@ -12,7 +12,7 @@ use crate::{ }, string::utf16, symbol::WellKnownSymbols, - value::{JsValue, PreferredType}, + value::{IntegerOrInfinity, JsValue, PreferredType}, Context, JsError, JsResult, }; use boa_profiler::Profiler; @@ -121,9 +121,9 @@ impl BuiltIn for Date { .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", 2) - .method(Self::to_locale_string, "toLocaleString", 2) - .method(Self::to_locale_time_string, "toLocaleTimeString", 2) + .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) @@ -152,11 +152,16 @@ impl Date { /// /// [spec]: https://tc39.es/ecma262/#sec-timeclip #[inline] - pub fn time_clip(time: f64) -> Option { - if time.abs() > 8.64e15 { - None - } else { - Some(time) + pub fn time_clip(time: f64) -> Option { + // 1. If time is not finite, return NaN. + // 2. If abs(ℝ(time)) > 8.64 × 1015, return NaN. + // 3. Return 𝔽(! ToIntegerOrInfinity(time)). + if time.is_nan() { + return None; + } + match IntegerOrInfinity::from(time) { + IntegerOrInfinity::Integer(i) if i.abs() <= 864i64 * 10i64.pow(13) => Some(i), + _ => None, } } @@ -1329,26 +1334,17 @@ impl Date { this_time_value(this)?; // 2. Let t be ? ToNumber(time). - let t = if let Some(t) = args.get(0) { - let t = t.to_number(context)?; - let seconds = (t / 1_000f64) as i64; - let nanoseconds = ((t % 1_000f64) * 1_000_000f64) as u32; - Self( - ignore_ambiguity(Local.timestamp_opt(seconds, nanoseconds)) - .map(|dt| dt.naive_utc()), - ) - } else { - Self(None) - }; + let t = args.get_or_undefined(0).to_number(context)?; // 3. Let v be TimeClip(t). - let v = Self::get_time(this, args, context)?; + let v_int = Self::time_clip(t); + let v = v_int.map(|t| Local.timestamp_millis(t).naive_utc()); // 4. Set the [[DateValue]] internal slot of this Date object to v. - this.set_data(ObjectData::date(t)); + this.set_data(ObjectData::date(Date(v))); // 5. Return v. - Ok(v) + Ok(v_int.map_or(JsValue::Rational(f64::NAN), JsValue::from)) } /// `Date.prototype.setUTCDate()` @@ -1728,7 +1724,7 @@ impl Date { _args: &[JsValue], context: &mut Context, ) -> JsResult { - Self::to_utc_string(this, &[JsValue::Null], context) + Self::to_utc_string(this, &[], context) } /// `Date.prototype.toISOString()` @@ -1903,13 +1899,13 @@ impl Date { pub fn to_utc_string( this: &JsValue, _args: &[JsValue], - context: &mut Context, + _context: &mut Context, ) -> JsResult { if let Some(t) = this_time_value(this)?.0 { let utc_string = t.format("%a, %d %b %Y %H:%M:%S GMT").to_string(); Ok(JsValue::new(utc_string)) } else { - Ok(context.construct_error("Invalid time value")) + Ok(js_string!("Invalid Date").into()) } } diff --git a/boa_engine/src/value/integer.rs b/boa_engine/src/value/integer.rs index d4a1b281331..2cebeeadb31 100644 --- a/boa_engine/src/value/integer.rs +++ b/boa_engine/src/value/integer.rs @@ -30,6 +30,29 @@ impl IntegerOrInfinity { } } +impl From for IntegerOrInfinity { + fn from(number: f64) -> Self { + // `ToIntegerOrInfinity ( argument )` + if number.is_nan() || number == 0.0 { + // 2. If number is NaN, +0𝔽, or -0𝔽, return 0. + Self::Integer(0) + } else if number == f64::INFINITY { + // 3. If number is +∞𝔽, return +∞. + Self::PositiveInfinity + } else if number == f64::NEG_INFINITY { + // 4. If number is -∞𝔽, return -∞. + Self::NegativeInfinity + } else { + // 5. Let integer be floor(abs(ℝ(number))). + // 6. If number < +0𝔽, set integer to -integer. + let integer = number.abs().floor().copysign(number) as i64; + + // 7. Return integer. + Self::Integer(integer) + } + } +} + impl PartialEq for IntegerOrInfinity { fn eq(&self, other: &i64) -> bool { match self { diff --git a/boa_engine/src/value/mod.rs b/boa_engine/src/value/mod.rs index a5df0b672cf..4b4b46713b7 100644 --- a/boa_engine/src/value/mod.rs +++ b/boa_engine/src/value/mod.rs @@ -865,23 +865,8 @@ impl JsValue { // 1. Let number be ? ToNumber(argument). let number = self.to_number(context)?; - if number.is_nan() || number == 0.0 { - // 2. If number is NaN, +0𝔽, or -0𝔽, return 0. - Ok(IntegerOrInfinity::Integer(0)) - } else if number == f64::INFINITY { - // 3. If number is +∞𝔽, return +∞. - Ok(IntegerOrInfinity::PositiveInfinity) - } else if number == f64::NEG_INFINITY { - // 4. If number is -∞𝔽, return -∞. - Ok(IntegerOrInfinity::NegativeInfinity) - } else { - // 5. Let integer be floor(abs(ℝ(number))). - // 6. If number < +0𝔽, set integer to -integer. - let integer = number.abs().floor().copysign(number) as i64; - - // 7. Return integer. - Ok(IntegerOrInfinity::Integer(integer)) - } + // Continues on `IntegerOrInfinity::from::` + Ok(IntegerOrInfinity::from(number)) } /// Converts a value to a double precision floating point. From c2dd0271d0b1583d37d7b9d63907851ae319e4d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Borges?= Date: Mon, 14 Nov 2022 06:39:24 +0000 Subject: [PATCH 02/24] Switch tarpaulin to llvm engine (#2432) Trying this to see if it makes coverage numbers more accurate --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 58c181022a8..99ea7b3223d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -27,7 +27,7 @@ jobs: - name: Run cargo-tarpaulin uses: actions-rs/tarpaulin@v0.1 with: - args: --features intl --ignore-tests + args: --features intl --ignore-tests --engine llvm - name: Upload to codecov.io uses: codecov/codecov-action@v3 From e8e95f2311ac0bcc4e893b8e10f225d882a4e6cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 08:50:46 +0000 Subject: [PATCH 03/24] Bump test262 from `f6c48f3` to `1d5dc6b` (#2437) Bumps [test262](https://github.com/tc39/test262) from `f6c48f3` to `1d5dc6b`.
Commits
  • 1d5dc6b Remove tests involving the Emoji_Test property
  • e04de94 Line Terminator test description corrections
  • 0593463 Add missed changes for Symbols as WeakMap keys proposal.
  • See full diff in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- test262 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test262 b/test262 index f6c48f333e7..1d5dc6b577a 160000 --- a/test262 +++ b/test262 @@ -1 +1 @@ -Subproject commit f6c48f333e7d16bf11b30cd29b4ea9ac0354f142 +Subproject commit 1d5dc6b577ae6728c087f7d6143c3a5ccf88bcff From fcab8051d4a843855e4c9f99a6394823fa4e4473 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 12:54:54 +0000 Subject: [PATCH 04/24] Bump css-loader from 6.7.1 to 6.7.2 (#2434) Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 6.7.1 to 6.7.2.
Release notes

Sourced from css-loader's releases.

v6.7.2

6.7.2 (2022-11-13)

Bug Fixes

  • css modules generation with inline syntax (#1480) (2f4c273)
Changelog

Sourced from css-loader's changelog.

6.7.2 (2022-11-13)

Bug Fixes

  • css modules generation with inline syntax (#1480) (2f4c273)
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=css-loader&package-manager=npm_and_yarn&previous-version=6.7.1&new-version=6.7.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- package-lock.json | 46 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2ae907935ac..a6a69cff3e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "bootstrap": "^5.2.2", "clean-webpack-plugin": "^4.0.0", "copy-webpack-plugin": "^11.0.0", - "css-loader": "^6.7.1", + "css-loader": "^6.7.2", "file-loader": "^6.2.0", "html-webpack-plugin": "^5.5.0", "monaco-editor-webpack-plugin": "^7.0.1", @@ -1167,19 +1167,19 @@ } }, "node_modules/css-loader": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", - "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.2.tgz", + "integrity": "sha512-oqGbbVcBJkm8QwmnNzrFrWTnudnRZC+1eXikLJl0n4ljcfotgRifpg2a1lKy8jTrc4/d9A/ap1GFq1jDKG7J+Q==", "dev": true, "dependencies": { "icss-utils": "^5.1.0", - "postcss": "^8.4.7", + "postcss": "^8.4.18", "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.5" + "semver": "^7.3.8" }, "engines": { "node": ">= 12.13.0" @@ -3165,9 +3165,9 @@ } }, "node_modules/postcss": { - "version": "8.4.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", - "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", + "version": "8.4.19", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz", + "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==", "dev": true, "funding": [ { @@ -3636,9 +3636,9 @@ } }, "node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "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" @@ -5583,19 +5583,19 @@ } }, "css-loader": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", - "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.2.tgz", + "integrity": "sha512-oqGbbVcBJkm8QwmnNzrFrWTnudnRZC+1eXikLJl0n4ljcfotgRifpg2a1lKy8jTrc4/d9A/ap1GFq1jDKG7J+Q==", "dev": true, "requires": { "icss-utils": "^5.1.0", - "postcss": "^8.4.7", + "postcss": "^8.4.18", "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.5" + "semver": "^7.3.8" } }, "css-select": { @@ -7073,9 +7073,9 @@ } }, "postcss": { - "version": "8.4.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", - "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", + "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", @@ -7387,9 +7387,9 @@ } }, "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "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" diff --git a/package.json b/package.json index 9179820ca4d..f212f119db6 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "bootstrap": "^5.2.2", "clean-webpack-plugin": "^4.0.0", "copy-webpack-plugin": "^11.0.0", - "css-loader": "^6.7.1", + "css-loader": "^6.7.2", "file-loader": "^6.2.0", "html-webpack-plugin": "^5.5.0", "monaco-editor-webpack-plugin": "^7.0.1", From 4493254de7ecf3e0d216a5631802d626a11be4ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 13:03:57 +0000 Subject: [PATCH 05/24] Bump chrono from 0.4.22 to 0.4.23 (#2436) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.22 to 0.4.23.
Release notes

Sourced from chrono's releases.

0.4.23 is the next 0.4 release of the popular chrono date and time library for Rust. After the 0.4.20-0.4.22 series that brought chrono back to life after a long hiatus, development has been fairly quiet, allowing us to start planning changes for the 0.5.0 release. As such, we've started deprecating some APIs that are likely to be removed in 0.5. If you have any feedback on these changes, please let us know in the issue tracker!

Deprecations

  • Deprecate methods that have an _opt() alternative (#827)
  • Deprecate usage of the Date<Tz> type (#851)

Features

  • Optimize RFC 3339 (and RFC 2822) encoding (#844, thanks to @​conradludgate)
  • Addition and subtraction with the Days type (#784)
  • Add NaiveDateTime::from_timestamp_millis(_opt) (#818, thanks to @​Pscheidl -- backported in #823)
  • Allow for changing TZ variable and cache it for Local timezone (#853)
  • Add optional support for the arbitrary::Arbitrary trait (#849, thanks to @​greyblake and @​asayers)

Fixes

  • Support tzdb location on AIX (#826)
  • Fix warnings in documentation (#847)

On behalf of @​esheppa and @​djc, thanks to all contributors!

Commits
  • 9e5eb49 Bump version to 0.4.23
  • dc4287a store hash of environment variable
  • 57908e9 allow sharing of the allocated environment variable
  • 84f3c30 move last_changed to the Cache
  • 8bc4139 add bench for Local::now()
  • 22b4d32 Avoid use of deprecated API
  • 77317d5 Deprecate usage of the Date<Tz> type
  • 7ba090d Add TimeZone::with_ymd_and_hms() helper method
  • 03165c8 Move Date::years_since() implementation into NaiveDate
  • 645fca0 chore: apply clippy suggestions for 1.65
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=chrono&package-manager=cargo&previous-version=0.4.22&new-version=0.4.23)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- Cargo.lock | 4 ++-- boa_engine/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 423428b13ef..b3fa8672403 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -275,9 +275,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ "iana-time-zone", "js-sys", diff --git a/boa_engine/Cargo.toml b/boa_engine/Cargo.toml index 1db60c0a7f3..2d6fd774ee2 100644 --- a/boa_engine/Cargo.toml +++ b/boa_engine/Cargo.toml @@ -49,7 +49,7 @@ num-integer = "0.1.45" bitflags = "1.3.2" indexmap = "1.9.1" ryu-js = "0.2.2" -chrono = "0.4.22" +chrono = "0.4.23" fast-float = "0.2.0" unicode-normalization = "0.1.22" dyn-clone = "1.0.9" From 41d57f49e2b95e5ab02b8f739c50471d6cae7cea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 19:16:30 +0000 Subject: [PATCH 06/24] Bump clap from 4.0.22 to 4.0.23 (#2435) Bumps [clap](https://github.com/clap-rs/clap) from 4.0.22 to 4.0.23.
Release notes

Sourced from clap's releases.

v4.0.23

[4.0.23] - 2022-11-11

Fixes

  • Don't panic on reporting invalid-long errors when followed by invalid UTF8
  • (help) Clarified argument to help subcommand
Changelog

Sourced from clap's changelog.

[4.0.23] - 2022-11-11

Fixes

  • Don't panic on reporting invalid-long errors when followed by invalid UTF8
  • (help) Clarified argument to help subcommand
Commits
  • 95144b7 chore: Release
  • 20ecae1 docs: Update changelog
  • e6a3529 Merge pull request #4474 from epage/utf8
  • e9cbed3 fix(parser): Don't panic on invalid UTF-8 values
  • 45d26e0 test(parser): Show UTF8 bug
  • 4d69e56 Merge pull request #4471 from epage/assert
  • ec03972 test(assert): Verify empty positional assert exists
  • 0d27188 Merge pull request #4465 from epage/help
  • 9376a57 fix(help): Clarify that 'help' command accepts multiple
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=clap&package-manager=cargo&previous-version=4.0.22&new-version=4.0.23)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- Cargo.lock | 8 ++++---- boa_cli/Cargo.toml | 2 +- boa_tester/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b3fa8672403..96f665cea0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,7 +85,7 @@ dependencies = [ "boa_engine", "boa_interner", "boa_parser", - "clap 4.0.22", + "clap 4.0.23", "colored", "jemallocator", "phf", @@ -215,7 +215,7 @@ dependencies = [ "boa_gc", "boa_interner", "boa_parser", - "clap 4.0.22", + "clap 4.0.23", "colored", "fxhash", "gc", @@ -329,9 +329,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.0.22" +version = "4.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91b9970d7505127a162fdaa9b96428d28a479ba78c9ec7550a63a5d9863db682" +checksum = "0eb41c13df48950b20eb4cd0eefa618819469df1bffc49d11e8487c4ba0037e5" dependencies = [ "atty", "bitflags", diff --git a/boa_cli/Cargo.toml b/boa_cli/Cargo.toml index f9f4ea292c5..312e4d05a82 100644 --- a/boa_cli/Cargo.toml +++ b/boa_cli/Cargo.toml @@ -18,7 +18,7 @@ boa_interner.workspace = true boa_parser.workspace = true rustyline = "10.0.0" rustyline-derive = "0.7.0" -clap = { version = "4.0.22", features = ["derive"] } +clap = { version = "4.0.23", features = ["derive"] } serde_json = "1.0.87" colored = "2.0.0" regex = "1.7.0" diff --git a/boa_tester/Cargo.toml b/boa_tester/Cargo.toml index d3f9bc968f3..2d55ff8aeeb 100644 --- a/boa_tester/Cargo.toml +++ b/boa_tester/Cargo.toml @@ -16,7 +16,7 @@ boa_engine = { workspace = true, features = ["intl"] } boa_interner.workspace = true boa_gc.workspace = true boa_parser.workspace = true -clap = { version = "4.0.22", features = ["derive"] } +clap = { version = "4.0.23", features = ["derive"] } serde = { version = "1.0.147", features = ["derive"] } serde_yaml = "0.9.14" serde_json = "1.0.87" From 98e6dd36cb28eec2c7c5447c8dbf71d147469180 Mon Sep 17 00:00:00 2001 From: Kevin Date: Mon, 14 Nov 2022 19:59:51 +0000 Subject: [PATCH 07/24] Boa Gc implementation draft (#2394) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not sure if anyone else may be working on something more substantial/in-depth, but I thought I'd post this. 😄 The basic rundown is that this is more of an untested (and in some ways naïve) draft than anything else. It builds rather heavily on `rust-gc`, and tries to keep plenty of the core aspects so as to not break anything too much, and also to minimize overarching changes were it to actually be merged at some point. This implementation does add ~~a generational divide (although a little unoptimized) to the heap,~~ a GcAlloc/Collector struct with methods, and an ephemeron implementation that allows for the WeakPair and WeakGc pointers. --- Cargo.lock | 149 +++-- boa_engine/Cargo.toml | 1 - .../src/builtins/async_generator/mod.rs | 6 +- boa_engine/src/builtins/function/mod.rs | 10 +- boa_engine/src/builtins/generator/mod.rs | 4 +- boa_engine/src/builtins/promise/mod.rs | 4 +- boa_engine/src/bytecompiler/mod.rs | 4 +- boa_engine/src/environments/compile.rs | 8 +- boa_engine/src/environments/runtime.rs | 2 +- boa_engine/src/job.rs | 2 +- boa_engine/src/object/jsobject.rs | 14 +- boa_engine/src/realm.rs | 6 +- boa_engine/src/string/mod.rs | 4 +- boa_engine/src/symbol.rs | 4 +- boa_engine/src/vm/code_block.rs | 8 +- boa_examples/Cargo.toml | 1 - boa_gc/Cargo.toml | 3 +- boa_gc/src/cell.rs | 594 ++++++++++++++++++ boa_gc/src/internals/ephemeron_box.rs | 146 +++++ boa_gc/src/internals/gc_box.rs | 195 ++++++ boa_gc/src/internals/mod.rs | 5 + boa_gc/src/lib.rs | 385 +++++++++++- boa_gc/src/pointers/ephemeron.rs | 125 ++++ boa_gc/src/pointers/gc.rs | 275 ++++++++ boa_gc/src/pointers/mod.rs | 9 + boa_gc/src/pointers/weak.rs | 49 ++ boa_gc/src/test/allocation.rs | 31 + boa_gc/src/test/cell.rs | 15 + boa_gc/src/test/mod.rs | 37 ++ boa_gc/src/test/weak.rs | 133 ++++ boa_gc/src/trace.rs | 450 +++++++++++++ boa_macros/Cargo.toml | 3 +- boa_macros/src/lib.rs | 101 +++ boa_tester/Cargo.toml | 1 - boa_tester/src/exec/mod.rs | 6 +- 35 files changed, 2662 insertions(+), 128 deletions(-) create mode 100644 boa_gc/src/cell.rs create mode 100644 boa_gc/src/internals/ephemeron_box.rs create mode 100644 boa_gc/src/internals/gc_box.rs create mode 100644 boa_gc/src/internals/mod.rs create mode 100644 boa_gc/src/pointers/ephemeron.rs create mode 100644 boa_gc/src/pointers/gc.rs create mode 100644 boa_gc/src/pointers/mod.rs create mode 100644 boa_gc/src/pointers/weak.rs create mode 100644 boa_gc/src/test/allocation.rs create mode 100644 boa_gc/src/test/cell.rs create mode 100644 boa_gc/src/test/mod.rs create mode 100644 boa_gc/src/test/weak.rs create mode 100644 boa_gc/src/trace.rs diff --git a/Cargo.lock b/Cargo.lock index 96f665cea0d..1447e5da805 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -112,7 +112,6 @@ dependencies = [ "dyn-clone", "fast-float", "float-cmp", - "gc", "icu_datetime", "icu_locale_canonicalizer", "icu_locid", @@ -148,14 +147,14 @@ dependencies = [ "boa_gc", "boa_interner", "boa_parser", - "gc", ] [[package]] name = "boa_gc" version = "0.16.0" dependencies = [ - "gc", + "boa_macros", + "boa_profiler", "measureme", ] @@ -177,8 +176,10 @@ dependencies = [ name = "boa_macros" version = "0.16.0" dependencies = [ + "proc-macro2", "quote", "syn", + "synstructure", ] [[package]] @@ -218,7 +219,6 @@ dependencies = [ "clap 4.0.23", "colored", "fxhash", - "gc", "once_cell", "rayon", "regex", @@ -245,9 +245,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.11.0" +version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" [[package]] name = "byteorder" @@ -263,9 +263,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" [[package]] name = "cfg-if" @@ -317,9 +317,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.22" +version = "3.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" +checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" dependencies = [ "bitflags", "clap_lex 0.2.4", @@ -421,7 +421,7 @@ dependencies = [ "atty", "cast", "ciborium", - "clap 3.2.22", + "clap 3.2.23", "criterion-plot", "itertools", "lazy_static", @@ -492,9 +492,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.78" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f39818dcfc97d45b03953c1292efc4e80954e1583c4aa770bac1383e2310a4" +checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a" dependencies = [ "cc", "cxxbridge-flags", @@ -504,9 +504,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.78" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e580d70777c116df50c390d1211993f62d40302881e54d4b79727acb83d0199" +checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827" dependencies = [ "cc", "codespan-reporting", @@ -519,15 +519,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.78" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56a46460b88d1cec95112c8c363f0e2c39afdb237f60583b0b36343bf627ea9c" +checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a" [[package]] name = "cxxbridge-macro" -version = "1.0.78" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747b608fecf06b0d72d440f27acc99288207324b793be2c17991839f3d4995ea" +checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" dependencies = [ "proc-macro2", "quote", @@ -634,9 +634,9 @@ checksum = "95765f67b4b18863968b4a1bd5bb576f732b29a4a28c7cd84c09fa3e2875f33c" [[package]] name = "fd-lock" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e11dcc7e4d79a8c89b9ab4c6f5c30b1fc4a83c420792da3542fd31179ed5f517" +checksum = "0c93a581058d957dc4176875aad04f82f81613e6611d64aa1a9c755bdfb16711" dependencies = [ "cfg-if", "rustix", @@ -679,27 +679,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "gc" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edaac0f5832202ebc99520cb77c932248010c4645d20be1dc62d6579f5b3752" -dependencies = [ - "gc_derive", -] - -[[package]] -name = "gc_derive" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60df8444f094ff7885631d80e78eb7d88c3c2361a98daaabb06256e4500db941" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "getrandom" version = "0.2.8" @@ -742,9 +721,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.51" +version = "0.1.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a6ef98976b22b3b7f2f3a806f858cb862044cfa66805aa3ad84cb3d3b785ed" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -756,9 +735,9 @@ dependencies = [ [[package]] name = "iana-time-zone-haiku" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde6edd6cef363e9359ed3c98ba64590ba9eecba2293eb5a723ab32aee8926aa" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" dependencies = [ "cxx", "cxx-build", @@ -914,9 +893,9 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ea37f355c05dde75b84bba2d767906ad522e97cd9e2eef2be7a4ab7fb442c06" +checksum = "59ce5ef949d49ee85593fc4d3f3f95ad61657076395cbbce23e2121fc5542074" [[package]] name = "itertools" @@ -971,9 +950,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.135" +version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" [[package]] name = "link-cplusplus" @@ -1111,9 +1090,9 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" dependencies = [ "hermit-abi", "libc", @@ -1133,9 +1112,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "os_str_bytes" -version = "6.3.0" +version = "6.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" [[package]] name = "parking_lot" @@ -1259,9 +1238,9 @@ checksum = "7c68cb38ed13fd7bc9dd5db8f165b7c8d9c1a315104083a2b10f11354c2af97f" [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro-error" @@ -1289,9 +1268,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ "unicode-ident", ] @@ -1402,9 +1381,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "regress" @@ -1423,9 +1402,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.35.11" +version = "0.35.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbb2fda4666def1433b1b05431ab402e42a1084285477222b72d6c564c417cef" +checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9" dependencies = [ "bitflags", "errno", @@ -1644,9 +1623,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.15.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" @@ -1893,46 +1872,60 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ + "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", + "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" [[package]] name = "writeable" @@ -1975,9 +1968,9 @@ dependencies = [ [[package]] name = "zerofrom-derive" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8785f47d6062c1932866147f91297286a9f350b3070e9d9f0b6078e37d623c1a" +checksum = "2e8aa86add9ddbd2409c1ed01e033cd457d79b1b1229b64922c25095c595e829" dependencies = [ "proc-macro2", "quote", diff --git a/boa_engine/Cargo.toml b/boa_engine/Cargo.toml index 2d6fd774ee2..579e3e83946 100644 --- a/boa_engine/Cargo.toml +++ b/boa_engine/Cargo.toml @@ -37,7 +37,6 @@ boa_profiler.workspace = true boa_macros.workspace = true boa_ast.workspace = true boa_parser.workspace = true -gc = "0.4.1" serde = { version = "1.0.147", features = ["derive", "rc"] } serde_json = "1.0.87" rand = "0.8.5" diff --git a/boa_engine/src/builtins/async_generator/mod.rs b/boa_engine/src/builtins/async_generator/mod.rs index e6ea7180641..768dfd48a62 100644 --- a/boa_engine/src/builtins/async_generator/mod.rs +++ b/boa_engine/src/builtins/async_generator/mod.rs @@ -18,7 +18,7 @@ use crate::{ vm::GeneratorResumeKind, Context, JsError, JsResult, }; -use boa_gc::{Cell, Finalize, Gc, Trace}; +use boa_gc::{Finalize, Gc, GcCell, Trace}; use boa_profiler::Profiler; use std::collections::VecDeque; @@ -56,7 +56,7 @@ pub struct AsyncGenerator { pub(crate) state: AsyncGeneratorState, /// The `[[AsyncGeneratorContext]]` internal slot. - pub(crate) context: Option>>, + pub(crate) context: Option>>, /// The `[[AsyncGeneratorQueue]]` internal slot. pub(crate) queue: VecDeque, @@ -511,7 +511,7 @@ impl AsyncGenerator { pub(crate) fn resume( generator: &JsObject, state: AsyncGeneratorState, - generator_context: &Gc>, + generator_context: &Gc>, completion: (JsResult, bool), context: &mut Context, ) { diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index 5cc70deec57..ebd927fd795 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -34,7 +34,7 @@ use boa_ast::{ operations::{bound_names, contains, lexically_declared_names, ContainsSymbol}, StatementList, }; -use boa_gc::{self, custom_trace, Finalize, Gc, Trace}; +use boa_gc::{self, custom_trace, Finalize, Gc, GcCell, Trace}; use boa_interner::Sym; use boa_parser::Parser; use boa_profiler::Profiler; @@ -178,7 +178,7 @@ unsafe impl Trace for ClassFieldDefinition { /// with `Any::downcast_ref` and `Any::downcast_mut` to recover the original /// type. #[derive(Clone, Debug, Trace, Finalize)] -pub struct Captures(Gc>>); +pub struct Captures(Gc>>); impl Captures { /// Creates a new capture context. @@ -186,7 +186,7 @@ impl Captures { where T: NativeObject, { - Self(Gc::new(boa_gc::Cell::new(Box::new(captures)))) + Self(Gc::new(GcCell::new(Box::new(captures)))) } /// Casts `Captures` to `Any` @@ -194,7 +194,7 @@ impl Captures { /// # Panics /// /// Panics if it's already borrowed as `&mut Any` - pub fn as_any(&self) -> boa_gc::Ref<'_, dyn Any> { + pub fn as_any(&self) -> boa_gc::GcCellRef<'_, dyn Any> { Ref::map(self.0.borrow(), |data| data.deref().as_any()) } @@ -203,7 +203,7 @@ impl Captures { /// # Panics /// /// Panics if it's already borrowed as `&mut Any` - pub fn as_mut_any(&self) -> boa_gc::RefMut<'_, Box, dyn Any> { + pub fn as_mut_any(&self) -> boa_gc::GcCellRefMut<'_, Box, dyn Any> { RefMut::map(self.0.borrow_mut(), |data| data.deref_mut().as_mut_any()) } } diff --git a/boa_engine/src/builtins/generator/mod.rs b/boa_engine/src/builtins/generator/mod.rs index 74eea06fc13..7599dd90742 100644 --- a/boa_engine/src/builtins/generator/mod.rs +++ b/boa_engine/src/builtins/generator/mod.rs @@ -20,7 +20,7 @@ use crate::{ vm::{CallFrame, GeneratorResumeKind, ReturnType}, Context, JsError, JsResult, }; -use boa_gc::{Cell, Finalize, Gc, Trace}; +use boa_gc::{Finalize, Gc, GcCell, Trace}; use boa_profiler::Profiler; /// Indicates the state of a generator. @@ -52,7 +52,7 @@ pub struct Generator { pub(crate) state: GeneratorState, /// The `[[GeneratorContext]]` internal slot. - pub(crate) context: Option>>, + pub(crate) context: Option>>, } impl BuiltIn for Generator { diff --git a/boa_engine/src/builtins/promise/mod.rs b/boa_engine/src/builtins/promise/mod.rs index ae4ebef81e8..7bdf64cbc7a 100644 --- a/boa_engine/src/builtins/promise/mod.rs +++ b/boa_engine/src/builtins/promise/mod.rs @@ -21,7 +21,7 @@ use crate::{ value::JsValue, Context, JsError, JsResult, }; -use boa_gc::{Cell as GcCell, Finalize, Gc, Trace}; +use boa_gc::{Finalize, Gc, GcCell, Trace}; use boa_profiler::Profiler; use std::{cell::Cell, rc::Rc}; use tap::{Conv, Pipe}; @@ -118,7 +118,7 @@ impl PromiseCapability { // 2. NOTE: C is assumed to be a constructor function that supports the parameter conventions of the Promise constructor (see 27.2.3.1). // 3. Let promiseCapability be the PromiseCapability Record { [[Promise]]: undefined, [[Resolve]]: undefined, [[Reject]]: undefined }. - let promise_capability = Gc::new(boa_gc::Cell::new(RejectResolve { + let promise_capability = Gc::new(GcCell::new(RejectResolve { reject: JsValue::undefined(), resolve: JsValue::undefined(), })); diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 33dec83bbc2..8bff029b4e8 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -30,7 +30,7 @@ use boa_ast::{ }, Declaration, Expression, Statement, StatementList, StatementListItem, }; -use boa_gc::Gc; +use boa_gc::{Gc, GcCell}; use boa_interner::{Interner, Sym}; use rustc_hash::FxHashMap; use std::mem::size_of; @@ -265,7 +265,7 @@ impl<'b> ByteCompiler<'b> { #[inline] fn push_compile_environment( &mut self, - environment: Gc>, + environment: Gc>, ) -> usize { let index = self.code_block.compile_environments.len(); self.code_block.compile_environments.push(environment); diff --git a/boa_engine/src/environments/compile.rs b/boa_engine/src/environments/compile.rs index 37aa9a64ec4..1ff6f3faf78 100644 --- a/boa_engine/src/environments/compile.rs +++ b/boa_engine/src/environments/compile.rs @@ -2,7 +2,7 @@ use crate::{ environments::runtime::BindingLocator, property::PropertyDescriptor, Context, JsString, JsValue, }; use boa_ast::expression::Identifier; -use boa_gc::{Cell, Finalize, Gc, Trace}; +use boa_gc::{Finalize, Gc, GcCell, Trace}; use rustc_hash::FxHashMap; @@ -22,7 +22,7 @@ struct CompileTimeBinding { /// A compile time environment also indicates, if it is a function environment. #[derive(Debug, Finalize, Trace)] pub(crate) struct CompileTimeEnvironment { - outer: Option>>, + outer: Option>>, environment_index: usize, #[unsafe_ignore_trace] bindings: FxHashMap, @@ -223,7 +223,7 @@ impl Context { let environment_index = self.realm.compile_env.borrow().environment_index + 1; let outer = self.realm.compile_env.clone(); - self.realm.compile_env = Gc::new(Cell::new(CompileTimeEnvironment { + self.realm.compile_env = Gc::new(GcCell::new(CompileTimeEnvironment { outer: Some(outer), environment_index, bindings: FxHashMap::default(), @@ -241,7 +241,7 @@ impl Context { #[inline] pub(crate) fn pop_compile_time_environment( &mut self, - ) -> (usize, Gc>) { + ) -> (usize, Gc>) { let current_env_borrow = self.realm.compile_env.borrow(); if let Some(outer) = ¤t_env_borrow.outer { let outer_clone = outer.clone(); diff --git a/boa_engine/src/environments/runtime.rs b/boa_engine/src/environments/runtime.rs index 3314eb75e51..c06ab965724 100644 --- a/boa_engine/src/environments/runtime.rs +++ b/boa_engine/src/environments/runtime.rs @@ -3,7 +3,7 @@ use std::cell::Cell; use crate::{ environments::CompileTimeEnvironment, error::JsNativeError, object::JsObject, Context, JsValue, }; -use boa_gc::{Cell as GcCell, Finalize, Gc, Trace}; +use boa_gc::{Finalize, Gc, GcCell, Trace}; use boa_ast::expression::Identifier; use rustc_hash::FxHashSet; diff --git a/boa_engine/src/job.rs b/boa_engine/src/job.rs index 2a0988b2e57..f24be132a3a 100644 --- a/boa_engine/src/job.rs +++ b/boa_engine/src/job.rs @@ -1,5 +1,5 @@ use crate::{prelude::JsObject, Context, JsResult, JsValue}; -use gc::{Finalize, Trace}; +use boa_gc::{Finalize, Trace}; /// `JobCallback` records /// diff --git a/boa_engine/src/object/jsobject.rs b/boa_engine/src/object/jsobject.rs index d1afe8f99aa..6bcf077f9bd 100644 --- a/boa_engine/src/object/jsobject.rs +++ b/boa_engine/src/object/jsobject.rs @@ -10,7 +10,7 @@ use crate::{ value::PreferredType, Context, JsResult, JsValue, }; -use boa_gc::{self, Finalize, Gc, Trace}; +use boa_gc::{self, Finalize, Gc, GcCell, Trace}; use rustc_hash::FxHashMap; use std::{ cell::RefCell, @@ -21,15 +21,15 @@ use std::{ }; /// A wrapper type for an immutably borrowed type T. -pub type Ref<'a, T> = boa_gc::Ref<'a, T>; +pub type Ref<'a, T> = boa_gc::GcCellRef<'a, T>; /// A wrapper type for a mutably borrowed type T. -pub type RefMut<'a, T, U> = boa_gc::RefMut<'a, T, U>; +pub type RefMut<'a, T, U> = boa_gc::GcCellRefMut<'a, T, U>; /// Garbage collected `Object`. #[derive(Trace, Finalize, Clone, Default)] pub struct JsObject { - inner: Gc>, + inner: Gc>, } impl JsObject { @@ -37,7 +37,7 @@ impl JsObject { #[inline] fn from_object(object: Object) -> Self { Self { - inner: Gc::new(boa_gc::Cell::new(object)), + inner: Gc::new(GcCell::new(object)), } } @@ -738,9 +738,9 @@ Cannot both specify accessors and a value or writable attribute", } } -impl AsRef> for JsObject { +impl AsRef> for JsObject { #[inline] - fn as_ref(&self) -> &boa_gc::Cell { + fn as_ref(&self) -> &GcCell { &self.inner } } diff --git a/boa_engine/src/realm.rs b/boa_engine/src/realm.rs index 588f01fd16c..ab8cf8dfbd9 100644 --- a/boa_engine/src/realm.rs +++ b/boa_engine/src/realm.rs @@ -8,7 +8,7 @@ use crate::{ environments::{CompileTimeEnvironment, DeclarativeEnvironmentStack}, object::{GlobalPropertyMap, JsObject, JsPrototype, ObjectData, PropertyMap}, }; -use boa_gc::{Cell, Gc}; +use boa_gc::{Gc, GcCell}; use boa_profiler::Profiler; /// Representation of a Realm. @@ -21,7 +21,7 @@ pub struct Realm { pub(crate) global_property_map: PropertyMap, pub(crate) global_prototype: JsPrototype, pub(crate) environments: DeclarativeEnvironmentStack, - pub(crate) compile_env: Gc>, + pub(crate) compile_env: Gc>, } impl Realm { @@ -33,7 +33,7 @@ impl Realm { // Allow identification of the global object easily let global_object = JsObject::from_proto_and_data(None, ObjectData::global()); - let global_compile_environment = Gc::new(Cell::new(CompileTimeEnvironment::new_global())); + let global_compile_environment = Gc::new(GcCell::new(CompileTimeEnvironment::new_global())); Self { global_object, diff --git a/boa_engine/src/string/mod.rs b/boa_engine/src/string/mod.rs index 2b6ea514eb8..2932e76b9b7 100644 --- a/boa_engine/src/string/mod.rs +++ b/boa_engine/src/string/mod.rs @@ -24,7 +24,7 @@ mod common; use crate::{builtins::string::is_trimmable_whitespace, JsBigInt}; -use boa_gc::{unsafe_empty_trace, Finalize, Trace}; +use boa_gc::{empty_trace, Finalize, Trace}; pub use boa_macros::utf16; use std::{ @@ -292,7 +292,7 @@ sa::assert_eq_size!(JsString, *const ()); // Safety: `JsString` does not contain any objects which needs to be traced, so this is safe. unsafe impl Trace for JsString { - unsafe_empty_trace!(); + empty_trace!(); } impl JsString { diff --git a/boa_engine/src/symbol.rs b/boa_engine/src/symbol.rs index f7f44488294..c0b69c841f2 100644 --- a/boa_engine/src/symbol.rs +++ b/boa_engine/src/symbol.rs @@ -16,7 +16,7 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol use crate::{js_string, string::utf16, JsString}; -use boa_gc::{unsafe_empty_trace, Finalize, Trace}; +use boa_gc::{empty_trace, Finalize, Trace}; use std::{ cell::Cell, hash::{Hash, Hasher}, @@ -255,7 +255,7 @@ pub struct JsSymbol { // Safety: JsSymbol does not contain any objects which needs to be traced, // so this is safe. unsafe impl Trace for JsSymbol { - unsafe_empty_trace!(); + empty_trace!(); } impl JsSymbol { diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index a8444e20725..02f52e097f2 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -24,7 +24,7 @@ use crate::{ Context, JsResult, JsString, JsValue, }; use boa_ast::{expression::Identifier, function::FormalParameterList}; -use boa_gc::{Cell, Finalize, Gc, Trace}; +use boa_gc::{Finalize, Gc, GcCell, Trace}; use boa_interner::{Interner, Sym, ToInternedString}; use boa_profiler::Profiler; use std::{collections::VecDeque, convert::TryInto, mem::size_of}; @@ -103,7 +103,7 @@ pub struct CodeBlock { pub(crate) arguments_binding: Option, /// Compile time environments in this function. - pub(crate) compile_environments: Vec>>, + pub(crate) compile_environments: Vec>>, /// The `[[IsClassConstructor]]` internal slot. pub(crate) is_class_constructor: bool, @@ -1099,7 +1099,7 @@ impl JsObject { prototype, ObjectData::generator(Generator { state: GeneratorState::SuspendedStart, - context: Some(Gc::new(Cell::new(GeneratorContext { + context: Some(Gc::new(GcCell::new(GeneratorContext { environments, call_frame, stack, @@ -1242,7 +1242,7 @@ impl JsObject { prototype, ObjectData::async_generator(AsyncGenerator { state: AsyncGeneratorState::SuspendedStart, - context: Some(Gc::new(Cell::new(GeneratorContext { + context: Some(Gc::new(GcCell::new(GeneratorContext { environments, call_frame, stack, diff --git a/boa_examples/Cargo.toml b/boa_examples/Cargo.toml index 28b1be49752..b6135a7dc82 100644 --- a/boa_examples/Cargo.toml +++ b/boa_examples/Cargo.toml @@ -17,4 +17,3 @@ boa_ast.workspace = true boa_interner.workspace = true boa_gc.workspace = true boa_parser.workspace = true -gc = "0.4.1" diff --git a/boa_gc/Cargo.toml b/boa_gc/Cargo.toml index 5cca97b53be..ceaa195cdc6 100644 --- a/boa_gc/Cargo.toml +++ b/boa_gc/Cargo.toml @@ -11,7 +11,8 @@ repository.workspace = true rust-version.workspace = true [dependencies] -gc = { version = "0.4.1", features = ["derive"] } +boa_profiler.workspace = true +boa_macros.workspace = true # Optional Dependencies measureme = { version = "10.1.0", optional = true } diff --git a/boa_gc/src/cell.rs b/boa_gc/src/cell.rs new file mode 100644 index 00000000000..36607629347 --- /dev/null +++ b/boa_gc/src/cell.rs @@ -0,0 +1,594 @@ +//! A garbage collected cell implementation +use std::cell::{Cell, UnsafeCell}; +use std::cmp::Ordering; +use std::fmt::{self, Debug, Display}; +use std::hash::Hash; +use std::ops::{Deref, DerefMut}; + +use crate::trace::{Finalize, Trace}; + +/// `BorrowFlag` represent the internal state of a `GcCell` and +/// keeps track of the amount of current borrows. +#[derive(Copy, Clone)] +pub(crate) struct BorrowFlag(usize); + +/// `BorrowState` represents the various states of a `BorrowFlag` +/// +/// - Reading: the value is currently being read/borrowed. +/// - Writing: the value is currently being written/borrowed mutably. +/// - Unused: the value is currently unrooted. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub(crate) enum BorrowState { + Reading, + Writing, + Unused, +} + +const ROOT: usize = 1; +const WRITING: usize = !1; +const UNUSED: usize = 0; + +/// The base borrowflag init is rooted, and has no outstanding borrows. +pub(crate) const BORROWFLAG_INIT: BorrowFlag = BorrowFlag(ROOT); + +impl BorrowFlag { + /// Check the current `BorrowState` of `BorrowFlag`. + #[inline] + pub(crate) fn borrowed(self) -> BorrowState { + match self.0 & !ROOT { + UNUSED => BorrowState::Unused, + WRITING => BorrowState::Writing, + _ => BorrowState::Reading, + } + } + + /// Check whether the borrow bit is flagged. + #[inline] + pub(crate) fn rooted(self) -> bool { + self.0 & ROOT > 0 + } + + /// Set the `BorrowFlag`'s state to writing. + #[inline] + pub(crate) fn set_writing(self) -> Self { + // Set every bit other than the root bit, which is preserved + Self(self.0 | WRITING) + } + + /// Remove the root flag on `BorrowFlag` + #[inline] + pub(crate) fn set_unused(self) -> Self { + // Clear every bit other than the root bit, which is preserved + Self(self.0 & ROOT) + } + + /// Increments the counter for a new borrow. + /// + /// # Panic + /// - This method will panic if the current `BorrowState` is writing. + /// - This method will panic after incrementing if the borrow count overflows. + #[inline] + pub(crate) fn add_reading(self) -> Self { + assert!(self.borrowed() != BorrowState::Writing); + // Add 1 to the integer starting at the second binary digit. As our + // borrowstate is not writing, we know that overflow cannot happen, so + // this is equivalent to the following, more complicated, expression: + // + // BorrowFlag((self.0 & ROOT) | (((self.0 >> 1) + 1) << 1)) + let flags = Self(self.0 + 0b10); + + // This will fail if the borrow count overflows, which shouldn't happen, + // but let's be safe + { + assert!(flags.borrowed() == BorrowState::Reading); + } + flags + } + + /// Decrements the counter to remove a borrow. + /// + /// # Panic + /// - This method will panic if the current `BorrowState` is not reading. + #[inline] + pub(crate) fn sub_reading(self) -> Self { + assert!(self.borrowed() == BorrowState::Reading); + // Subtract 1 from the integer starting at the second binary digit. As + // our borrowstate is not writing or unused, we know that overflow or + // undeflow cannot happen, so this is equivalent to the following, more + // complicated, expression: + // + // BorrowFlag((self.0 & ROOT) | (((self.0 >> 1) - 1) << 1)) + Self(self.0 - 0b10) + } + + /// Set the root flag on the `BorrowFlag`. + #[inline] + pub(crate) fn set_rooted(self, rooted: bool) -> Self { + // Preserve the non-root bits + Self((self.0 & !ROOT) | (usize::from(rooted))) + } +} + +impl Debug for BorrowFlag { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("BorrowFlag") + .field("Rooted", &self.rooted()) + .field("State", &self.borrowed()) + .finish() + } +} + +/// A mutable memory location with dynamically checked borrow rules +/// that can be used inside of a garbage-collected pointer. +/// +/// This object is a `RefCell` that can be used inside of a `Gc`. +pub struct GcCell { + pub(crate) flags: Cell, + pub(crate) cell: UnsafeCell, +} + +impl GcCell { + /// Creates a new `GcCell` containing `value`. + #[inline] + pub fn new(value: T) -> Self { + Self { + flags: Cell::new(BORROWFLAG_INIT), + cell: UnsafeCell::new(value), + } + } + + /// Consumes the `GcCell`, returning the wrapped value. + #[inline] + pub fn into_inner(self) -> T { + self.cell.into_inner() + } +} + +impl GcCell { + /// Immutably borrows the wrapped value. + /// + /// The borrow lasts until the returned `GcCellRef` exits scope. + /// Multiple immutable borrows can be taken out at the same time. + /// + /// # Panics + /// + /// Panics if the value is currently mutably borrowed. + #[inline] + pub fn borrow(&self) -> GcCellRef<'_, T> { + match self.try_borrow() { + Ok(value) => value, + Err(e) => panic!("{}", e), + } + } + + /// Mutably borrows the wrapped value. + /// + /// The borrow lasts until the returned `GcCellRefMut` exits scope. + /// The value cannot be borrowed while this borrow is active. + /// + /// # Panics + /// + /// Panics if the value is currently borrowed. + #[inline] + pub fn borrow_mut(&self) -> GcCellRefMut<'_, T> { + match self.try_borrow_mut() { + Ok(value) => value, + Err(e) => panic!("{}", e), + } + } + + /// Immutably borrows the wrapped value, returning an error if the value is currently mutably + /// borrowed. + /// + /// The borrow lasts until the returned `GcCellRef` exits scope. Multiple immutable borrows can be + /// taken out at the same time. + /// + /// This is the non-panicking variant of [`borrow`](#method.borrow). + /// + /// # Errors + /// + /// Returns an `Err` if the value is currently mutably borrowed. + pub fn try_borrow(&self) -> Result, BorrowError> { + if self.flags.get().borrowed() == BorrowState::Writing { + return Err(BorrowError); + } + self.flags.set(self.flags.get().add_reading()); + + // SAFETY: calling value on a rooted value may cause Undefined Behavior + unsafe { + Ok(GcCellRef { + flags: &self.flags, + value: &*self.cell.get(), + }) + } + } + + /// Mutably borrows the wrapped value, returning an error if the value is currently borrowed. + /// + /// The borrow lasts until the returned `GcCellRefMut` exits scope. + /// The value cannot be borrowed while this borrow is active. + /// + /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut). + /// + /// # Errors + /// + /// Returns an `Err` if the value is currently borrowed. + pub fn try_borrow_mut(&self) -> Result, BorrowMutError> { + if self.flags.get().borrowed() != BorrowState::Unused { + return Err(BorrowMutError); + } + self.flags.set(self.flags.get().set_writing()); + + // SAFETY: This is safe as the value is rooted if it was not previously rooted, + // so it cannot be dropped. + unsafe { + // Force the val_ref's contents to be rooted for the duration of the + // mutable borrow + if !self.flags.get().rooted() { + (*self.cell.get()).root(); + } + + Ok(GcCellRefMut { + gc_cell: self, + value: &mut *self.cell.get(), + }) + } + } +} + +/// An error returned by [`GcCell::try_borrow`](struct.GcCell.html#method.try_borrow). +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Default, Hash)] +pub struct BorrowError; + +impl Display for BorrowError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Display::fmt("GcCell already mutably borrowed", f) + } +} + +/// An error returned by [`GcCell::try_borrow_mut`](struct.GcCell.html#method.try_borrow_mut). +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Default, Hash)] +pub struct BorrowMutError; + +impl Display for BorrowMutError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Display::fmt("GcCell already borrowed", f) + } +} + +impl Finalize for GcCell {} + +// SAFETY: GcCell maintains it's own BorrowState and rootedness. GcCell's implementation +// focuses on only continuing Trace based methods while the cell state is not written. +// Implementing a Trace while the cell is being written to or incorrectly implementing Trace +// on GcCell's value may cause Undefined Behavior +unsafe impl Trace for GcCell { + #[inline] + unsafe fn trace(&self) { + match self.flags.get().borrowed() { + BorrowState::Writing => (), + // SAFETY: Please see GcCell's Trace impl Safety note. + _ => unsafe { (*self.cell.get()).trace() }, + } + } + + #[inline] + unsafe fn weak_trace(&self) { + match self.flags.get().borrowed() { + BorrowState::Writing => (), + // SAFETY: Please see GcCell's Trace impl Safety note. + _ => unsafe { (*self.cell.get()).weak_trace() }, + } + } + + unsafe fn root(&self) { + assert!(!self.flags.get().rooted(), "Can't root a GcCell twice!"); + self.flags.set(self.flags.get().set_rooted(true)); + + match self.flags.get().borrowed() { + BorrowState::Writing => (), + // SAFETY: Please see GcCell's Trace impl Safety note. + _ => unsafe { (*self.cell.get()).root() }, + } + } + + #[inline] + unsafe fn unroot(&self) { + assert!(self.flags.get().rooted(), "Can't unroot a GcCell twice!"); + self.flags.set(self.flags.get().set_rooted(false)); + + match self.flags.get().borrowed() { + BorrowState::Writing => (), + // SAFETY: Please see GcCell's Trace impl Safety note. + _ => unsafe { (*self.cell.get()).unroot() }, + } + } + + #[inline] + fn run_finalizer(&self) { + Finalize::finalize(self); + match self.flags.get().borrowed() { + BorrowState::Writing => (), + // SAFETY: Please see GcCell's Trace impl Safety note. + _ => unsafe { (*self.cell.get()).run_finalizer() }, + } + } +} + +/// A wrapper type for an immutably borrowed value from a `GcCell`. +pub struct GcCellRef<'a, T: ?Sized + 'static> { + pub(crate) flags: &'a Cell, + pub(crate) value: &'a T, +} + +impl<'a, T: ?Sized> GcCellRef<'a, T> { + /// Copies a `GcCellRef`. + /// + /// The `GcCell` is already immutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `GcCellRef::clone(...)`. A `Clone` implementation or a method + /// would interfere with the use of `c.borrow().clone()` to clone + /// the contents of a `GcCell`. + #[inline] + #[allow(clippy::should_implement_trait)] + #[must_use] + pub fn clone(orig: &GcCellRef<'a, T>) -> GcCellRef<'a, T> { + orig.flags.set(orig.flags.get().add_reading()); + GcCellRef { + flags: orig.flags, + value: orig.value, + } + } + + /// Makes a new `GcCellRef` from a component of the borrowed data. + /// + /// The `GcCell` is already immutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as `GcCellRef::map(...)`. + /// A method would interfere with methods of the same name on the contents + /// of a `GcCellRef` used through `Deref`. + #[inline] + pub fn map(orig: Self, f: F) -> GcCellRef<'a, U> + where + U: ?Sized, + F: FnOnce(&T) -> &U, + { + let ret = GcCellRef { + flags: orig.flags, + value: f(orig.value), + }; + + // We have to tell the compiler not to call the destructor of GcCellRef, + // because it will update the borrow flags. + std::mem::forget(orig); + + ret + } + + /// Splits a `GcCellRef` into multiple `GcCellRef`s for different components of the borrowed data. + /// + /// The `GcCell` is already immutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as `GcCellRef::map_split(...)`. + /// A method would interfere with methods of the same name on the contents of a `GcCellRef` used through `Deref`. + #[inline] + pub fn map_split(orig: Self, f: F) -> (GcCellRef<'a, U>, GcCellRef<'a, V>) + where + U: ?Sized, + V: ?Sized, + F: FnOnce(&T) -> (&U, &V), + { + let (a, b) = f(orig.value); + + orig.flags.set(orig.flags.get().add_reading()); + + let ret = ( + GcCellRef { + flags: orig.flags, + value: a, + }, + GcCellRef { + flags: orig.flags, + value: b, + }, + ); + + // We have to tell the compiler not to call the destructor of GcCellRef, + // because it will update the borrow flags. + std::mem::forget(orig); + + ret + } +} + +impl<'a, T: ?Sized> Deref for GcCellRef<'a, T> { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + self.value + } +} + +impl<'a, T: ?Sized> Drop for GcCellRef<'a, T> { + fn drop(&mut self) { + debug_assert!(self.flags.get().borrowed() == BorrowState::Reading); + self.flags.set(self.flags.get().sub_reading()); + } +} + +impl<'a, T: ?Sized + Debug> Debug for GcCellRef<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Debug::fmt(&**self, f) + } +} + +impl<'a, T: ?Sized + Display> Display for GcCellRef<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&**self, f) + } +} + +/// A wrapper type for a mutably borrowed value from a `GcCell`. +pub struct GcCellRefMut<'a, T: Trace + ?Sized + 'static, U: ?Sized = T> { + pub(crate) gc_cell: &'a GcCell, + pub(crate) value: &'a mut U, +} + +impl<'a, T: Trace + ?Sized, U: ?Sized> GcCellRefMut<'a, T, U> { + /// Makes a new `GcCellRefMut` for a component of the borrowed data, e.g., an enum + /// variant. + /// + /// The `GcCellRefMut` is already mutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `GcCellRefMut::map(...)`. A method would interfere with methods of the same + /// name on the contents of a `GcCell` used through `Deref`. + #[inline] + pub fn map(orig: Self, f: F) -> GcCellRefMut<'a, T, V> + where + V: ?Sized, + F: FnOnce(&mut U) -> &mut V, + { + // SAFETY: This is safe as `GcCellRefMut` is already borrowed, so the value is rooted. + let value = unsafe { &mut *(orig.value as *mut U) }; + + let ret = GcCellRefMut { + gc_cell: orig.gc_cell, + value: f(value), + }; + + // We have to tell the compiler not to call the destructor of GcCellRefMut, + // because it will update the borrow flags. + std::mem::forget(orig); + + ret + } +} + +impl<'a, T: Trace + ?Sized, U: ?Sized> Deref for GcCellRefMut<'a, T, U> { + type Target = U; + + #[inline] + fn deref(&self) -> &U { + self.value + } +} + +impl<'a, T: Trace + ?Sized, U: ?Sized> DerefMut for GcCellRefMut<'a, T, U> { + #[inline] + fn deref_mut(&mut self) -> &mut U { + self.value + } +} + +impl<'a, T: Trace + ?Sized, U: ?Sized> Drop for GcCellRefMut<'a, T, U> { + #[inline] + fn drop(&mut self) { + debug_assert!(self.gc_cell.flags.get().borrowed() == BorrowState::Writing); + // Restore the rooted state of the GcCell's contents to the state of the GcCell. + // During the lifetime of the GcCellRefMut, the GcCell's contents are rooted. + if !self.gc_cell.flags.get().rooted() { + // SAFETY: If `GcCell` is no longer rooted, then unroot it. This should be safe + // as the internal `GcBox` should be guaranteed to have at least 1 root. + unsafe { + (*self.gc_cell.cell.get()).unroot(); + } + } + self.gc_cell + .flags + .set(self.gc_cell.flags.get().set_unused()); + } +} + +impl<'a, T: Trace + ?Sized, U: Debug + ?Sized> Debug for GcCellRefMut<'a, T, U> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Debug::fmt(&**self, f) + } +} + +impl<'a, T: Trace + ?Sized, U: Display + ?Sized> Display for GcCellRefMut<'a, T, U> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&**self, f) + } +} + +// SAFETY: GcCell tracks it's `BorrowState` is `Writing` +unsafe impl Send for GcCell {} + +impl Clone for GcCell { + #[inline] + fn clone(&self) -> Self { + Self::new(self.borrow().clone()) + } +} + +impl Default for GcCell { + #[inline] + fn default() -> Self { + Self::new(Default::default()) + } +} + +#[allow(clippy::inline_always)] +impl PartialEq for GcCell { + #[inline(always)] + fn eq(&self, other: &Self) -> bool { + *self.borrow() == *other.borrow() + } +} + +impl Eq for GcCell {} + +#[allow(clippy::inline_always)] +impl PartialOrd for GcCell { + #[inline(always)] + fn partial_cmp(&self, other: &Self) -> Option { + (*self.borrow()).partial_cmp(&*other.borrow()) + } + + #[inline(always)] + fn lt(&self, other: &Self) -> bool { + *self.borrow() < *other.borrow() + } + + #[inline(always)] + fn le(&self, other: &Self) -> bool { + *self.borrow() <= *other.borrow() + } + + #[inline(always)] + fn gt(&self, other: &Self) -> bool { + *self.borrow() > *other.borrow() + } + + #[inline(always)] + fn ge(&self, other: &Self) -> bool { + *self.borrow() >= *other.borrow() + } +} + +impl Ord for GcCell { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + (*self.borrow()).cmp(&*other.borrow()) + } +} + +impl Debug for GcCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.flags.get().borrowed() { + BorrowState::Unused | BorrowState::Reading => f + .debug_struct("GcCell") + .field("flags", &self.flags.get()) + .field("value", &self.borrow()) + .finish(), + BorrowState::Writing => f + .debug_struct("GcCell") + .field("flags", &self.flags.get()) + .field("value", &"") + .finish(), + } + } +} diff --git a/boa_gc/src/internals/ephemeron_box.rs b/boa_gc/src/internals/ephemeron_box.rs new file mode 100644 index 00000000000..420b7fb36c6 --- /dev/null +++ b/boa_gc/src/internals/ephemeron_box.rs @@ -0,0 +1,146 @@ +use crate::trace::Trace; +use crate::{finalizer_safe, GcBox}; +use crate::{Finalize, Gc}; +use std::cell::Cell; +use std::ptr::NonNull; + +/// The inner allocation of an [`Ephemeron`][crate::Ephemeron] pointer. +pub(crate) struct EphemeronBox { + key: Cell>>>, + value: V, +} + +impl EphemeronBox { + pub(crate) fn new(key: &Gc, value: V) -> Self { + Self { + key: Cell::new(Some(key.inner_ptr())), + value, + } + } +} + +impl EphemeronBox { + /// Checks if the key pointer is marked by Trace + #[inline] + pub(crate) fn is_marked(&self) -> bool { + if let Some(key) = self.inner_key() { + key.is_marked() + } else { + false + } + } + + /// Returns some pointer to the `key`'s `GcBox` or None + /// # Panics + /// This method will panic if called while the garbage collector is dropping. + #[inline] + pub(crate) fn inner_key_ptr(&self) -> Option<*mut GcBox> { + assert!(finalizer_safe()); + self.key.get().map(NonNull::as_ptr) + } + + /// Returns some reference to `key`'s `GcBox` or None + #[inline] + pub(crate) fn inner_key(&self) -> Option<&GcBox> { + // SAFETY: This is safe as `EphemeronBox::inner_key_ptr()` will + // fetch either a live `GcBox` or None. The value of `key` is set + // to None in the case where `EphemeronBox` and `key`'s `GcBox` + // entered into `Collector::sweep()` as unmarked. + unsafe { + if let Some(inner_key) = self.inner_key_ptr() { + Some(&*inner_key) + } else { + None + } + } + } + + /// Returns a reference to the value of `key`'s `GcBox` + #[inline] + pub(crate) fn key(&self) -> Option<&K> { + if let Some(key_box) = self.inner_key() { + Some(key_box.value()) + } else { + None + } + } + + /// Returns a reference to `value` + #[inline] + pub(crate) fn value(&self) -> &V { + &self.value + } + + /// Calls [`Trace::weak_trace()`][crate::Trace] on key + #[inline] + fn weak_trace_key(&self) { + if let Some(key) = self.inner_key() { + key.weak_trace_inner(); + } + } + + /// Calls [`Trace::weak_trace()`][crate::Trace] on value + #[inline] + fn weak_trace_value(&self) { + // SAFETY: Value is a sized element that must implement trace. The + // operation is safe as EphemeronBox owns value and `Trace::weak_trace` + // must be implemented on it + unsafe { + self.value().weak_trace(); + } + } +} + +// `EphemeronBox`'s Finalize is special in that if it is determined to be unreachable +// and therefore so has the `GcBox` that `key`stores the pointer to, then we set `key` +// to None to guarantee that we do not access freed memory. +impl Finalize for EphemeronBox { + #[inline] + fn finalize(&self) { + self.key.set(None); + } +} + +// SAFETY: EphemeronBox implements primarly two methods of trace `Trace::is_marked_ephemeron` +// to determine whether the key field is stored and `Trace::weak_trace` which continues the `Trace::weak_trace()` +// into `key` and `value`. +unsafe impl Trace for EphemeronBox { + #[inline] + unsafe fn trace(&self) { + /* An ephemeron is never traced with Phase One Trace */ + } + + /// Checks if the `key`'s `GcBox` has been marked by `Trace::trace()` or `Trace::weak_trace`. + #[inline] + fn is_marked_ephemeron(&self) -> bool { + self.is_marked() + } + + /// Checks if this `EphemeronBox` has already been determined reachable. If so, continue to trace + /// value in `key` and `value`. + #[inline] + unsafe fn weak_trace(&self) { + if self.is_marked() { + self.weak_trace_key(); + self.weak_trace_value(); + } + } + + // EphemeronBox does not implement root. + #[inline] + unsafe fn root(&self) {} + + // EphemeronBox does not implement unroot + #[inline] + unsafe fn unroot(&self) {} + + // An `EphemeronBox`'s key is set to None once it has been finalized. + // + // NOTE: while it is possible for the `key`'s pointer value to be + // resurrected, we should still consider the finalize the ephemeron + // box and set the `key` to None. + #[inline] + fn run_finalizer(&self) { + Finalize::finalize(self); + } +} diff --git a/boa_gc/src/internals/gc_box.rs b/boa_gc/src/internals/gc_box.rs new file mode 100644 index 00000000000..eaca4b48f79 --- /dev/null +++ b/boa_gc/src/internals/gc_box.rs @@ -0,0 +1,195 @@ +use crate::Trace; +use std::cell::Cell; +use std::fmt; +use std::ptr::{self, NonNull}; + +// Age and Weak Flags +const MARK_MASK: usize = 1 << (usize::BITS - 2); +const WEAK_MASK: usize = 1 << (usize::BITS - 1); +const ROOTS_MASK: usize = !(MARK_MASK | WEAK_MASK); +const ROOTS_MAX: usize = ROOTS_MASK; + +/// The `GcBoxheader` contains the `GcBox`'s current state for the `Collector`'s +/// Mark/Sweep as well as a pointer to the next node in the heap. +/// +/// These flags include: +/// - Root Count +/// - Mark Flag Bit +/// - Weak Flag Bit +/// +/// The next node is set by the `Allocator` during initialization and by the +/// `Collector` during the sweep phase. +pub(crate) struct GcBoxHeader { + roots: Cell, + pub(crate) next: Cell>>>, +} + +impl GcBoxHeader { + /// Creates a new `GcBoxHeader` with a root of 1 and next set to None. + #[inline] + pub(crate) fn new() -> Self { + Self { + roots: Cell::new(1), + next: Cell::new(None), + } + } + + /// Creates a new `GcBoxHeader` with the Weak bit at 1 and roots of 1. + #[inline] + pub(crate) fn new_weak() -> Self { + // Set weak_flag + Self { + roots: Cell::new(WEAK_MASK | 1), + next: Cell::new(None), + } + } + + /// Returns the `GcBoxHeader`'s current root count + #[inline] + pub(crate) fn roots(&self) -> usize { + self.roots.get() & ROOTS_MASK + } + + /// Increments `GcBoxHeader`'s root count. + #[inline] + pub(crate) fn inc_roots(&self) { + let roots = self.roots.get(); + + if (roots & ROOTS_MASK) < ROOTS_MAX { + self.roots.set(roots + 1); + } else { + // TODO: implement a better way to handle root overload. + panic!("roots counter overflow"); + } + } + + /// Decreases `GcBoxHeader`'s current root count. + #[inline] + pub(crate) fn dec_roots(&self) { + // Underflow check as a stop gap for current issue when dropping. + if self.roots.get() > 0 { + self.roots.set(self.roots.get() - 1); + } + } + + /// Returns a bool for whether `GcBoxHeader`'s mark bit is 1. + #[inline] + pub(crate) fn is_marked(&self) -> bool { + self.roots.get() & MARK_MASK != 0 + } + + /// Sets `GcBoxHeader`'s mark bit to 1. + #[inline] + pub(crate) fn mark(&self) { + self.roots.set(self.roots.get() | MARK_MASK); + } + + /// Sets `GcBoxHeader`'s mark bit to 0. + #[inline] + pub(crate) fn unmark(&self) { + self.roots.set(self.roots.get() & !MARK_MASK); + } + + /// Returns a bool for whether the `GcBoxHeader`'s weak bit is 1. + #[inline] + pub(crate) fn is_ephemeron(&self) -> bool { + self.roots.get() & WEAK_MASK != 0 + } +} + +impl fmt::Debug for GcBoxHeader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("GcBoxHeader") + .field("Roots", &self.roots()) + .field("Weak", &self.is_ephemeron()) + .field("Marked", &self.is_marked()) + .finish() + } +} + +/// A garbage collected allocation. +#[derive(Debug)] +pub(crate) struct GcBox { + pub(crate) header: GcBoxHeader, + pub(crate) value: T, +} + +impl GcBox { + /// Returns a new `GcBox` with a rooted `GcBoxHeader`. + pub(crate) fn new(value: T) -> Self { + Self { + header: GcBoxHeader::new(), + value, + } + } + + /// Returns a new `GcBox` with a rooted and weak `GcBoxHeader`. + pub(crate) fn new_weak(value: T) -> Self { + Self { + header: GcBoxHeader::new_weak(), + value, + } + } +} + +impl GcBox { + /// Returns `true` if the two references refer to the same `GcBox`. + pub(crate) fn ptr_eq(this: &Self, other: &Self) -> bool { + // Use .header to ignore fat pointer vtables, to work around + // https://github.com/rust-lang/rust/issues/46139 + ptr::eq(&this.header, &other.header) + } + + /// Marks this `GcBox` and marks through its data. + #[inline] + pub(crate) unsafe fn trace_inner(&self) { + if !self.header.is_marked() && !self.header.is_ephemeron() { + self.header.mark(); + // SAFETY: if `GcBox::trace_inner()` has been called, then, + // this box must have been deemed as reachable via tracing + // from a root, which by extension means that value has not + // been dropped either. + unsafe { + self.value.trace(); + } + } + } + + /// Trace inner data + #[inline] + pub(crate) fn weak_trace_inner(&self) { + // SAFETY: if a `GcBox` has `weak_trace_inner` called, then the inner. + // value must have been deemed as reachable. + unsafe { + self.value.weak_trace(); + } + } + + /// Increases the root count on this `GcBox`. + /// + /// Roots prevent the `GcBox` from being destroyed by the garbage collector. + #[inline] + pub(crate) fn root_inner(&self) { + self.header.inc_roots(); + } + + /// Decreases the root count on this `GcBox`. + /// + /// Roots prevent the `GcBox` from being destroyed by the garbage collector. + #[inline] + pub(crate) fn unroot_inner(&self) { + self.header.dec_roots(); + } + + /// Returns a reference to the `GcBox`'s value. + #[inline] + pub(crate) fn value(&self) -> &T { + &self.value + } + + /// Returns a bool for whether the header is marked. + #[inline] + pub(crate) fn is_marked(&self) -> bool { + self.header.is_marked() + } +} diff --git a/boa_gc/src/internals/mod.rs b/boa_gc/src/internals/mod.rs new file mode 100644 index 00000000000..005c000ade5 --- /dev/null +++ b/boa_gc/src/internals/mod.rs @@ -0,0 +1,5 @@ +mod ephemeron_box; +pub(crate) use ephemeron_box::EphemeronBox; + +mod gc_box; +pub(crate) use gc_box::GcBox; diff --git a/boa_gc/src/lib.rs b/boa_gc/src/lib.rs index ac24531793c..4b26855ea85 100644 --- a/boa_gc/src/lib.rs +++ b/boa_gc/src/lib.rs @@ -1,6 +1,383 @@ //! Garbage collector for the Boa JavaScript engine. -pub use gc::{ - custom_trace, finalizer_safe, force_collect, unsafe_empty_trace, Finalize, Gc, GcCell as Cell, - GcCellRef as Ref, GcCellRefMut as RefMut, Trace, -}; +#![warn( + clippy::perf, + clippy::single_match_else, + clippy::dbg_macro, + clippy::doc_markdown, + clippy::wildcard_imports, + clippy::struct_excessive_bools, + clippy::doc_markdown, + clippy::semicolon_if_nothing_returned, + clippy::pedantic +)] +#![deny( + clippy::all, + clippy::cast_lossless, + clippy::redundant_closure_for_method_calls, + clippy::use_self, + clippy::unnested_or_patterns, + clippy::trivially_copy_pass_by_ref, + clippy::needless_pass_by_value, + clippy::match_wildcard_for_single_variants, + clippy::map_unwrap_or, + clippy::undocumented_unsafe_blocks, + clippy::missing_safety_doc, + unsafe_op_in_unsafe_fn, + unused_qualifications, + unused_import_braces, + unused_lifetimes, + unreachable_pub, + trivial_numeric_casts, + rustdoc::broken_intra_doc_links, + missing_debug_implementations, + missing_copy_implementations, + deprecated_in_future, + meta_variable_misuse, + non_ascii_idents, + rust_2018_compatibility, + rust_2018_idioms, + future_incompatible, + nonstandard_style, + missing_docs +)] +#![allow(clippy::let_unit_value, clippy::module_name_repetitions)] + +extern crate self as boa_gc; + +use boa_profiler::Profiler; +use std::cell::{Cell, RefCell}; +use std::mem; +use std::ptr::NonNull; + +mod trace; + +pub(crate) mod internals; + +mod cell; +mod pointers; + +pub use crate::trace::{Finalize, Trace}; +pub use boa_macros::{Finalize, Trace}; +pub use cell::{GcCell, GcCellRef, GcCellRefMut}; +pub use pointers::{Ephemeron, Gc, WeakGc}; + +use internals::GcBox; + +type GcPointer = NonNull>; + +thread_local!(static EPHEMERON_QUEUE: Cell>> = Cell::new(None)); +thread_local!(static GC_DROPPING: Cell = Cell::new(false)); +thread_local!(static BOA_GC: RefCell = RefCell::new( BoaGc { + config: GcConfig::default(), + runtime: GcRuntimeData::default(), + adult_start: Cell::new(None), +})); + +#[derive(Debug, Clone, Copy)] +struct GcConfig { + threshold: usize, + used_space_percentage: usize, +} + +// Setting the defaults to an arbitrary value currently. +// +// TODO: Add a configure later +impl Default for GcConfig { + fn default() -> Self { + Self { + threshold: 1024, + used_space_percentage: 80, + } + } +} + +#[derive(Default, Debug, Clone, Copy)] +struct GcRuntimeData { + collections: usize, + bytes_allocated: usize, +} + +#[derive(Debug)] +struct BoaGc { + config: GcConfig, + runtime: GcRuntimeData, + adult_start: Cell>, +} + +impl Drop for BoaGc { + fn drop(&mut self) { + Collector::dump(self); + } +} + +// Whether or not the thread is currently in the sweep phase of garbage collection. +// During this phase, attempts to dereference a `Gc` pointer will trigger a panic. +/// `DropGuard` flags whether the Collector is currently running `Collector::sweep()` or `Collector::dump()` +/// +/// While the `DropGuard` is active, all `GcBox`s must not be dereferenced or accessed as it could cause Undefined Behavior +#[derive(Debug, Clone)] +struct DropGuard; + +impl DropGuard { + fn new() -> Self { + GC_DROPPING.with(|dropping| dropping.set(true)); + Self + } +} + +impl Drop for DropGuard { + fn drop(&mut self) { + GC_DROPPING.with(|dropping| dropping.set(false)); + } +} + +/// Returns `true` if it is safe for a type to run [`Finalize::finalize`]. +#[must_use] +#[inline] +pub fn finalizer_safe() -> bool { + GC_DROPPING.with(|dropping| !dropping.get()) +} + +/// The Allocator handles allocation of garbage collected values. +/// +/// The allocator can trigger a garbage collection. +#[derive(Debug, Clone, Copy)] +struct Allocator; + +impl Allocator { + /// Allocate a new garbage collected value to the Garbage Collector's heap. + fn allocate(value: GcBox) -> NonNull> { + let _timer = Profiler::global().start_event("New Pointer", "BoaAlloc"); + let element_size = mem::size_of_val::>(&value); + BOA_GC.with(|st| { + let mut gc = st.borrow_mut(); + + Self::manage_state(&mut gc); + value.header.next.set(gc.adult_start.take()); + // Safety: Value Cannot be a null as it must be a GcBox + let ptr = unsafe { NonNull::new_unchecked(Box::into_raw(Box::from(value))) }; + + gc.adult_start.set(Some(ptr)); + gc.runtime.bytes_allocated += element_size; + + ptr + }) + } + + fn manage_state(gc: &mut BoaGc) { + if gc.runtime.bytes_allocated > gc.config.threshold { + Collector::run_full_collection(gc); + + if gc.runtime.bytes_allocated + > gc.config.threshold / 100 * gc.config.used_space_percentage + { + gc.config.threshold = + gc.runtime.bytes_allocated / gc.config.used_space_percentage * 100; + } + } + } +} + +// This collector currently functions in four main phases +// +// Mark -> Finalize -> Mark -> Sweep +// +// Mark nodes as reachable then finalize the unreachable nodes. A remark phase +// then needs to be retriggered as finalization can potentially resurrect dead +// nodes. +// +// A better approach in a more concurrent structure may be to reorder. +// +// Mark -> Sweep -> Finalize +struct Collector; + +impl Collector { + /// Run a collection on the full heap. + fn run_full_collection(gc: &mut BoaGc) { + let _timer = Profiler::global().start_event("Gc Full Collection", "gc"); + gc.runtime.collections += 1; + let unreachable_adults = Self::mark_heap(&gc.adult_start); + + // Check if any unreachable nodes were found and finalize + if !unreachable_adults.is_empty() { + // SAFETY: Please see `Collector::finalize()` + unsafe { Self::finalize(unreachable_adults) }; + } + + let _final_unreachable_adults = Self::mark_heap(&gc.adult_start); + + // SAFETY: Please see `Collector::sweep()` + unsafe { + Self::sweep(&gc.adult_start, &mut gc.runtime.bytes_allocated); + } + } + + /// Walk the heap and mark any nodes deemed reachable + fn mark_heap(head: &Cell>>>) -> Vec>> { + let _timer = Profiler::global().start_event("Gc Marking", "gc"); + // Walk the list, tracing and marking the nodes + let mut finalize = Vec::new(); + let mut ephemeron_queue = Vec::new(); + let mut mark_head = head; + while let Some(node) = mark_head.get() { + // SAFETY: node must be valid as it is coming directly from the heap. + let node_ref = unsafe { node.as_ref() }; + if node_ref.header.is_ephemeron() { + ephemeron_queue.push(node); + } else if node_ref.header.roots() > 0 { + // SAFETY: the reference to node must be valid as it is rooted. Passing + // invalid references can result in Undefined Behavior + unsafe { + node_ref.trace_inner(); + } + } else { + finalize.push(node); + } + mark_head = &node_ref.header.next; + } + + // Ephemeron Evaluation + if !ephemeron_queue.is_empty() { + ephemeron_queue = Self::mark_ephemerons(ephemeron_queue); + } + + // Any left over nodes in the ephemeron queue at this point are + // unreachable and need to be notified/finalized. + finalize.extend(ephemeron_queue); + + finalize + } + + // Tracing Ephemerons/Weak is always requires tracing the inner nodes in case it ends up marking unmarked node + // + // Time complexity should be something like O(nd) where d is the longest chain of epehemerons + /// Mark any ephemerons that are deemed live and trace their fields. + fn mark_ephemerons( + initial_queue: Vec>>, + ) -> Vec>> { + let mut ephemeron_queue = initial_queue; + loop { + // iterate through ephemeron queue, sorting nodes by whether they + // are reachable or unreachable + let (reachable, other): (Vec<_>, Vec<_>) = + ephemeron_queue.into_iter().partition(|node| { + // SAFETY: Any node on the eph_queue or the heap must be non null + let node = unsafe { node.as_ref() }; + if node.value.is_marked_ephemeron() { + node.header.mark(); + true + } else { + node.header.roots() > 0 + } + }); + // Replace the old queue with the unreachable + ephemeron_queue = other; + + // If reachable nodes is not empty, trace values. If it is empty, + // break from the loop + if reachable.is_empty() { + break; + } + EPHEMERON_QUEUE.with(|state| state.set(Some(Vec::new()))); + // iterate through reachable nodes and trace their values, + // enqueuing any ephemeron that is found during the trace + for node in reachable { + // TODO: deal with fetch ephemeron_queue + // SAFETY: Node must be a valid pointer or else it would not be deemed reachable. + unsafe { + node.as_ref().weak_trace_inner(); + } + } + + EPHEMERON_QUEUE.with(|st| { + if let Some(found_nodes) = st.take() { + ephemeron_queue.extend(found_nodes); + } + }); + } + ephemeron_queue + } + + /// # Safety + /// + /// Passing a vec with invalid pointers will result in Undefined Behaviour. + unsafe fn finalize(finalize_vec: Vec>>) { + let _timer = Profiler::global().start_event("Gc Finalization", "gc"); + for node in finalize_vec { + // We double check that the unreachable nodes are actually unreachable + // prior to finalization as they could have been marked by a different + // trace after initially being added to the queue + // + // SAFETY: The caller must ensure all pointers inside `finalize_vec` are valid. + let node = unsafe { node.as_ref() }; + if !node.header.is_marked() { + Trace::run_finalizer(&node.value); + } + } + } + + /// # Safety + /// + /// - Providing an invalid pointer in the `heap_start` or in any of the headers of each + /// node will result in Undefined Behaviour. + /// - Providing a list of pointers that weren't allocated by `Box::into_raw(Box::new(..))` + /// will result in Undefined Behaviour. + unsafe fn sweep( + heap_start: &Cell>>>, + total_allocated: &mut usize, + ) { + let _timer = Profiler::global().start_event("Gc Sweeping", "gc"); + let _guard = DropGuard::new(); + + let mut sweep_head = heap_start; + while let Some(node) = sweep_head.get() { + // SAFETY: The caller must ensure the validity of every node of `heap_start`. + let node_ref = unsafe { node.as_ref() }; + if node_ref.is_marked() { + node_ref.header.unmark(); + sweep_head = &node_ref.header.next; + } else if node_ref.header.is_ephemeron() && node_ref.header.roots() > 0 { + // Keep the ephemeron box's alive if rooted, but note that it's pointer is no longer safe + Trace::run_finalizer(&node_ref.value); + sweep_head = &node_ref.header.next; + } else { + // SAFETY: The algorithm ensures only unmarked/unreachable pointers are dropped. + // The caller must ensure all pointers were allocated by `Box::into_raw(Box::new(..))`. + let unmarked_node = unsafe { Box::from_raw(node.as_ptr()) }; + let unallocated_bytes = mem::size_of_val::>(&*unmarked_node); + *total_allocated -= unallocated_bytes; + sweep_head.set(unmarked_node.header.next.take()); + } + } + } + + // Clean up the heap when BoaGc is dropped + fn dump(gc: &mut BoaGc) { + // Not initializing a dropguard since this should only be invoked when BOA_GC is being dropped. + let _guard = DropGuard::new(); + + let sweep_head = &gc.adult_start; + while let Some(node) = sweep_head.get() { + // SAFETY: + // The `Allocator` must always ensure its start node is a valid, non-null pointer that + // was allocated by `Box::from_raw(Box::new(..))`. + let unmarked_node = unsafe { Box::from_raw(node.as_ptr()) }; + sweep_head.set(unmarked_node.header.next.take()); + } + } +} + +/// Forcefully runs a garbage collection of all unaccessible nodes. +pub fn force_collect() { + BOA_GC.with(|current| { + let mut gc = current.borrow_mut(); + + if gc.runtime.bytes_allocated > 0 { + Collector::run_full_collection(&mut gc); + } + }); +} + +#[cfg(test)] +mod test; diff --git a/boa_gc/src/pointers/ephemeron.rs b/boa_gc/src/pointers/ephemeron.rs new file mode 100644 index 00000000000..35702a7b119 --- /dev/null +++ b/boa_gc/src/pointers/ephemeron.rs @@ -0,0 +1,125 @@ +use crate::{ + finalizer_safe, + internals::EphemeronBox, + trace::{Finalize, Trace}, + Allocator, Gc, GcBox, EPHEMERON_QUEUE, +}; +use std::cell::Cell; +use std::ptr::NonNull; + +#[derive(Debug)] +/// A key-value pair where the value becomes unaccesible when the key is garbage collected. +/// +/// See Racket's explanation on [**ephemerons**][eph] for a brief overview or read Barry Hayes' +/// [_Ephemerons_: a new finalization mechanism][acm]. +/// +/// +/// [eph]: https://docs.racket-lang.org/reference/ephemerons.html +/// [acm]: https://dl.acm.org/doi/10.1145/263700.263733 +pub struct Ephemeron { + inner_ptr: Cell>>>, +} + +impl Ephemeron { + /// Creates a new `Ephemeron`. + pub fn new(key: &Gc, value: V) -> Self { + Self { + inner_ptr: Cell::new(Allocator::allocate(GcBox::new_weak(EphemeronBox::new( + key, value, + )))), + } + } +} + +impl Ephemeron { + #[inline] + fn inner_ptr(&self) -> NonNull>> { + self.inner_ptr.get() + } + + #[inline] + fn inner(&self) -> &GcBox> { + // SAFETY: GcBox> must live until it is unrooted by Drop + unsafe { &*self.inner_ptr().as_ptr() } + } + + #[inline] + /// Gets the weak key of this `Ephemeron`, or `None` if the key was already garbage + /// collected. + pub fn key(&self) -> Option<&K> { + self.inner().value().key() + } + + #[inline] + /// Gets the stored value of this `Ephemeron`. + pub fn value(&self) -> &V { + self.inner().value().value() + } + + #[inline] + /// Gets a `Gc` for the stored key of this `Ephemeron`. + pub fn upgrade_key(&self) -> Option> { + // SAFETY: ptr must be a valid pointer or None would have been returned. + self.inner().value().inner_key_ptr().map(|ptr| unsafe { + let inner_ptr = NonNull::new_unchecked(ptr); + Gc::from_ptr(inner_ptr) + }) + } +} + +impl Finalize for Ephemeron {} + +// SAFETY: Ephemerons trace implementation is standard for everything except `Trace::weak_trace()`, +// which pushes the GcBox> onto the EphemeronQueue +unsafe impl Trace for Ephemeron { + #[inline] + unsafe fn trace(&self) {} + + // Push this Ephemeron's pointer onto the EphemeronQueue + #[inline] + unsafe fn weak_trace(&self) { + EPHEMERON_QUEUE.with(|q| { + let mut queue = q.take().expect("queue is initialized by weak_trace"); + queue.push(self.inner_ptr()); + }); + } + + #[inline] + unsafe fn root(&self) {} + + #[inline] + unsafe fn unroot(&self) {} + + #[inline] + fn run_finalizer(&self) { + Finalize::finalize(self); + } +} + +impl Clone for Ephemeron { + #[inline] + fn clone(&self) -> Self { + // SAFETY: This is safe because the inner_ptr must live as long as it's roots. + // Mismanagement of roots can cause inner_ptr to use after free or Undefined + // Behavior. + unsafe { + let eph = Self { + inner_ptr: Cell::new(NonNull::new_unchecked(self.inner_ptr().as_ptr())), + }; + // Increment the Ephemeron's GcBox roots by 1 + self.inner().root_inner(); + eph + } + } +} + +impl Drop for Ephemeron { + #[inline] + fn drop(&mut self) { + // NOTE: We assert that this drop call is not a + // drop from `Collector::dump` or `Collector::sweep` + if finalizer_safe() { + self.inner().unroot_inner(); + } + } +} diff --git a/boa_gc/src/pointers/gc.rs b/boa_gc/src/pointers/gc.rs new file mode 100644 index 00000000000..df44a8b327e --- /dev/null +++ b/boa_gc/src/pointers/gc.rs @@ -0,0 +1,275 @@ +use std::cell::Cell; +use std::cmp::Ordering; +use std::fmt::{self, Debug, Display}; +use std::hash::{Hash, Hasher}; +use std::marker::PhantomData; +use std::ops::Deref; +use std::ptr::{self, addr_of_mut, NonNull}; +use std::rc::Rc; + +use crate::internals::GcBox; +use crate::trace::{Finalize, Trace}; +use crate::{finalizer_safe, Allocator}; + +// Technically, this function is safe, since we're just modifying the address of a pointer without +// dereferencing it. +pub(crate) fn set_data_ptr(mut ptr: *mut T, data: *mut U) -> *mut T { + // SAFETY: this should be safe as ptr must be a valid nonnull + unsafe { + ptr::write(addr_of_mut!(ptr).cast::<*mut u8>(), data.cast::()); + } + ptr +} + +/// A garbage-collected pointer type over an immutable value. +pub struct Gc { + pub(crate) inner_ptr: Cell>>, + pub(crate) marker: PhantomData>, +} + +impl Gc { + /// Constructs a new `Gc` with the given value. + pub fn new(value: T) -> Self { + // Create GcBox and allocate it to heap. + // + // Note: Allocator can cause Collector to run + let inner_ptr = Allocator::allocate(GcBox::new(value)); + // SAFETY: inner_ptr was just allocated, so it must be a valid value that implements [`Trace`] + unsafe { (*inner_ptr.as_ptr()).value().unroot() } + let gc = Self { + inner_ptr: Cell::new(inner_ptr), + marker: PhantomData, + }; + gc.set_root(); + gc + } +} + +impl Gc { + /// Returns `true` if the two `Gc`s point to the same allocation. + pub fn ptr_eq(this: &Self, other: &Self) -> bool { + GcBox::ptr_eq(this.inner(), other.inner()) + } + + /// Will return a new rooted `Gc` from a `GcBox` pointer + pub(crate) fn from_ptr(ptr: NonNull>) -> Self { + // SAFETY: the value provided as a pointer MUST be a valid GcBox. + unsafe { + ptr.as_ref().root_inner(); + let gc = Self { + inner_ptr: Cell::new(ptr), + marker: PhantomData, + }; + gc.set_root(); + gc + } + } +} + +/// Returns the given pointer with its root bit cleared. +pub(crate) unsafe fn clear_root_bit( + ptr: NonNull>, +) -> NonNull> { + let ptr = ptr.as_ptr(); + let data = ptr.cast::(); + let addr = data as isize; + let ptr = set_data_ptr(ptr, data.wrapping_offset((addr & !1) - addr)); + // SAFETY: ptr must be a non null value + unsafe { NonNull::new_unchecked(ptr) } +} + +impl Gc { + fn rooted(&self) -> bool { + self.inner_ptr.get().as_ptr().cast::() as usize & 1 != 0 + } + + pub(crate) fn set_root(&self) { + let ptr = self.inner_ptr.get().as_ptr(); + let data = ptr.cast::(); + let addr = data as isize; + let ptr = set_data_ptr(ptr, data.wrapping_offset((addr | 1) - addr)); + // SAFETY: ptr must be a non null value. + unsafe { + self.inner_ptr.set(NonNull::new_unchecked(ptr)); + } + } + + fn clear_root(&self) { + // SAFETY: inner_ptr must be a valid non-null pointer to a live GcBox. + unsafe { + self.inner_ptr.set(clear_root_bit(self.inner_ptr.get())); + } + } + + #[inline] + pub(crate) fn inner_ptr(&self) -> NonNull> { + assert!(finalizer_safe()); + // SAFETY: inner_ptr must be a live GcBox. Calling this on a dropped GcBox + // can result in Undefined Behavior. + unsafe { clear_root_bit(self.inner_ptr.get()) } + } + + #[inline] + fn inner(&self) -> &GcBox { + // SAFETY: Please see Gc::inner_ptr() + unsafe { self.inner_ptr().as_ref() } + } +} + +impl Finalize for Gc {} + +// SAFETY: `Gc` maintains it's own rootedness and implements all methods of +// Trace. It is not possible to root an already rooted `Gc` and vice versa. +unsafe impl Trace for Gc { + #[inline] + unsafe fn trace(&self) { + // SAFETY: Inner must be live and allocated GcBox. + unsafe { + self.inner().trace_inner(); + } + } + + #[inline] + unsafe fn weak_trace(&self) { + self.inner().weak_trace_inner(); + } + + #[inline] + unsafe fn root(&self) { + assert!(!self.rooted(), "Can't double-root a Gc"); + // Try to get inner before modifying our state. Inner may be + // inaccessible due to this method being invoked during the sweeping + // phase, and we don't want to modify our state before panicking. + self.inner().root_inner(); + self.set_root(); + } + + #[inline] + unsafe fn unroot(&self) { + assert!(self.rooted(), "Can't double-unroot a Gc"); + // Try to get inner before modifying our state. Inner may be + // inaccessible due to this method being invoked during the sweeping + // phase, and we don't want to modify our state before panicking. + self.inner().unroot_inner(); + self.clear_root(); + } + + #[inline] + fn run_finalizer(&self) { + Finalize::finalize(self); + } +} + +impl Clone for Gc { + #[inline] + fn clone(&self) -> Self { + Self::from_ptr(self.inner_ptr()) + } +} + +impl Deref for Gc { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + self.inner().value() + } +} + +impl Drop for Gc { + #[inline] + fn drop(&mut self) { + // If this pointer was a root, we should unroot it. + if self.rooted() { + self.inner().unroot_inner(); + } + } +} + +impl Default for Gc { + #[inline] + fn default() -> Self { + Self::new(Default::default()) + } +} + +#[allow(clippy::inline_always)] +impl PartialEq for Gc { + #[inline(always)] + fn eq(&self, other: &Self) -> bool { + **self == **other + } +} + +impl Eq for Gc {} + +#[allow(clippy::inline_always)] +impl PartialOrd for Gc { + #[inline(always)] + fn partial_cmp(&self, other: &Self) -> Option { + (**self).partial_cmp(&**other) + } + + #[inline(always)] + fn lt(&self, other: &Self) -> bool { + **self < **other + } + + #[inline(always)] + fn le(&self, other: &Self) -> bool { + **self <= **other + } + + #[inline(always)] + fn gt(&self, other: &Self) -> bool { + **self > **other + } + + #[inline(always)] + fn ge(&self, other: &Self) -> bool { + **self >= **other + } +} + +impl Ord for Gc { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + (**self).cmp(&**other) + } +} + +impl Hash for Gc { + fn hash(&self, state: &mut H) { + (**self).hash(state); + } +} + +impl Display for Gc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&**self, f) + } +} + +impl Debug for Gc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Debug::fmt(&**self, f) + } +} + +impl fmt::Pointer for Gc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&self.inner(), f) + } +} + +impl std::borrow::Borrow for Gc { + fn borrow(&self) -> &T { + self + } +} + +impl AsRef for Gc { + fn as_ref(&self) -> &T { + self + } +} diff --git a/boa_gc/src/pointers/mod.rs b/boa_gc/src/pointers/mod.rs new file mode 100644 index 00000000000..5c14182762d --- /dev/null +++ b/boa_gc/src/pointers/mod.rs @@ -0,0 +1,9 @@ +//! Pointers represents the External types returned by the Boa Garbage Collector + +mod ephemeron; +mod gc; +mod weak; + +pub use ephemeron::Ephemeron; +pub use gc::Gc; +pub use weak::WeakGc; diff --git a/boa_gc/src/pointers/weak.rs b/boa_gc/src/pointers/weak.rs new file mode 100644 index 00000000000..26a1945efa2 --- /dev/null +++ b/boa_gc/src/pointers/weak.rs @@ -0,0 +1,49 @@ +use crate::{Ephemeron, Finalize, Gc, Trace}; + +/// A weak reference to a [`Gc`]. +/// +/// This type allows keeping references to [`Gc`] managed values without keeping them alive for +/// garbage collections. However, this also means [`WeakGc::value`] can return `None` at any moment. +#[derive(Debug, Trace, Finalize)] +#[repr(transparent)] +pub struct WeakGc { + inner: Ephemeron, +} + +impl WeakGc { + /// Creates a new weak pointer for a garbage collected value. + pub fn new(value: &Gc) -> Self { + Self { + inner: Ephemeron::new(value, ()), + } + } +} + +impl WeakGc { + #[inline] + /// Gets the value of this weak pointer, or `None` if the value was already garbage collected. + pub fn value(&self) -> Option<&T> { + self.inner.key() + } + + #[inline] + /// Upgrade returns a `Gc` pointer for the internal value if valid, or None if the value was already garbage collected. + pub fn upgrade(&self) -> Option> { + self.inner.upgrade_key() + } +} + +impl Clone for WeakGc { + #[inline] + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + } + } +} + +impl From> for WeakGc { + fn from(inner: Ephemeron) -> Self { + Self { inner } + } +} diff --git a/boa_gc/src/test/allocation.rs b/boa_gc/src/test/allocation.rs new file mode 100644 index 00000000000..27386999836 --- /dev/null +++ b/boa_gc/src/test/allocation.rs @@ -0,0 +1,31 @@ +use super::{run_test, Harness}; +use crate::{force_collect, Gc, GcCell}; + +#[test] +fn gc_basic_cell_allocation() { + run_test(|| { + let gc_cell = Gc::new(GcCell::new(16_u16)); + + force_collect(); + Harness::assert_collections(1); + Harness::assert_bytes_allocated(); + assert_eq!(*gc_cell.borrow_mut(), 16); + }); +} + +#[test] +fn gc_basic_pointer_alloc() { + run_test(|| { + let gc = Gc::new(16_u8); + + force_collect(); + Harness::assert_collections(1); + Harness::assert_bytes_allocated(); + assert_eq!(*gc, 16); + + drop(gc); + force_collect(); + Harness::assert_collections(2); + Harness::assert_empty_gc(); + }); +} diff --git a/boa_gc/src/test/cell.rs b/boa_gc/src/test/cell.rs new file mode 100644 index 00000000000..1e82acf1e7c --- /dev/null +++ b/boa_gc/src/test/cell.rs @@ -0,0 +1,15 @@ +use boa_gc::{Gc, GcCell}; + +use super::run_test; + +#[test] +fn boa_borrow_mut_test() { + run_test(|| { + let v = Gc::new(GcCell::new(Vec::new())); + + for _ in 1..=259 { + let cell = Gc::new(GcCell::new([0u8; 10])); + v.borrow_mut().push(cell); + } + }); +} diff --git a/boa_gc/src/test/mod.rs b/boa_gc/src/test/mod.rs new file mode 100644 index 00000000000..559fbb8d80f --- /dev/null +++ b/boa_gc/src/test/mod.rs @@ -0,0 +1,37 @@ +use crate::BOA_GC; + +mod allocation; +mod cell; +mod weak; + +struct Harness; + +impl Harness { + fn assert_collections(o: usize) { + BOA_GC.with(|current| { + let gc = current.borrow(); + assert_eq!(gc.runtime.collections, o); + }); + } + + fn assert_empty_gc() { + BOA_GC.with(|current| { + let gc = current.borrow(); + + assert!(gc.adult_start.get().is_none()); + assert!(gc.runtime.bytes_allocated == 0); + }); + } + + fn assert_bytes_allocated() { + BOA_GC.with(|current| { + let gc = current.borrow(); + assert!(gc.runtime.bytes_allocated > 0); + }); + } +} + +fn run_test(test: impl FnOnce() + Send + 'static) { + let handle = std::thread::spawn(test); + handle.join().unwrap(); +} diff --git a/boa_gc/src/test/weak.rs b/boa_gc/src/test/weak.rs new file mode 100644 index 00000000000..5f1e33ccc19 --- /dev/null +++ b/boa_gc/src/test/weak.rs @@ -0,0 +1,133 @@ +use boa_gc::{force_collect, Ephemeron, Gc, WeakGc}; + +use super::run_test; + +#[test] +fn eph_weak_gc_test() { + run_test(|| { + let gc_value = Gc::new(3); + + { + let cloned_gc = gc_value.clone(); + + let weak = WeakGc::new(&cloned_gc); + + assert_eq!(*weak.value().expect("Is live currently"), 3); + drop(cloned_gc); + force_collect(); + assert_eq!(*weak.value().expect("WeakGc is still live here"), 3); + + drop(gc_value); + force_collect(); + + assert!(weak.value().is_none()); + } + }); +} + +#[test] +fn eph_ephemeron_test() { + run_test(|| { + let gc_value = Gc::new(3); + + { + let cloned_gc = gc_value.clone(); + + let ephemeron = Ephemeron::new(&cloned_gc, String::from("Hello World!")); + + assert_eq!(*ephemeron.key().expect("Ephemeron is live"), 3); + assert_eq!(*ephemeron.value(), String::from("Hello World!")); + drop(cloned_gc); + force_collect(); + assert_eq!(*ephemeron.key().expect("Ephemeron is still live here"), 3); + + drop(gc_value); + force_collect(); + + assert!(ephemeron.key().is_none()); + } + }); +} + +#[test] +fn eph_allocation_chains() { + run_test(|| { + let gc_value = Gc::new(String::from("foo")); + + { + let cloned_gc = gc_value.clone(); + let weak = WeakGc::new(&cloned_gc); + let wrap = Gc::new(weak); + + assert_eq!(wrap.value().expect("weak is live"), &String::from("foo")); + + let eph = Ephemeron::new(&wrap, 3); + + drop(cloned_gc); + force_collect(); + + assert_eq!( + eph.key() + .expect("eph is still live") + .value() + .expect("weak is still live"), + &String::from("foo") + ); + + drop(gc_value); + force_collect(); + + assert!(eph.key().expect("eph is still live").value().is_none()); + } + }); +} + +#[test] +fn eph_basic_alloc_dump_test() { + run_test(|| { + let gc_value = Gc::new(String::from("gc here")); + let _gc_two = Gc::new("hmmm"); + + let eph = Ephemeron::new(&gc_value, 4); + let _fourth = Gc::new("tail"); + + assert_eq!(*eph.key().expect("must be live"), String::from("gc here")); + }); +} + +#[test] +fn eph_basic_upgrade_test() { + run_test(|| { + let init_gc = Gc::new(String::from("foo")); + + let weak = WeakGc::new(&init_gc); + + let new_gc = weak.upgrade().expect("Weak is still live"); + + drop(weak); + force_collect(); + + assert_eq!(*init_gc, *new_gc); + }); +} + +#[test] +fn eph_basic_clone_test() { + run_test(|| { + let init_gc = Gc::new(String::from("bar")); + + let weak = WeakGc::new(&init_gc); + + let new_gc = weak.upgrade().expect("Weak is live"); + let new_weak = weak.clone(); + + drop(weak); + force_collect(); + + assert_eq!(*new_gc, *new_weak.value().expect("weak should be live")); + assert_eq!( + *init_gc, + *new_weak.value().expect("weak_should be live still") + ); + }); +} diff --git a/boa_gc/src/trace.rs b/boa_gc/src/trace.rs new file mode 100644 index 00000000000..354a0119d40 --- /dev/null +++ b/boa_gc/src/trace.rs @@ -0,0 +1,450 @@ +use std::borrow::{Cow, ToOwned}; +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; +use std::hash::{BuildHasher, Hash}; +use std::marker::PhantomData; +use std::num::{ + NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, + NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, +}; +use std::path::{Path, PathBuf}; +use std::rc::Rc; +use std::sync::atomic::{ + AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, + AtomicU64, AtomicU8, AtomicUsize, +}; + +/// Substitute for the [`Drop`] trait for garbage collected types. +pub trait Finalize { + /// Cleanup logic for a type. + fn finalize(&self) {} +} + +/// The Trace trait, which needs to be implemented on garbage-collected objects. +/// +/// # Safety +/// +/// - An incorrect implementation of the trait can result in heap overflows, data corruption, +/// use-after-free, or Undefined Behaviour in general. +/// +/// - Calling any of the functions marked as `unsafe` outside of the context of the garbage collector +/// can result in Undefined Behaviour. +pub unsafe trait Trace: Finalize { + /// Marks all contained `Gc`s. + /// + /// # Safety + /// + /// See [`Trace`]. + unsafe fn trace(&self); + + /// Marks all contained weak references of a `Gc`. + /// + /// # Safety + /// + /// See [`Trace`]. + unsafe fn weak_trace(&self); + + /// Increments the root-count of all contained `Gc`s. + /// + /// # Safety + /// + /// See [`Trace`]. + unsafe fn root(&self); + + /// Decrements the root-count of all contained `Gc`s. + /// + /// # Safety + /// + /// See [`Trace`]. + unsafe fn unroot(&self); + + /// Checks if an ephemeron's key is marked. + #[doc(hidden)] + fn is_marked_ephemeron(&self) -> bool { + false + } + + /// Runs [`Finalize::finalize`] on this object and all + /// contained subobjects. + fn run_finalizer(&self); +} + +/// Utility macro to define an empty implementation of [`Trace`]. +/// +/// Use this for marking types as not containing any `Trace` types. +#[macro_export] +macro_rules! empty_trace { + () => { + #[inline] + unsafe fn trace(&self) {} + #[inline] + unsafe fn weak_trace(&self) {} + #[inline] + unsafe fn root(&self) {} + #[inline] + unsafe fn unroot(&self) {} + #[inline] + fn run_finalizer(&self) { + $crate::Finalize::finalize(self) + } + }; +} + +/// Utility macro to manually implement [`Trace`] on a type. +/// +/// You define a `this` parameter name and pass in a body, which should call `mark` on every +/// traceable element inside the body. The mark implementation will automatically delegate to the +/// correct method on the argument. +/// +/// # Safety +/// +/// Misusing the `mark` function may result in Undefined Behaviour. +#[macro_export] +macro_rules! custom_trace { + ($this:ident, $body:expr) => { + #[inline] + unsafe fn trace(&self) { + #[inline] + fn mark(it: &T) { + // SAFETY: The implementor must ensure that `trace` is correctly implemented. + unsafe { + $crate::Trace::trace(it); + } + } + let $this = self; + $body + } + #[inline] + unsafe fn weak_trace(&self) { + #[inline] + fn mark(it: &T) { + // SAFETY: The implementor must ensure that `weak_trace` is correctly implemented. + unsafe { + $crate::Trace::weak_trace(it); + } + } + let $this = self; + $body + } + #[inline] + unsafe fn root(&self) { + #[inline] + fn mark(it: &T) { + // SAFETY: The implementor must ensure that `root` is correctly implemented. + unsafe { + $crate::Trace::root(it); + } + } + let $this = self; + $body + } + #[inline] + unsafe fn unroot(&self) { + #[inline] + fn mark(it: &T) { + // SAFETY: The implementor must ensure that `unroot` is correctly implemented. + unsafe { + $crate::Trace::unroot(it); + } + } + let $this = self; + $body + } + #[inline] + fn run_finalizer(&self) { + #[inline] + fn mark(it: &T) { + $crate::Trace::run_finalizer(it); + } + $crate::Finalize::finalize(self); + let $this = self; + $body + } + }; +} + +impl Finalize for &'static T {} +// SAFETY: 'static references don't need to be traced, since they live indefinitely. +unsafe impl Trace for &'static T { + empty_trace!(); +} + +macro_rules! simple_empty_finalize_trace { + ($($T:ty),*) => { + $( + impl Finalize for $T {} + + // SAFETY: + // Primitive types and string types don't have inner nodes that need to be marked. + unsafe impl Trace for $T { empty_trace!(); } + )* + } +} + +simple_empty_finalize_trace![ + (), + bool, + isize, + usize, + i8, + u8, + i16, + u16, + i32, + u32, + i64, + u64, + i128, + u128, + f32, + f64, + char, + String, + Box, + Rc, + Path, + PathBuf, + NonZeroIsize, + NonZeroUsize, + NonZeroI8, + NonZeroU8, + NonZeroI16, + NonZeroU16, + NonZeroI32, + NonZeroU32, + NonZeroI64, + NonZeroU64, + NonZeroI128, + NonZeroU128, + AtomicBool, + AtomicIsize, + AtomicUsize, + AtomicI8, + AtomicU8, + AtomicI16, + AtomicU16, + AtomicI32, + AtomicU32, + AtomicI64, + AtomicU64 +]; + +impl Finalize for [T; N] {} +// SAFETY: +// All elements inside the array are correctly marked. +unsafe impl Trace for [T; N] { + custom_trace!(this, { + for v in this { + mark(v); + } + }); +} + +macro_rules! fn_finalize_trace_one { + ($ty:ty $(,$args:ident)*) => { + impl Finalize for $ty {} + // SAFETY: + // Function pointers don't have inner nodes that need to be marked. + unsafe impl Trace for $ty { empty_trace!(); } + } +} +macro_rules! fn_finalize_trace_group { + () => { + fn_finalize_trace_one!(extern "Rust" fn () -> Ret); + fn_finalize_trace_one!(extern "C" fn () -> Ret); + fn_finalize_trace_one!(unsafe extern "Rust" fn () -> Ret); + fn_finalize_trace_one!(unsafe extern "C" fn () -> Ret); + }; + ($($args:ident),*) => { + fn_finalize_trace_one!(extern "Rust" fn ($($args),*) -> Ret, $($args),*); + fn_finalize_trace_one!(extern "C" fn ($($args),*) -> Ret, $($args),*); + fn_finalize_trace_one!(extern "C" fn ($($args),*, ...) -> Ret, $($args),*); + fn_finalize_trace_one!(unsafe extern "Rust" fn ($($args),*) -> Ret, $($args),*); + fn_finalize_trace_one!(unsafe extern "C" fn ($($args),*) -> Ret, $($args),*); + fn_finalize_trace_one!(unsafe extern "C" fn ($($args),*, ...) -> Ret, $($args),*); + } +} + +macro_rules! tuple_finalize_trace { + () => {}; // This case is handled above, by simple_finalize_empty_trace!(). + ($($args:ident),*) => { + impl<$($args),*> Finalize for ($($args,)*) {} + // SAFETY: + // All elements inside the tuple are correctly marked. + unsafe impl<$($args: $crate::Trace),*> Trace for ($($args,)*) { + custom_trace!(this, { + #[allow(non_snake_case, unused_unsafe)] + fn avoid_lints<$($args: $crate::Trace),*>(&($(ref $args,)*): &($($args,)*)) { + // SAFETY: The implementor must ensure a correct implementation. + unsafe { $(mark($args);)* } + } + avoid_lints(this) + }); + } + } +} + +macro_rules! type_arg_tuple_based_finalize_trace_impls { + ($(($($args:ident),*);)*) => { + $( + fn_finalize_trace_group!($($args),*); + tuple_finalize_trace!($($args),*); + )* + } +} + +type_arg_tuple_based_finalize_trace_impls![ + (); + (A); + (A, B); + (A, B, C); + (A, B, C, D); + (A, B, C, D, E); + (A, B, C, D, E, F); + (A, B, C, D, E, F, G); + (A, B, C, D, E, F, G, H); + (A, B, C, D, E, F, G, H, I); + (A, B, C, D, E, F, G, H, I, J); + (A, B, C, D, E, F, G, H, I, J, K); + (A, B, C, D, E, F, G, H, I, J, K, L); +]; + +impl Finalize for Box {} +// SAFETY: The inner value of the `Box` is correctly marked. +unsafe impl Trace for Box { + custom_trace!(this, { + mark(&**this); + }); +} + +impl Finalize for Box<[T]> {} +// SAFETY: All the inner elements of the `Box` array are correctly marked. +unsafe impl Trace for Box<[T]> { + custom_trace!(this, { + for e in this.iter() { + mark(e); + } + }); +} + +impl Finalize for Vec {} +// SAFETY: All the inner elements of the `Vec` are correctly marked. +unsafe impl Trace for Vec { + custom_trace!(this, { + for e in this { + mark(e); + } + }); +} + +impl Finalize for Option {} +// SAFETY: The inner value of the `Option` is correctly marked. +unsafe impl Trace for Option { + custom_trace!(this, { + if let Some(ref v) = *this { + mark(v); + } + }); +} + +impl Finalize for Result {} +// SAFETY: Both inner values of the `Result` are correctly marked. +unsafe impl Trace for Result { + custom_trace!(this, { + match *this { + Ok(ref v) => mark(v), + Err(ref v) => mark(v), + } + }); +} + +impl Finalize for BinaryHeap {} +// SAFETY: All the elements of the `BinaryHeap` are correctly marked. +unsafe impl Trace for BinaryHeap { + custom_trace!(this, { + for v in this.iter() { + mark(v); + } + }); +} + +impl Finalize for BTreeMap {} +// SAFETY: All the elements of the `BTreeMap` are correctly marked. +unsafe impl Trace for BTreeMap { + custom_trace!(this, { + for (k, v) in this { + mark(k); + mark(v); + } + }); +} + +impl Finalize for BTreeSet {} +// SAFETY: All the elements of the `BTreeSet` are correctly marked. +unsafe impl Trace for BTreeSet { + custom_trace!(this, { + for v in this { + mark(v); + } + }); +} + +impl Finalize for HashMap {} +// SAFETY: All the elements of the `HashMap` are correctly marked. +unsafe impl Trace for HashMap { + custom_trace!(this, { + for (k, v) in this.iter() { + mark(k); + mark(v); + } + }); +} + +impl Finalize for HashSet {} +// SAFETY: All the elements of the `HashSet` are correctly marked. +unsafe impl Trace for HashSet { + custom_trace!(this, { + for v in this.iter() { + mark(v); + } + }); +} + +impl Finalize for LinkedList {} +// SAFETY: All the elements of the `LinkedList` are correctly marked. +unsafe impl Trace for LinkedList { + custom_trace!(this, { + for v in this.iter() { + mark(v); + } + }); +} + +impl Finalize for PhantomData {} +// SAFETY: A `PhantomData` doesn't have inner data that needs to be marked. +unsafe impl Trace for PhantomData { + empty_trace!(); +} + +impl Finalize for VecDeque {} +// SAFETY: All the elements of the `VecDeque` are correctly marked. +unsafe impl Trace for VecDeque { + custom_trace!(this, { + for v in this.iter() { + mark(v); + } + }); +} + +impl Finalize for Cow<'static, T> {} +// SAFETY: 'static references don't need to be traced, since they live indefinitely, and the owned +// variant is correctly marked. +unsafe impl Trace for Cow<'static, T> +where + T::Owned: Trace, +{ + custom_trace!(this, { + if let Cow::Owned(ref v) = this { + mark(v); + } + }); +} diff --git a/boa_macros/Cargo.toml b/boa_macros/Cargo.toml index d68aae7e972..048fd0f0d67 100644 --- a/boa_macros/Cargo.toml +++ b/boa_macros/Cargo.toml @@ -15,4 +15,5 @@ proc-macro = true [dependencies] quote = "1.0.21" syn = "1.0.103" - +proc-macro2 = "1.0" +synstructure = "0.12" diff --git a/boa_macros/src/lib.rs b/boa_macros/src/lib.rs index 205905786c9..c1fc87fa6dd 100644 --- a/boa_macros/src/lib.rs +++ b/boa_macros/src/lib.rs @@ -1,6 +1,7 @@ use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, LitStr}; +use synstructure::{decl_derive, AddBounds, Structure}; /// Construct a utf-16 array literal from a utf-8 [`str`] literal. #[proc_macro] @@ -13,3 +14,103 @@ pub fn utf16(input: TokenStream) -> TokenStream { } .into() } + +decl_derive!([Trace, attributes(unsafe_ignore_trace)] => derive_trace); + +fn derive_trace(mut s: Structure<'_>) -> proc_macro2::TokenStream { + s.filter(|bi| { + !bi.ast() + .attrs + .iter() + .any(|attr| attr.path.is_ident("unsafe_ignore_trace")) + }); + let trace_body = s.each(|bi| quote!(mark(#bi))); + + s.add_bounds(AddBounds::Fields); + let trace_impl = s.unsafe_bound_impl( + quote!(::boa_gc::Trace), + quote! { + #[inline] + unsafe fn trace(&self) { + #[allow(dead_code)] + #[inline] + fn mark(it: &T) { + unsafe { + ::boa_gc::Trace::trace(it); + } + } + match *self { #trace_body } + } + #[inline] + unsafe fn weak_trace(&self) { + #[allow(dead_code, unreachable_code)] + #[inline] + fn mark(it: &T) { + unsafe { + ::boa_gc::Trace::weak_trace(it) + } + } + match *self { #trace_body } + } + #[inline] + unsafe fn root(&self) { + #[allow(dead_code)] + #[inline] + fn mark(it: &T) { + unsafe { + ::boa_gc::Trace::root(it); + } + } + match *self { #trace_body } + } + #[inline] + unsafe fn unroot(&self) { + #[allow(dead_code)] + #[inline] + fn mark(it: &T) { + unsafe { + ::boa_gc::Trace::unroot(it); + } + } + match *self { #trace_body } + } + #[inline] + fn run_finalizer(&self) { + ::boa_gc::Finalize::finalize(self); + #[allow(dead_code)] + #[inline] + fn mark(it: &T) { + unsafe { + ::boa_gc::Trace::run_finalizer(it); + } + } + match *self { #trace_body } + } + }, + ); + + // We also implement drop to prevent unsafe drop implementations on this + // type and encourage people to use Finalize. This implementation will + // call `Finalize::finalize` if it is safe to do so. + let drop_impl = s.unbound_impl( + quote!(::std::ops::Drop), + quote! { + fn drop(&mut self) { + if ::boa_gc::finalizer_safe() { + ::boa_gc::Finalize::finalize(self); + } + } + }, + ); + + quote! { + #trace_impl + #drop_impl + } +} + +decl_derive!([Finalize] => derive_finalize); + +fn derive_finalize(s: Structure<'_>) -> proc_macro2::TokenStream { + s.unbound_impl(quote!(::boa_gc::Finalize), quote!()) +} diff --git a/boa_tester/Cargo.toml b/boa_tester/Cargo.toml index 2d55ff8aeeb..6f252beb774 100644 --- a/boa_tester/Cargo.toml +++ b/boa_tester/Cargo.toml @@ -25,6 +25,5 @@ regex = "1.7.0" once_cell = "1.16.0" colored = "2.0.0" fxhash = "0.2.1" -gc = { version = "0.4.1", features = ["derive"] } rayon = "1.5.3" anyhow = "1.0.66" diff --git a/boa_tester/src/exec/mod.rs b/boa_tester/src/exec/mod.rs index 4310c8219e0..dc948894646 100644 --- a/boa_tester/src/exec/mod.rs +++ b/boa_tester/src/exec/mod.rs @@ -12,7 +12,7 @@ use boa_engine::{ builtins::JsArgs, object::FunctionBuilder, property::Attribute, Context, JsNativeErrorKind, JsResult, JsValue, }; -use boa_gc::{Cell, Finalize, Gc, Trace}; +use boa_gc::{Finalize, Gc, GcCell, Trace}; use boa_parser::Parser; use colored::Colorize; use rayon::prelude::*; @@ -400,13 +400,13 @@ impl Test { /// Object which includes the result of the async operation. #[derive(Debug, Clone, Trace, Finalize)] struct AsyncResult { - inner: Gc>>, + inner: Gc>>, } impl Default for AsyncResult { fn default() -> Self { Self { - inner: Gc::new(Cell::new(Ok(()))), + inner: Gc::new(GcCell::new(Ok(()))), } } } From ff02cd07f1e7a2e5d27f8ce3848e765ba1773434 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Nov 2022 17:21:52 +0000 Subject: [PATCH 08/24] Bump clap from 4.0.23 to 4.0.24 (#2439) Bumps [clap](https://github.com/clap-rs/clap) from 4.0.23 to 4.0.24.
Release notes

Sourced from clap's releases.

v4.0.24

[4.0.24] - 2022-11-14

Fixes

  • Avoid panic when printing an argument that isn't built
Changelog

Sourced from clap's changelog.

[4.0.24] - 2022-11-14

Fixes

  • Avoid panic when printing an argument that isn't built
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=clap&package-manager=cargo&previous-version=4.0.23&new-version=4.0.24)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- Cargo.lock | 8 ++++---- boa_cli/Cargo.toml | 2 +- boa_tester/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1447e5da805..dc216db0d53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,7 +85,7 @@ dependencies = [ "boa_engine", "boa_interner", "boa_parser", - "clap 4.0.23", + "clap 4.0.24", "colored", "jemallocator", "phf", @@ -216,7 +216,7 @@ dependencies = [ "boa_gc", "boa_interner", "boa_parser", - "clap 4.0.23", + "clap 4.0.24", "colored", "fxhash", "once_cell", @@ -329,9 +329,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.0.23" +version = "4.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eb41c13df48950b20eb4cd0eefa618819469df1bffc49d11e8487c4ba0037e5" +checksum = "60494cedb60cb47462c0ff7be53de32c0e42a6fc2c772184554fa12bd9489c03" dependencies = [ "atty", "bitflags", diff --git a/boa_cli/Cargo.toml b/boa_cli/Cargo.toml index 312e4d05a82..659ff2b91b4 100644 --- a/boa_cli/Cargo.toml +++ b/boa_cli/Cargo.toml @@ -18,7 +18,7 @@ boa_interner.workspace = true boa_parser.workspace = true rustyline = "10.0.0" rustyline-derive = "0.7.0" -clap = { version = "4.0.23", features = ["derive"] } +clap = { version = "4.0.24", features = ["derive"] } serde_json = "1.0.87" colored = "2.0.0" regex = "1.7.0" diff --git a/boa_tester/Cargo.toml b/boa_tester/Cargo.toml index 6f252beb774..1119ac2ed35 100644 --- a/boa_tester/Cargo.toml +++ b/boa_tester/Cargo.toml @@ -16,7 +16,7 @@ boa_engine = { workspace = true, features = ["intl"] } boa_interner.workspace = true boa_gc.workspace = true boa_parser.workspace = true -clap = { version = "4.0.23", features = ["derive"] } +clap = { version = "4.0.24", features = ["derive"] } serde = { version = "1.0.147", features = ["derive"] } serde_yaml = "0.9.14" serde_json = "1.0.87" From c1b5f38d11615b1443e27112a1cba307261bfd9d Mon Sep 17 00:00:00 2001 From: Addison Crump Date: Tue, 15 Nov 2022 18:30:59 +0000 Subject: [PATCH 09/24] VM Fuzzer (#2401) This Pull Request offers a basic VM fuzzer which relies on implied oracles (namely, "does it crash or timeout?"). It changes the following: - Adds an insns_remaining field to Context, denoting the number of instructions remaining to execute (only available when fuzzing) - Adds a JsNativeError variant, denoting when the number of instructions has been exceeded (only available when fuzzing) - Adds a VM fuzzer which looks for cases where Boa may crash on an input This offers no guarantees about correctness, only assertion violations. Depends on #2400. Any issues I raise in association with this fuzzer will link back to this fuzzer. You may run the fuzzer using the following commands: ```bash $ cd boa_engine $ cargo +nightly fuzz run -s none vm-implied ``` Co-authored-by: Addison Crump --- boa_engine/src/context/mod.rs | 17 +++++++++++++ boa_engine/src/error.rs | 23 +++++++++++++++++ boa_engine/src/vm/mod.rs | 9 +++++++ fuzz/Cargo.lock | 31 +++++------------------ fuzz/Cargo.toml | 12 +++++++++ fuzz/README.md | 19 ++++++++++++++ fuzz/fuzz_targets/bytecompiler-implied.rs | 25 ++++++++++++++++++ fuzz/fuzz_targets/common.rs | 24 +++++++++++++++++- fuzz/fuzz_targets/vm-implied.rs | 19 ++++++++++++++ 9 files changed, 153 insertions(+), 26 deletions(-) create mode 100644 fuzz/fuzz_targets/bytecompiler-implied.rs create mode 100644 fuzz/fuzz_targets/vm-implied.rs diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index 47cc742b3d1..cc9691d4f58 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -97,6 +97,10 @@ pub struct Context { #[cfg(feature = "intl")] icu: icu::Icu, + /// Number of instructions remaining before a forced exit + #[cfg(feature = "fuzz")] + pub(crate) instructions_remaining: usize, + pub(crate) vm: Vm, pub(crate) promise_job_queue: VecDeque, @@ -593,6 +597,8 @@ pub struct ContextBuilder { interner: Option, #[cfg(feature = "intl")] icu: Option, + #[cfg(feature = "fuzz")] + instructions_remaining: usize, } impl ContextBuilder { @@ -615,6 +621,15 @@ impl ContextBuilder { Ok(self) } + /// Specifies the number of instructions remaining to the [`Context`]. + /// + /// This function is only available if the `fuzz` feature is enabled. + #[cfg(feature = "fuzz")] + pub fn instructions_remaining(mut self, instructions_remaining: usize) -> Self { + self.instructions_remaining = instructions_remaining; + self + } + /// Creates a new [`ContextBuilder`] with a default empty [`Interner`] /// and a default [`BoaProvider`] if the `intl` feature is enabled. pub fn new() -> Self { @@ -643,6 +658,8 @@ impl ContextBuilder { icu::Icu::new(Box::new(icu_testdata::get_provider())) .expect("Failed to initialize default icu data.") }), + #[cfg(feature = "fuzz")] + instructions_remaining: self.instructions_remaining, promise_job_queue: VecDeque::new(), }; diff --git a/boa_engine/src/error.rs b/boa_engine/src/error.rs index 00e124da31f..c76bee7f336 100644 --- a/boa_engine/src/error.rs +++ b/boa_engine/src/error.rs @@ -503,6 +503,17 @@ impl JsNativeError { Self::new(JsNativeErrorKind::Uri, Box::default(), None) } + /// Creates a new `JsNativeError` that indicates that the context hit its execution limit. This + /// is only used in a fuzzing context. + #[cfg(feature = "fuzz")] + pub fn no_instructions_remain() -> Self { + Self::new( + JsNativeErrorKind::NoInstructionsRemain, + Box::default(), + None, + ) + } + /// Sets the message of this error. /// /// # Examples @@ -619,6 +630,12 @@ impl JsNativeError { } JsNativeErrorKind::Type => (constructors.type_error().prototype(), ErrorKind::Type), JsNativeErrorKind::Uri => (constructors.uri_error().prototype(), ErrorKind::Uri), + #[cfg(feature = "fuzz")] + JsNativeErrorKind::NoInstructionsRemain => { + unreachable!( + "The NoInstructionsRemain native error cannot be converted to an opaque type." + ) + } }; let o = JsObject::from_proto_and_data(prototype, ObjectData::error(tag)); @@ -747,6 +764,10 @@ pub enum JsNativeErrorKind { /// [e_uri]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI /// [d_uri]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURI Uri, + /// Error thrown when no instructions remain. Only used in a fuzzing context; not a valid JS + /// error variant. + #[cfg(feature = "fuzz")] + NoInstructionsRemain, } impl std::fmt::Display for JsNativeErrorKind { @@ -760,6 +781,8 @@ impl std::fmt::Display for JsNativeErrorKind { JsNativeErrorKind::Syntax => "SyntaxError", JsNativeErrorKind::Type => "TypeError", JsNativeErrorKind::Uri => "UriError", + #[cfg(feature = "fuzz")] + JsNativeErrorKind::NoInstructionsRemain => "NoInstructionsRemain", } .fmt(f) } diff --git a/boa_engine/src/vm/mod.rs b/boa_engine/src/vm/mod.rs index 49ba10667e1..7f75db94aa2 100644 --- a/boa_engine/src/vm/mod.rs +++ b/boa_engine/src/vm/mod.rs @@ -7,6 +7,8 @@ use crate::{ vm::{call_frame::CatchAddresses, code_block::Readable}, Context, JsResult, JsValue, }; +#[cfg(feature = "fuzz")] +use crate::{JsError, JsNativeError}; use boa_interner::ToInternedString; use boa_profiler::Profiler; use std::{convert::TryInto, mem::size_of, time::Instant}; @@ -179,6 +181,13 @@ impl Context { }); while self.vm.frame().pc < self.vm.frame().code.code.len() { + #[cfg(feature = "fuzz")] + if self.instructions_remaining == 0 { + return Err(JsError::from_native(JsNativeError::no_instructions_remain())); + } else { + self.instructions_remaining -= 1; + } + let result = if self.vm.trace { let mut pc = self.vm.frame().pc; let opcode: Opcode = self diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index beb643d96dc..ab15b48ab56 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -59,7 +59,6 @@ dependencies = [ "chrono", "dyn-clone", "fast-float", - "gc", "indexmap", "num-bigint", "num-integer", @@ -93,7 +92,8 @@ dependencies = [ name = "boa_gc" version = "0.16.0" dependencies = [ - "gc", + "boa_macros", + "boa_profiler", ] [[package]] @@ -113,8 +113,10 @@ dependencies = [ name = "boa_macros" version = "0.16.0" dependencies = [ + "proc-macro2", "quote", "syn", + "synstructure", ] [[package]] @@ -167,9 +169,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ "iana-time-zone", "js-sys", @@ -263,27 +265,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95765f67b4b18863968b4a1bd5bb576f732b29a4a28c7cd84c09fa3e2875f33c" -[[package]] -name = "gc" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edaac0f5832202ebc99520cb77c932248010c4645d20be1dc62d6579f5b3752" -dependencies = [ - "gc_derive", -] - -[[package]] -name = "gc_derive" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60df8444f094ff7885631d80e78eb7d88c3c2361a98daaabb06256e4500db941" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "getrandom" version = "0.2.8" diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index cc86d751da9..bdd84c79cf4 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -27,3 +27,15 @@ name = "parser-idempotency" path = "fuzz_targets/parser-idempotency.rs" test = false doc = false + +[[bin]] +name = "vm-implied" +path = "fuzz_targets/vm-implied.rs" +test = false +doc = false + +[[bin]] +name = "bytecompiler-implied" +path = "fuzz_targets/bytecompiler-implied.rs" +test = false +doc = false diff --git a/fuzz/README.md b/fuzz/README.md index 097e46ccb97..9bb314bfd6b 100644 --- a/fuzz/README.md +++ b/fuzz/README.md @@ -35,3 +35,22 @@ following: information, as the inputs parsed between the two should be the same. In this way, this fuzzer can identify correctness issues present in the parser. + +## Bytecompiler Fuzzer + +The bytecompiler fuzzer, located in [bytecompiler-implied.rs](fuzz_targets/bytecompiler-implied.rs), identifies cases +which cause an assertion failure in the bytecompiler. These crashes can cause denial of service issues and may block the +discovery of crash cases in the VM fuzzer. + +## VM Fuzzer + +The VM fuzzer, located in [vm-implied.rs](fuzz_targets/vm-implied.rs), identifies crash cases in the VM. It does so by +generating an arbitrary AST, converting it to source code (to remove invalid inputs), then executing that source code. +Because we are not comparing against any invariants other than "does it crash", this fuzzer will only discover faults +which cause the VM to terminate unexpectedly, e.g. as a result of a panic. It will not discover logic errors present in +the VM. + +To ensure that the VM does not attempt to execute an infinite loop, Boa is restricted to a finite number of instructions +before the VM is terminated. If a program takes more than a second or so to execute, it likely indicates an issue in the +VM (as we expect the fuzzer to execute only a certain amount of instructions, which should take significantly less +time). diff --git a/fuzz/fuzz_targets/bytecompiler-implied.rs b/fuzz/fuzz_targets/bytecompiler-implied.rs new file mode 100644 index 00000000000..dd91bbc32a3 --- /dev/null +++ b/fuzz/fuzz_targets/bytecompiler-implied.rs @@ -0,0 +1,25 @@ +#![no_main] + +mod common; + +use crate::common::FuzzSource; +use boa_engine::Context; +use boa_parser::Parser; +use libfuzzer_sys::{fuzz_target, Corpus}; +use std::io::Cursor; + +fn do_fuzz(original: FuzzSource) -> Corpus { + let mut ctx = Context::builder() + .interner(original.interner) + .instructions_remaining(0) + .build(); + let mut parser = Parser::new(Cursor::new(&original.source)); + if let Ok(parsed) = parser.parse_all(ctx.interner_mut()) { + let _ = ctx.compile(&parsed); + Corpus::Keep + } else { + Corpus::Reject + } +} + +fuzz_target!(|original: FuzzSource| -> Corpus { do_fuzz(original) }); diff --git a/fuzz/fuzz_targets/common.rs b/fuzz/fuzz_targets/common.rs index 161a6145c00..8d4c50b5425 100644 --- a/fuzz/fuzz_targets/common.rs +++ b/fuzz/fuzz_targets/common.rs @@ -2,7 +2,7 @@ use boa_ast::{ visitor::{VisitWith, VisitorMut}, Expression, StatementList, }; -use boa_interner::{Interner, Sym}; +use boa_interner::{Interner, Sym, ToInternedString}; use libfuzzer_sys::arbitrary; use libfuzzer_sys::arbitrary::{Arbitrary, Unstructured}; use std::fmt::{Debug, Formatter}; @@ -72,3 +72,25 @@ impl Debug for FuzzData { .finish_non_exhaustive() } } + +pub struct FuzzSource { + pub interner: Interner, + pub source: String, +} + +impl<'a> Arbitrary<'a> for FuzzSource { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + let data = FuzzData::arbitrary(u)?; + let source = data.ast.to_interned_string(&data.interner); + Ok(Self { + interner: data.interner, + source, + }) + } +} + +impl Debug for FuzzSource { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("Fuzzed source:\n{}", self.source)) + } +} diff --git a/fuzz/fuzz_targets/vm-implied.rs b/fuzz/fuzz_targets/vm-implied.rs new file mode 100644 index 00000000000..77d4c14b3b9 --- /dev/null +++ b/fuzz/fuzz_targets/vm-implied.rs @@ -0,0 +1,19 @@ +#![no_main] + +mod common; + +use crate::common::FuzzSource; +use boa_engine::{Context, JsResult, JsValue}; +use libfuzzer_sys::fuzz_target; + +fn do_fuzz(original: FuzzSource) -> JsResult { + let mut ctx = Context::builder() + .interner(original.interner) + .instructions_remaining(1 << 16) + .build(); + ctx.eval(&original.source) +} + +fuzz_target!(|original: FuzzSource| { + let _ = do_fuzz(original); +}); From 79f638d667d3ccef3600e6339eb401400565682b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Juli=C3=A1n=20Espina?= Date: Tue, 15 Nov 2022 21:25:10 +0000 Subject: [PATCH 10/24] Implement the `WeakRef` builtin (#2438) 52/60 tests passing. The remaining tests are either features not implemented ([FinalizationRegistry](https://tc39.es/ecma262/multipage/managing-memory.html#sec-finalization-registry-objects)) or features still in development ([symbols-as-weakmap-keys](https://github.com/tc39/proposal-symbols-as-weakmap-keys)). --- boa_engine/src/builtins/array/mod.rs | 5 +- boa_engine/src/builtins/mod.rs | 42 +++--- boa_engine/src/builtins/object/mod.rs | 4 +- boa_engine/src/builtins/weak/mod.rs | 3 + boa_engine/src/builtins/weak/weak_ref.rs | 166 +++++++++++++++++++++ boa_engine/src/context/intrinsics.rs | 7 + boa_engine/src/context/mod.rs | 17 +++ boa_engine/src/object/jsobject.rs | 11 ++ boa_engine/src/object/mod.rs | 25 +++- boa_engine/src/profiler.rs | 1 - boa_engine/src/tests.rs | 2 +- boa_engine/src/value/mod.rs | 3 +- boa_engine/src/value/operations.rs | 4 +- boa_engine/src/vm/code_block.rs | 2 +- boa_engine/src/vm/opcode/binary_ops/mod.rs | 4 +- test_ignore.txt | 6 + 16 files changed, 264 insertions(+), 38 deletions(-) create mode 100644 boa_engine/src/builtins/weak/mod.rs create mode 100644 boa_engine/src/builtins/weak/weak_ref.rs delete mode 100644 boa_engine/src/profiler.rs diff --git a/boa_engine/src/builtins/array/mod.rs b/boa_engine/src/builtins/array/mod.rs index 846cec01c95..2e4245a8eab 100644 --- a/boa_engine/src/builtins/array/mod.rs +++ b/boa_engine/src/builtins/array/mod.rs @@ -420,10 +420,7 @@ impl Array { JsValue::Object(o) if o.is_callable() => Some(o), _ => { return Err(JsNativeError::typ() - .with_message(format!( - "{} is not a function", - mapfn.type_of().to_std_string_escaped() - )) + .with_message(format!("`{}` is not callable", mapfn.type_of())) .into()) } }; diff --git a/boa_engine/src/builtins/mod.rs b/boa_engine/src/builtins/mod.rs index d6aeda78608..3e7aa658d8f 100644 --- a/boa_engine/src/builtins/mod.rs +++ b/boa_engine/src/builtins/mod.rs @@ -33,6 +33,7 @@ pub mod symbol; pub mod typed_array; pub mod undefined; pub mod uri; +pub mod weak; #[cfg(feature = "console")] pub mod console; @@ -82,44 +83,42 @@ use crate::{ builtins::{ array_buffer::ArrayBuffer, async_generator::AsyncGenerator, async_generator_function::AsyncGeneratorFunction, generator::Generator, - generator_function::GeneratorFunction, typed_array::TypedArray, uri::Uri, + generator_function::GeneratorFunction, typed_array::TypedArray, uri::Uri, weak::WeakRef, }, property::{Attribute, PropertyDescriptor}, Context, JsValue, }; -/// Trait representing a global built-in object such as `Math`, `Object` or -/// `String`. +/// Trait representing a global built-in object such as `Math`, `Object` or `String`. /// -/// This trait must be implemented for any global built-in accessible from -/// Javascript. +/// This trait must be implemented for any global built-in accessible from JavaScript. pub(crate) trait BuiltIn { /// Binding name of the built-in inside the global object. /// - /// E.g. If you want access the properties of a `Complex` built-in - /// with the name `Cplx` you must assign `"Cplx"` to this constant, - /// making any property inside it accessible from Javascript as `Cplx.prop` + /// E.g. If you want access the properties of a `Complex` built-in with the name `Cplx` you must + /// assign `"Cplx"` to this constant, making any property inside it accessible from Javascript + /// as `Cplx.prop` const NAME: &'static str; - /// Property attribute flags of the built-in. - /// Check [Attribute] for more information. + /// Property attribute flags of the built-in. Check [`Attribute`] for more information. const ATTRIBUTE: Attribute = Attribute::WRITABLE .union(Attribute::NON_ENUMERABLE) .union(Attribute::CONFIGURABLE); /// Initialization code for the built-in. - /// This is where the methods, properties, static methods and the constructor - /// of a built-in must be initialized to be accessible from Javascript. + /// + /// This is where the methods, properties, static methods and the constructor of a built-in must + /// be initialized to be accessible from Javascript. /// /// # Note /// - /// A return value of `None` indicates that the value must not be added as - /// a global attribute for the global object. + /// A return value of `None` indicates that the value must not be added as a global attribute + /// for the global object. fn init(context: &mut Context) -> Option; } -/// Utility function that checks if a type implements `BuiltIn` before -/// initializing it as a global built-in. +/// Utility function that checks if a type implements `BuiltIn` before initializing it as a global +/// built-in. #[inline] fn init_builtin(context: &mut Context) { if let Some(value) = B::init(context) { @@ -195,7 +194,8 @@ pub fn init(context: &mut Context) { AsyncFunction, AsyncGenerator, AsyncGeneratorFunction, - Uri + Uri, + WeakRef }; #[cfg(feature = "intl")] @@ -206,17 +206,15 @@ pub fn init(context: &mut Context) { } pub trait JsArgs { - /// Utility function to `get` a parameter from - /// a `[JsValue]` or default to `JsValue::Undefined` + /// Utility function to `get` a parameter from a `[JsValue]` or default to `JsValue::Undefined` /// if `get` returns `None`. /// /// Call this if you are thinking of calling something similar to /// `args.get(n).cloned().unwrap_or_default()` or /// `args.get(n).unwrap_or(&undefined)`. /// - /// This returns a reference for efficiency, in case - /// you only need to call methods of `JsValue`, so - /// try to minimize calling `clone`. + /// This returns a reference for efficiency, in case you only need to call methods of `JsValue`, + /// so try to minimize calling `clone`. fn get_or_undefined(&self, index: usize) -> &JsValue; } diff --git a/boa_engine/src/builtins/object/mod.rs b/boa_engine/src/builtins/object/mod.rs index a185a750d97..88859126f59 100644 --- a/boa_engine/src/builtins/object/mod.rs +++ b/boa_engine/src/builtins/object/mod.rs @@ -628,8 +628,8 @@ impl Object { val => { return Err(JsNativeError::typ() .with_message(format!( - "expected an object or null, got {}", - val.type_of().to_std_string_escaped() + "expected an object or null, got `{}`", + val.type_of() )) .into()) } diff --git a/boa_engine/src/builtins/weak/mod.rs b/boa_engine/src/builtins/weak/mod.rs new file mode 100644 index 00000000000..0beedf03846 --- /dev/null +++ b/boa_engine/src/builtins/weak/mod.rs @@ -0,0 +1,3 @@ +mod weak_ref; + +pub(crate) use weak_ref::WeakRef; diff --git a/boa_engine/src/builtins/weak/weak_ref.rs b/boa_engine/src/builtins/weak/weak_ref.rs new file mode 100644 index 00000000000..a111b5cd0fb --- /dev/null +++ b/boa_engine/src/builtins/weak/weak_ref.rs @@ -0,0 +1,166 @@ +use boa_gc::{Finalize, Trace, WeakGc}; +use boa_profiler::Profiler; +use tap::{Conv, Pipe}; + +use crate::{ + builtins::{BuiltIn, JsArgs}, + context::intrinsics::StandardConstructors, + object::{ + internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData, + }, + property::Attribute, + symbol::WellKnownSymbols, + Context, JsNativeError, JsResult, JsValue, +}; + +/// The [`WeakRef`][wr] builtin object. +/// +/// [wr]: https://tc39.es/ecma262/#sec-weak-ref-objects +#[derive(Debug, Clone, Trace, Finalize)] +pub(crate) struct WeakRef; + +impl BuiltIn for WeakRef { + const NAME: &'static str = "WeakRef"; + + const ATTRIBUTE: Attribute = Attribute::WRITABLE.union(Attribute::CONFIGURABLE); + + fn init(context: &mut Context) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + ConstructorBuilder::with_standard_constructor( + context, + WeakRef::constructor, + context.intrinsics().constructors().weak_ref().clone(), + ) + .name(Self::NAME) + .length(Self::LENGTH) + .property( + WellKnownSymbols::to_string_tag(), + "WeakRef", + Attribute::CONFIGURABLE, + ) + .method(WeakRef::deref, "deref", 0) + .build() + .conv::() + .pipe(Some) + } +} + +impl WeakRef { + /// The amount of arguments the `WeakRef` constructor takes. + pub(crate) const LENGTH: usize = 1; + + /// Constructor [`WeakRef ( target )`][cons] + /// + /// [cons]: https://tc39.es/ecma262/#sec-weak-ref-target + pub(crate) fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { + // If NewTarget is undefined, throw a TypeError exception. + if new_target.is_undefined() { + return Err(JsNativeError::typ() + .with_message("WeakRef: cannot call constructor without `new`") + .into()); + } + + // 2. If target is not an Object, throw a TypeError exception. + let target = args.get(0).and_then(JsValue::as_object).ok_or_else(|| { + JsNativeError::typ().with_message(format!( + "WeakRef: expected target argument of type `object`, got target of type `{}`", + args.get_or_undefined(0).type_of() + )) + })?; + + // 3. Let weakRef be ? OrdinaryCreateFromConstructor(NewTarget, "%WeakRef.prototype%", « [[WeakRefTarget]] »). + // 5. Set weakRef.[[WeakRefTarget]] to target. + let weak_ref = JsObject::from_proto_and_data( + get_prototype_from_constructor(new_target, StandardConstructors::weak_ref, context)?, + ObjectData::weak_ref(WeakGc::new(target.inner())), + ); + + // 4. Perform AddToKeptObjects(target). + context.kept_alive.push(target.clone()); + + // 6. Return weakRef. + Ok(weak_ref.into()) + } + + /// Method [`WeakRef.prototype.deref ( )`][spec]. + /// + /// If the referenced object hasn't been collected, this method promotes a `WeakRef` into a + /// proper [`JsObject`], or returns `undefined` otherwise. + /// + /// [spec]: https://tc39.es/ecma262/#sec-weak-ref.prototype.deref + pub(crate) fn deref(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + // 1. Let weakRef be the this value. + // 2. Perform ? RequireInternalSlot(weakRef, [[WeakRefTarget]]). + let weak_ref = this.as_object().map(JsObject::borrow).ok_or_else(|| { + JsNativeError::typ().with_message(format!( + "WeakRef.prototype.deref: expected `this` to be an `object`, got value of type `{}`", + this.type_of() + )) + })?; + + let weak_ref = weak_ref.as_weak_ref().ok_or_else(|| { + JsNativeError::typ() + .with_message("WeakRef.prototype.deref: expected `this` to be a `WeakRef` object") + })?; + + // 3. Return WeakRefDeref(weakRef). + + // `WeakRefDeref` + // https://tc39.es/ecma262/multipage/managing-memory.html#sec-weakrefderef + // 1. Let target be weakRef.[[WeakRefTarget]]. + // 2. If target is not empty, then + if let Some(object) = weak_ref.upgrade() { + let object = JsObject::from(object); + + // a. Perform AddToKeptObjects(target). + context.kept_alive.push(object.clone()); + + // b. Return target. + Ok(object.into()) + } else { + // 3. Return undefined. + Ok(JsValue::undefined()) + } + } +} + +#[cfg(test)] +mod tests { + use crate::{Context, JsValue}; + + #[test] + fn weak_ref_collected() { + let context = &mut Context::default(); + + assert!(context + .eval( + r#" + var ptr; + { + let obj = {a: 5, b: 6}; + ptr = new WeakRef(obj); + } + ptr.deref() + "# + ) + .unwrap() + .is_object()); + + boa_gc::force_collect(); + + assert_eq!( + context + .eval( + r#" + ptr.deref() + "# + ) + .unwrap(), + JsValue::undefined() + ) + } +} diff --git a/boa_engine/src/context/intrinsics.rs b/boa_engine/src/context/intrinsics.rs index e8d7dac720e..0da0e55581a 100644 --- a/boa_engine/src/context/intrinsics.rs +++ b/boa_engine/src/context/intrinsics.rs @@ -116,6 +116,7 @@ pub struct StandardConstructors { data_view: StandardConstructor, date_time_format: StandardConstructor, promise: StandardConstructor, + weak_ref: StandardConstructor, } impl Default for StandardConstructors { @@ -175,6 +176,7 @@ impl Default for StandardConstructors { data_view: StandardConstructor::default(), date_time_format: StandardConstructor::default(), promise: StandardConstructor::default(), + weak_ref: StandardConstructor::default(), }; // The value of `Array.prototype` is the Array prototype object. @@ -402,6 +404,11 @@ impl StandardConstructors { pub fn promise(&self) -> &StandardConstructor { &self.promise } + + #[inline] + pub fn weak_ref(&self) -> &StandardConstructor { + &self.weak_ref + } } /// Cached intrinsic objects diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index cc9691d4f58..0fc3ea736e4 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -104,6 +104,8 @@ pub struct Context { pub(crate) vm: Vm, pub(crate) promise_job_queue: VecDeque, + + pub(crate) kept_alive: Vec, } impl Default for Context { @@ -538,6 +540,7 @@ impl Context { self.realm.set_global_binding_number(); let result = self.run(); self.vm.pop_frame(); + self.clear_kept_objects(); self.run_queued_jobs()?; let (result, _) = result?; Ok(result) @@ -547,6 +550,7 @@ impl Context { fn run_queued_jobs(&mut self) -> JsResult<()> { while let Some(job) = self.promise_job_queue.pop_front() { job.call_job_callback(&JsValue::Undefined, &[], self)?; + self.clear_kept_objects(); } Ok(()) } @@ -580,6 +584,18 @@ impl Context { // TODO self.promise_job_queue.push_back(job); } + + /// Abstract operation [`ClearKeptObjects`][clear]. + /// + /// Clears all objects maintained alive by calls to the [`AddToKeptObjects`][add] abstract + /// operation, used within the [`WeakRef`][weak] constructor. + /// + /// [clear]: https://tc39.es/ecma262/multipage/executable-code-and-execution-contexts.html#sec-clear-kept-objects + /// [add]: https://tc39.es/ecma262/multipage/executable-code-and-execution-contexts.html#sec-addtokeptobjects + /// [weak]: https://tc39.es/ecma262/multipage/managing-memory.html#sec-weak-ref-objects + pub fn clear_kept_objects(&mut self) { + self.kept_alive.clear(); + } } /// Builder for the [`Context`] type. /// @@ -661,6 +677,7 @@ impl ContextBuilder { #[cfg(feature = "fuzz")] instructions_remaining: self.instructions_remaining, promise_job_queue: VecDeque::new(), + kept_alive: Vec::new(), }; // Add new builtIns to Context Realm diff --git a/boa_engine/src/object/jsobject.rs b/boa_engine/src/object/jsobject.rs index 6bcf077f9bd..a67a7490ee1 100644 --- a/boa_engine/src/object/jsobject.rs +++ b/boa_engine/src/object/jsobject.rs @@ -736,6 +736,10 @@ Cannot both specify accessors and a value or writable attribute", } ) } + + pub(crate) fn inner(&self) -> &Gc> { + &self.inner + } } impl AsRef> for JsObject { @@ -745,6 +749,13 @@ impl AsRef> for JsObject { } } +impl From>> for JsObject { + #[inline] + fn from(inner: Gc>) -> Self { + JsObject { inner } + } +} + impl PartialEq for JsObject { fn eq(&self, other: &Self) -> bool { Self::equals(self, other) diff --git a/boa_engine/src/object/mod.rs b/boa_engine/src/object/mod.rs index 8cbc7fe7a93..64a06e4d0a1 100644 --- a/boa_engine/src/object/mod.rs +++ b/boa_engine/src/object/mod.rs @@ -56,7 +56,7 @@ use crate::{ Context, JsBigInt, JsResult, JsString, JsSymbol, JsValue, }; -use boa_gc::{custom_trace, Finalize, Trace}; +use boa_gc::{custom_trace, Finalize, GcCell, Trace, WeakGc}; use boa_interner::Sym; use rustc_hash::FxHashMap; use std::{ @@ -193,6 +193,7 @@ pub enum ObjectKind { NativeObject(Box), IntegerIndexed(IntegerIndexed), Promise(Promise), + WeakRef(WeakGc>), #[cfg(feature = "intl")] DateTimeFormat(Box), } @@ -222,6 +223,7 @@ unsafe impl Trace for ObjectKind { Self::DateTimeFormat(f) => mark(f), Self::Promise(p) => mark(p), Self::AsyncGenerator(g) => mark(g), + Self::WeakRef(wr) => mark(wr), Self::RegExp(_) | Self::BigInt(_) | Self::Boolean(_) @@ -512,6 +514,14 @@ impl ObjectData { } } + /// Creates the `WeakRef` object data + pub fn weak_ref(weak_ref: WeakGc>) -> Self { + Self { + kind: ObjectKind::WeakRef(weak_ref), + internal_methods: &ORDINARY_INTERNAL_METHODS, + } + } + /// Create the `NativeObject` object data pub fn native_object(native_object: Box) -> Self { Self { @@ -576,6 +586,7 @@ impl Display for ObjectKind { #[cfg(feature = "intl")] Self::DateTimeFormat(_) => "DateTimeFormat", Self::Promise(_) => "Promise", + Self::WeakRef(_) => "WeakRef", }) } } @@ -1424,6 +1435,18 @@ impl Object { } } + /// Gets the `WeakRef`data if the object is a `WeakRef`. + #[inline] + pub fn as_weak_ref(&self) -> Option<&WeakGc>> { + match self.data { + ObjectData { + kind: ObjectKind::WeakRef(ref weak_ref), + .. + } => Some(weak_ref), + _ => None, + } + } + /// Return `true` if it is a native object and the native type is `T`. #[inline] pub fn is(&self) -> bool diff --git a/boa_engine/src/profiler.rs b/boa_engine/src/profiler.rs deleted file mode 100644 index 8b137891791..00000000000 --- a/boa_engine/src/profiler.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/boa_engine/src/tests.rs b/boa_engine/src/tests.rs index e5febd0d3f6..7e97b8e52a1 100644 --- a/boa_engine/src/tests.rs +++ b/boa_engine/src/tests.rs @@ -2720,7 +2720,7 @@ fn instanceofoperator_rhs_not_object() { assert_eq!( &exec(scenario), - "\"TypeError: right-hand side of 'instanceof' should be an object, got number\"" + "\"TypeError: right-hand side of 'instanceof' should be an object, got `number`\"" ); } diff --git a/boa_engine/src/value/mod.rs b/boa_engine/src/value/mod.rs index 4b4b46713b7..473a1029d5f 100644 --- a/boa_engine/src/value/mod.rs +++ b/boa_engine/src/value/mod.rs @@ -949,7 +949,7 @@ impl JsValue { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-typeof-operator - pub fn type_of(&self) -> JsString { + pub fn type_of(&self) -> &'static str { match *self { Self::Rational(_) | Self::Integer(_) => "number", Self::String(_) => "string", @@ -966,7 +966,6 @@ impl JsValue { } } } - .into() } /// Abstract operation `IsArray ( argument )` diff --git a/boa_engine/src/value/operations.rs b/boa_engine/src/value/operations.rs index f22478f8627..45dcaaf22d9 100644 --- a/boa_engine/src/value/operations.rs +++ b/boa_engine/src/value/operations.rs @@ -421,8 +421,8 @@ impl JsValue { if !target.is_object() { return Err(JsNativeError::typ() .with_message(format!( - "right-hand side of 'instanceof' should be an object, got {}", - target.type_of().to_std_string_escaped() + "right-hand side of 'instanceof' should be an object, got `{}`", + target.type_of() )) .into()); } diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index 02f52e097f2..b79ddf3b4bf 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -434,7 +434,7 @@ impl ToInternedString for CodeBlock { for (i, value) in self.literals.iter().enumerate() { f.push_str(&format!( " {i:04}: <{}> {}\n", - value.type_of().to_std_string_escaped(), + value.type_of(), value.display() )); } diff --git a/boa_engine/src/vm/opcode/binary_ops/mod.rs b/boa_engine/src/vm/opcode/binary_ops/mod.rs index ef2854ef65a..31d1a04b682 100644 --- a/boa_engine/src/vm/opcode/binary_ops/mod.rs +++ b/boa_engine/src/vm/opcode/binary_ops/mod.rs @@ -86,8 +86,8 @@ impl Operation for In { if !rhs.is_object() { return Err(JsNativeError::typ() .with_message(format!( - "right-hand side of 'in' should be an object, got {}", - rhs.type_of().to_std_string_escaped() + "right-hand side of 'in' should be an object, got `{}`", + rhs.type_of() )) .into()); } diff --git a/test_ignore.txt b/test_ignore.txt index 523fbd39d23..4c1e07f946e 100644 --- a/test_ignore.txt +++ b/test_ignore.txt @@ -9,6 +9,7 @@ feature:Temporal feature:tail-call-optimization feature:ShadowRealm feature:FinalizationRegistry +feature:FinalizationRegistry.prototype.cleanupSome feature:Atomics feature:dynamic_import feature:decorators @@ -29,6 +30,11 @@ feature:Intl.Locale // Non-standard feature:caller +// Stage 3 proposals + +// https://github.com/tc39/proposal-symbols-as-weakmap-keys +feature:symbols-as-weakmap-keys + // These generate a stack overflow tco-call tco-member From ace098fcc5ea145350f365870db12072e2e79512 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Nov 2022 22:25:34 +0000 Subject: [PATCH 11/24] Bump clap from 4.0.24 to 4.0.25 (#2441) Bumps [clap](https://github.com/clap-rs/clap) from 4.0.24 to 4.0.25.
Release notes

Sourced from clap's releases.

v4.0.25

[4.0.25] - 2022-11-15

Features

  • (error) Report available subcommands when required subcommand is missing
Changelog

Sourced from clap's changelog.

[4.0.25] - 2022-11-15

Features

  • (error) Report available subcommands when required subcommand is missing
Commits
  • ba32ab8 chore: Release
  • 391cd00 docs: Update changelog
  • 8cefdf3 Merge pull request #4482 from epage/suggest
  • 6b62c82 feat(parser): Show available subcommands when one is missing
  • d21ee51 test(parser): Verify subcommand required message
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=clap&package-manager=cargo&previous-version=4.0.24&new-version=4.0.25)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- Cargo.lock | 8 ++++---- boa_cli/Cargo.toml | 2 +- boa_tester/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc216db0d53..7a6d9cafda7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,7 +85,7 @@ dependencies = [ "boa_engine", "boa_interner", "boa_parser", - "clap 4.0.24", + "clap 4.0.25", "colored", "jemallocator", "phf", @@ -216,7 +216,7 @@ dependencies = [ "boa_gc", "boa_interner", "boa_parser", - "clap 4.0.24", + "clap 4.0.25", "colored", "fxhash", "once_cell", @@ -329,9 +329,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.0.24" +version = "4.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60494cedb60cb47462c0ff7be53de32c0e42a6fc2c772184554fa12bd9489c03" +checksum = "389ca505fd2c00136e0d0cd34bcd8b6bd0b59d5779aab396054b716334230c1c" dependencies = [ "atty", "bitflags", diff --git a/boa_cli/Cargo.toml b/boa_cli/Cargo.toml index 659ff2b91b4..d77fbfae731 100644 --- a/boa_cli/Cargo.toml +++ b/boa_cli/Cargo.toml @@ -18,7 +18,7 @@ boa_interner.workspace = true boa_parser.workspace = true rustyline = "10.0.0" rustyline-derive = "0.7.0" -clap = { version = "4.0.24", features = ["derive"] } +clap = { version = "4.0.25", features = ["derive"] } serde_json = "1.0.87" colored = "2.0.0" regex = "1.7.0" diff --git a/boa_tester/Cargo.toml b/boa_tester/Cargo.toml index 1119ac2ed35..3871a5817ce 100644 --- a/boa_tester/Cargo.toml +++ b/boa_tester/Cargo.toml @@ -16,7 +16,7 @@ boa_engine = { workspace = true, features = ["intl"] } boa_interner.workspace = true boa_gc.workspace = true boa_parser.workspace = true -clap = { version = "4.0.24", features = ["derive"] } +clap = { version = "4.0.25", features = ["derive"] } serde = { version = "1.0.147", features = ["derive"] } serde_yaml = "0.9.14" serde_json = "1.0.87" From 9c7b4d55316bfb8f118d413c33467e29eb38268d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Juli=C3=A1n=20Espina?= Date: Thu, 17 Nov 2022 00:51:25 +0000 Subject: [PATCH 12/24] Cleanup `boa_tester` (#2440) Just some quality changes to improve the maintainability of the tester: - Replaces `anyhow` with `color_eyre` to have a better output on errors/panics. - Changes the ignore file to a TOML file and replaces all parsing logic with the `toml` crate. - Adds a `ignored` field on all `Test`s to simplify run logic. - Replaces the global `IGNORED` with an `ignored` argument on the CLI. --- Cargo.lock | 199 +++++++++++++++++++++++- boa_tester/Cargo.toml | 3 +- boa_tester/src/exec/mod.rs | 307 +++++++++++++++++-------------------- boa_tester/src/main.rs | 223 ++++++++++++++------------- boa_tester/src/read.rs | 92 +++++------ boa_tester/src/results.rs | 13 +- test_ignore.toml | 45 ++++++ test_ignore.txt | 68 -------- 8 files changed, 550 insertions(+), 400 deletions(-) create mode 100644 test_ignore.toml delete mode 100644 test_ignore.txt diff --git a/Cargo.lock b/Cargo.lock index 7a6d9cafda7..ecff1290e82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" version = "0.7.19" @@ -26,12 +41,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" -[[package]] -name = "anyhow" -version = "1.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" - [[package]] name = "arbitrary" version = "1.2.0" @@ -58,6 +67,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -210,13 +234,13 @@ dependencies = [ name = "boa_tester" version = "0.16.0" dependencies = [ - "anyhow", "bitflags", "boa_engine", "boa_gc", "boa_interner", "boa_parser", "clap 4.0.25", + "color-eyre", "colored", "fxhash", "once_cell", @@ -225,6 +249,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml", + "toml", ] [[package]] @@ -394,6 +419,33 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "color-eyre" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + [[package]] name = "colored" version = "2.0.0" @@ -626,6 +678,16 @@ dependencies = [ "str-buf", ] +[[package]] +name = "eyre" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "fast-float" version = "0.2.0" @@ -692,6 +754,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" + [[package]] name = "half" version = "1.8.2" @@ -872,6 +940,12 @@ dependencies = [ "icu_provider_blob", ] +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "indexmap" version = "1.9.1" @@ -1036,6 +1110,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miniz_oxide" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +dependencies = [ + "adler", +] + [[package]] name = "nibble_vec" version = "0.1.0" @@ -1098,6 +1181,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.16.0" @@ -1116,6 +1208,12 @@ version = "6.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + [[package]] name = "parking_lot" version = "0.11.2" @@ -1192,6 +1290,12 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + [[package]] name = "plotters" version = "0.3.4" @@ -1394,6 +1498,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -1525,6 +1635,15 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + [[package]] name = "siphasher" version = "0.3.10" @@ -1647,6 +1766,15 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + [[package]] name = "time" version = "0.1.44" @@ -1695,6 +1823,57 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + [[package]] name = "unicode-general-category" version = "0.6.0" @@ -1746,6 +1925,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "version_check" version = "0.9.4" diff --git a/boa_tester/Cargo.toml b/boa_tester/Cargo.toml index 3871a5817ce..9df9f58cd36 100644 --- a/boa_tester/Cargo.toml +++ b/boa_tester/Cargo.toml @@ -26,4 +26,5 @@ once_cell = "1.16.0" colored = "2.0.0" fxhash = "0.2.1" rayon = "1.5.3" -anyhow = "1.0.66" +toml = "0.5.9" +color-eyre = "0.6.2" diff --git a/boa_tester/src/exec/mod.rs b/boa_tester/src/exec/mod.rs index dc948894646..254bebc0fa1 100644 --- a/boa_tester/src/exec/mod.rs +++ b/boa_tester/src/exec/mod.rs @@ -2,11 +2,12 @@ mod js262; +use std::borrow::Cow; + use crate::read::ErrorType; use super::{ - Harness, Outcome, Phase, SuiteResult, Test, TestFlags, TestOutcomeResult, TestResult, - TestSuite, IGNORED, + Harness, Outcome, Phase, SuiteResult, Test, TestFlags, TestOutcomeResult, TestResult, TestSuite, }; use boa_engine::{ builtins::JsArgs, object::FunctionBuilder, property::Attribute, Context, JsNativeErrorKind, @@ -130,6 +131,24 @@ impl Test { /// Runs the test once, in strict or non-strict mode fn run_once(&self, harness: &Harness, strict: bool, verbose: u8) -> TestResult { + if self.ignored { + if verbose > 1 { + println!( + "`{}`{}: {}", + self.name, + if strict { " (strict mode)" } else { "" }, + "Ignored".yellow() + ); + } else { + print!("{}", "-".yellow()); + } + return TestResult { + name: self.name.clone(), + strict, + result: TestOutcomeResult::Ignored, + result_text: Box::default(), + }; + } if verbose > 1 { println!( "`{}`{}: starting", @@ -139,183 +158,143 @@ impl Test { } let test_content = if strict { - format!("\"use strict\";\n{}", self.content) + Cow::Owned(format!("\"use strict\";\n{}", self.content)) } else { - self.content.to_string() + Cow::Borrowed(&*self.content) }; - let (result, result_text) = if !IGNORED.contains_any_flag(self.flags) - && !IGNORED.contains_test(&self.name) - && !IGNORED.contains_any_feature(&self.features) - && (matches!(self.expected_outcome, Outcome::Positive) - || matches!( - self.expected_outcome, - Outcome::Negative { - phase: Phase::Parse, - error_type: _, - } - ) - || matches!( - self.expected_outcome, - Outcome::Negative { - phase: Phase::Early, - error_type: _, - } - ) - || matches!( - self.expected_outcome, - Outcome::Negative { - phase: Phase::Runtime, - error_type: _, - } - )) { - let res = std::panic::catch_unwind(|| match self.expected_outcome { - Outcome::Positive => { - let mut context = Context::default(); - let async_result = AsyncResult::default(); - - if let Err(e) = self.set_up_env(harness, &mut context, async_result.clone()) { - return (false, e); - } + let result = std::panic::catch_unwind(|| match self.expected_outcome { + Outcome::Positive => { + let mut context = Context::default(); + let async_result = AsyncResult::default(); - // TODO: timeout - let value = match context.eval(&test_content) { - Ok(v) => v, - Err(e) => return (false, format!("Uncaught {e}")), - }; + if let Err(e) = self.set_up_env(harness, &mut context, async_result.clone()) { + return (false, e); + } - if let Err(e) = async_result.inner.borrow().as_ref() { - return (false, format!("Uncaught {e}")); - } + // TODO: timeout + let value = match context.eval(&*test_content) { + Ok(v) => v, + Err(e) => return (false, format!("Uncaught {e}")), + }; - (true, value.display().to_string()) + if let Err(e) = async_result.inner.borrow().as_ref() { + return (false, format!("Uncaught {e}")); } - Outcome::Negative { - phase: Phase::Parse | Phase::Early, + + (true, value.display().to_string()) + } + Outcome::Negative { + phase: Phase::Parse | Phase::Early, + error_type, + } => { + assert_eq!( error_type, - } => { - assert_eq!( - error_type, - ErrorType::SyntaxError, - "non-SyntaxError parsing/early error found in {}", - self.name - ); - - let mut context = Context::default(); - match context.parse(&test_content) { - Ok(statement_list) => match context.compile(&statement_list) { - Ok(_) => (false, "StatementList compilation should fail".to_owned()), - Err(e) => (true, format!("Uncaught {e:?}")), - }, - Err(e) => (true, format!("Uncaught {e}")), - } + ErrorType::SyntaxError, + "non-SyntaxError parsing/early error found in {}", + self.name + ); + + let mut context = Context::default(); + match context.parse(&*test_content) { + Ok(statement_list) => match context.compile(&statement_list) { + Ok(_) => (false, "StatementList compilation should fail".to_owned()), + Err(e) => (true, format!("Uncaught {e:?}")), + }, + Err(e) => (true, format!("Uncaught {e}")), } - Outcome::Negative { - phase: Phase::Resolution, - error_type: _, - } => todo!("check module resolution errors"), - Outcome::Negative { - phase: Phase::Runtime, - error_type, - } => { - let mut context = Context::default(); - if let Err(e) = self.set_up_env(harness, &mut context, AsyncResult::default()) { - return (false, e); - } - let code = match Parser::new(test_content.as_bytes()) - .parse_all(context.interner_mut()) - .map_err(Into::into) - .and_then(|stmts| context.compile(&stmts)) - { - Ok(code) => code, - Err(e) => return (false, format!("Uncaught {e}")), - }; - - // TODO: timeout - let e = match context.execute(code) { - Ok(res) => return (false, res.display().to_string()), - Err(e) => e, - }; - if let Ok(e) = e.try_native(&mut context) { - match &e.kind { - JsNativeErrorKind::Syntax if error_type == ErrorType::SyntaxError => {} - JsNativeErrorKind::Reference - if error_type == ErrorType::ReferenceError => {} - JsNativeErrorKind::Range if error_type == ErrorType::RangeError => {} - JsNativeErrorKind::Type if error_type == ErrorType::TypeError => {} - _ => return (false, format!("Uncaught {e}")), + } + Outcome::Negative { + phase: Phase::Resolution, + error_type: _, + } => todo!("check module resolution errors"), + Outcome::Negative { + phase: Phase::Runtime, + error_type, + } => { + let mut context = Context::default(); + if let Err(e) = self.set_up_env(harness, &mut context, AsyncResult::default()) { + return (false, e); + } + let code = match Parser::new(test_content.as_bytes()) + .parse_all(context.interner_mut()) + .map_err(Into::into) + .and_then(|stmts| context.compile(&stmts)) + { + Ok(code) => code, + Err(e) => return (false, format!("Uncaught {e}")), + }; + + // TODO: timeout + let e = match context.execute(code) { + Ok(res) => return (false, res.display().to_string()), + Err(e) => e, + }; + if let Ok(e) = e.try_native(&mut context) { + match &e.kind { + JsNativeErrorKind::Syntax if error_type == ErrorType::SyntaxError => {} + JsNativeErrorKind::Reference if error_type == ErrorType::ReferenceError => { } - (true, format!("Uncaught {e}")) - } else { - let passed = e - .as_opaque() - .expect("try_native cannot fail if e is not opaque") - .as_object() - .and_then(|o| o.get("constructor", &mut context).ok()) - .as_ref() - .and_then(JsValue::as_object) - .and_then(|o| o.get("name", &mut context).ok()) - .as_ref() - .and_then(JsValue::as_string) - .map(|s| s == error_type.as_str()) - .unwrap_or_default(); - (passed, format!("Uncaught {e}")) + JsNativeErrorKind::Range if error_type == ErrorType::RangeError => {} + JsNativeErrorKind::Type if error_type == ErrorType::TypeError => {} + _ => return (false, format!("Uncaught {e}")), } + (true, format!("Uncaught {e}")) + } else { + let passed = e + .as_opaque() + .expect("try_native cannot fail if e is not opaque") + .as_object() + .and_then(|o| o.get("constructor", &mut context).ok()) + .as_ref() + .and_then(JsValue::as_object) + .and_then(|o| o.get("name", &mut context).ok()) + .as_ref() + .and_then(JsValue::as_string) + .map(|s| s == error_type.as_str()) + .unwrap_or_default(); + (passed, format!("Uncaught {e}")) } - }); - - let result = res.map_or_else( - |_| { - eprintln!("last panic was on test \"{}\"", self.name); - (TestOutcomeResult::Panic, String::new()) - }, - |(res, text)| { - if res { - (TestOutcomeResult::Passed, text) - } else { - (TestOutcomeResult::Failed, text) - } - }, - ); - - if verbose > 1 { - println!( - "`{}`{}: {}", - self.name, - if strict { " (strict mode)" } else { "" }, - if matches!(result, (TestOutcomeResult::Passed, _)) { - "Passed".green() - } else if matches!(result, (TestOutcomeResult::Failed, _)) { - "Failed".red() - } else { - "⚠ Panic ⚠".red() - } - ); - } else { - print!( - "{}", - if matches!(result, (TestOutcomeResult::Passed, _)) { - ".".green() - } else { - "F".red() - } - ); } + }); + + let (result, result_text) = result.map_or_else( + |_| { + eprintln!("last panic was on test \"{}\"", self.name); + (TestOutcomeResult::Panic, String::new()) + }, + |(res, text)| { + if res { + (TestOutcomeResult::Passed, text) + } else { + (TestOutcomeResult::Failed, text) + } + }, + ); - result + if verbose > 1 { + println!( + "`{}`{}: {}", + self.name, + if strict { " (strict mode)" } else { "" }, + if result == TestOutcomeResult::Passed { + "Passed".green() + } else if result == TestOutcomeResult::Failed { + "Failed".red() + } else { + "⚠ Panic ⚠".red() + } + ); } else { - if verbose > 1 { - println!( - "`{}`{}: {}", - self.name, - if strict { " (strict mode)" } else { "" }, - "Ignored".yellow() - ); - } else { - print!("{}", "-".yellow()); - } - (TestOutcomeResult::Ignored, String::new()) - }; + print!( + "{}", + if result == TestOutcomeResult::Passed { + ".".green() + } else { + "F".red() + } + ); + } if verbose > 2 { println!( diff --git a/boa_tester/src/main.rs b/boa_tester/src/main.rs index 54ef04d9a42..41b7b0f5846 100644 --- a/boa_tester/src/main.rs +++ b/boa_tester/src/main.rs @@ -1,7 +1,7 @@ //! Test262 test runner //! //! This crate will run the full ECMAScript test suite (Test262) and report compliance of the -//! `boa` context. +//! `boa` wrap_err. #![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" @@ -70,26 +70,34 @@ use self::{ read::{read_harness, read_suite, read_test, MetaData, Negative, TestFlag}, results::{compare_results, write_json}, }; -use anyhow::{bail, Context}; use bitflags::bitflags; use clap::{ArgAction, Parser, ValueHint}; +use color_eyre::{ + eyre::{bail, WrapErr}, + Result, +}; use colored::Colorize; use fxhash::{FxHashMap, FxHashSet}; -use once_cell::sync::Lazy; use read::ErrorType; -use serde::{Deserialize, Serialize}; +use serde::{ + de::{Unexpected, Visitor}, + Deserialize, Deserializer, Serialize, +}; use std::{ - fs, + fs::{self, File}, + io::Read, path::{Path, PathBuf}, }; /// Structure to allow defining ignored tests, features and files that should /// be ignored even when reading. -#[derive(Debug)] +#[derive(Debug, Deserialize)] struct Ignored { + #[serde(default)] tests: FxHashSet>, + #[serde(default)] features: FxHashSet>, - files: FxHashSet>, + #[serde(default = "TestFlags::empty")] flags: TestFlags, } @@ -102,16 +110,17 @@ impl Ignored { /// Checks if the ignore list contains the given feature name in the list /// of features to ignore. - pub(crate) fn contains_any_feature(&self, features: &[Box]) -> bool { - features - .iter() - .any(|feature| self.features.contains(feature)) - } - - /// Checks if the ignore list contains the given file name in the list to - /// ignore from reading. - pub(crate) fn contains_file(&self, file: &str) -> bool { - self.files.contains(file) + pub(crate) fn contains_feature(&self, feature: &str) -> bool { + if self.features.contains(feature) { + return true; + } + // Some features are an accessor instead of a simple feature name e.g. `Intl.DurationFormat`. + // This ensures those are also ignored. + feature + .split('.') + .next() + .map(|feat| self.features.contains(feat)) + .unwrap_or_default() } pub(crate) fn contains_any_flag(&self, flags: TestFlags) -> bool { @@ -124,74 +133,11 @@ impl Default for Ignored { Self { tests: FxHashSet::default(), features: FxHashSet::default(), - files: FxHashSet::default(), flags: TestFlags::empty(), } } } -/// List of ignored tests. -static IGNORED: Lazy = Lazy::new(|| { - let path = Path::new("test_ignore.txt"); - if path.exists() { - let filtered = fs::read_to_string(path).expect("could not read test filters"); - filtered - .lines() - .filter(|line| !line.is_empty() && !line.starts_with("//")) - .fold(Ignored::default(), |mut ign, line| { - // let mut line = line.to_owned(); - if line.starts_with("file:") { - let file = line - .strip_prefix("file:") - .expect("prefix disappeared") - .trim() - .to_owned() - .into_boxed_str(); - let test = if file.ends_with(".js") { - file.strip_suffix(".js") - .expect("suffix disappeared") - .to_owned() - .into_boxed_str() - } else { - file.clone() - }; - ign.files.insert(file); - ign.tests.insert(test); - } else if line.starts_with("feature:") { - ign.features.insert( - line.strip_prefix("feature:") - .expect("prefix disappeared") - .trim() - .to_owned() - .into_boxed_str(), - ); - } else if line.starts_with("flag:") { - let flag = line - .strip_prefix("flag:") - .expect("prefix disappeared") - .trim() - .parse::() - .expect("invalid flag found"); - ign.flags.insert(flag.into()); - } else { - let mut test = line.trim(); - if test - .rsplit('.') - .next() - .map(|ext| ext.eq_ignore_ascii_case("js")) - == Some(true) - { - test = test.strip_suffix(".js").expect("suffix disappeared"); - } - ign.tests.insert(test.to_owned().into_boxed_str()); - } - ign - }) - } else { - Ignored::default() - } -}); - /// Boa test262 tester #[derive(Debug, Parser)] #[command(author, version, about, name = "Boa test262 tester")] @@ -217,6 +163,10 @@ enum Cli { /// Execute tests serially #[arg(short, long)] disable_parallelism: bool, + + /// Path to a TOML file with the ignored tests, features, flags and/or files. + #[arg(short, long, default_value = "test_ignore.toml", value_hint = ValueHint::FilePath)] + ignored: PathBuf, }, /// Compare two test suite results. Compare { @@ -235,7 +185,8 @@ enum Cli { } /// Program entry point. -fn main() { +fn main() -> Result<()> { + color_eyre::install()?; match Cli::parse() { Cli::Run { verbose, @@ -243,23 +194,15 @@ fn main() { suite, output, disable_parallelism, - } => { - if let Err(e) = run_test_suite( - verbose, - !disable_parallelism, - test262_path.as_path(), - suite.as_path(), - output.as_deref(), - ) { - eprintln!("Error: {e}"); - let mut src = e.source(); - while let Some(e) = src { - eprintln!(" caused by: {e}"); - src = e.source(); - } - std::process::exit(1); - } - } + ignored: ignore, + } => run_test_suite( + verbose, + !disable_parallelism, + test262_path.as_path(), + suite.as_path(), + output.as_deref(), + ignore.as_path(), + ), Cli::Compare { base, new, @@ -275,24 +218,33 @@ fn run_test_suite( test262_path: &Path, suite: &Path, output: Option<&Path>, -) -> anyhow::Result<()> { + ignored: &Path, +) -> Result<()> { if let Some(path) = output { if path.exists() { if !path.is_dir() { bail!("the output path must be a directory."); } } else { - fs::create_dir_all(path).context("could not create the output directory")?; + fs::create_dir_all(path).wrap_err("could not create the output directory")?; } } + let ignored = { + let mut input = String::new(); + let mut f = File::open(ignored).wrap_err("could not open ignored tests file")?; + f.read_to_string(&mut input) + .wrap_err("could not read ignored tests file")?; + toml::from_str(&input).wrap_err("could not decode ignored tests file")? + }; + if verbose != 0 { println!("Loading the test suite..."); } - let harness = read_harness(test262_path).context("could not read harness")?; + let harness = read_harness(test262_path).wrap_err("could not read harness")?; if suite.to_string_lossy().ends_with(".js") { - let test = read_test(&test262_path.join(suite)).with_context(|| { + let test = read_test(&test262_path.join(suite)).wrap_err_with(|| { let suite = suite.display(); format!("could not read the test {suite}") })?; @@ -304,7 +256,7 @@ fn run_test_suite( println!(); } else { - let suite = read_suite(&test262_path.join(suite)).with_context(|| { + let suite = read_suite(&test262_path.join(suite), &ignored, false).wrap_err_with(|| { let suite = suite.display(); format!("could not read the suite {suite}") })?; @@ -332,7 +284,7 @@ fn run_test_suite( ); write_json(results, output, verbose) - .context("could not write the results to the output JSON file")?; + .wrap_err("could not write the results to the output JSON file")?; } Ok(()) @@ -419,6 +371,7 @@ struct Test { includes: Box<[Box]>, locale: Locale, content: Box, + ignored: bool, } impl Test { @@ -440,15 +393,12 @@ impl Test { includes: metadata.includes, locale: metadata.locale, content: content.into(), + ignored: false, } } - /// Sets the name of the test. - fn set_name(&mut self, name: N) - where - N: Into>, - { - self.name = name.into(); + fn set_ignored(&mut self) { + self.ignored = true; } } @@ -534,6 +484,59 @@ where } } +impl<'de> Deserialize<'de> for TestFlags { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct FlagsVisitor; + + impl<'de> Visitor<'de> for FlagsVisitor { + type Value = TestFlags; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "a sequence of flags") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut flags = TestFlags::empty(); + while let Some(elem) = seq.next_element::()? { + flags |= elem.into(); + } + Ok(flags) + } + } + + struct RawFlagsVisitor; + + impl<'de> Visitor<'de> for RawFlagsVisitor { + type Value = TestFlags; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "a flags number") + } + + fn visit_u16(self, v: u16) -> Result + where + E: serde::de::Error, + { + TestFlags::from_bits(v).ok_or_else(|| { + E::invalid_value(Unexpected::Unsigned(v.into()), &"a valid flag number") + }) + } + } + + if deserializer.is_human_readable() { + deserializer.deserialize_seq(FlagsVisitor) + } else { + deserializer.deserialize_u16(RawFlagsVisitor) + } + } +} + /// Phase for an error. #[derive(Debug, Clone, Copy, Deserialize)] #[serde(rename_all = "lowercase")] diff --git a/boa_tester/src/read.rs b/boa_tester/src/read.rs index e198b64220a..29dc714d63f 100644 --- a/boa_tester/src/read.rs +++ b/boa_tester/src/read.rs @@ -1,10 +1,15 @@ //! Module to read the list of test suites from disk. -use super::{Harness, Locale, Phase, Test, TestSuite, IGNORED}; -use anyhow::Context; +use crate::Ignored; + +use super::{Harness, Locale, Phase, Test, TestSuite}; +use color_eyre::{ + eyre::{eyre, WrapErr}, + Result, +}; use fxhash::FxHashMap; use serde::Deserialize; -use std::{fs, io, path::Path, str::FromStr}; +use std::{fs, io, path::Path}; /// Representation of the YAML metadata in Test262 tests. #[derive(Debug, Clone, Deserialize)] @@ -77,31 +82,12 @@ pub(super) enum TestFlag { NonDeterministic, } -impl FromStr for TestFlag { - type Err = String; // TODO: improve error type. - - fn from_str(s: &str) -> Result { - match s { - "onlyStrict" => Ok(Self::OnlyStrict), - "noStrict" => Ok(Self::NoStrict), - "module" => Ok(Self::Module), - "raw" => Ok(Self::Raw), - "async" => Ok(Self::Async), - "generated" => Ok(Self::Generated), - "CanBlockIsFalse" => Ok(Self::CanBlockIsFalse), - "CanBlockIsTrue" => Ok(Self::CanBlockIsTrue), - "non-deterministic" => Ok(Self::NonDeterministic), - _ => Err(format!("unknown test flag: {s}")), - } - } -} - /// Reads the Test262 defined bindings. -pub(super) fn read_harness(test262_path: &Path) -> anyhow::Result { +pub(super) fn read_harness(test262_path: &Path) -> Result { let mut includes = FxHashMap::default(); - for entry in - fs::read_dir(test262_path.join("harness")).context("error reading the harness directory")? + for entry in fs::read_dir(test262_path.join("harness")) + .wrap_err("error reading the harness directory")? { let entry = entry?; let file_name = entry.file_name(); @@ -112,7 +98,7 @@ pub(super) fn read_harness(test262_path: &Path) -> anyhow::Result { } let content = fs::read_to_string(entry.path()) - .with_context(|| format!("error reading the harnes/{file_name}"))?; + .wrap_err_with(|| format!("error reading the harnes/{file_name}"))?; includes.insert( file_name.into_owned().into_boxed_str(), @@ -120,13 +106,13 @@ pub(super) fn read_harness(test262_path: &Path) -> anyhow::Result { ); } let assert = fs::read_to_string(test262_path.join("harness/assert.js")) - .context("error reading harnes/assert.js")? + .wrap_err("error reading harnes/assert.js")? .into_boxed_str(); let sta = fs::read_to_string(test262_path.join("harness/sta.js")) - .context("error reading harnes/sta.js")? + .wrap_err("error reading harnes/sta.js")? .into_boxed_str(); let doneprint_handle = fs::read_to_string(test262_path.join("harness/doneprintHandle.js")) - .context("error reading harnes/doneprintHandle.js")? + .wrap_err("error reading harnes/doneprintHandle.js")? .into_boxed_str(); Ok(Harness { @@ -138,38 +124,54 @@ pub(super) fn read_harness(test262_path: &Path) -> anyhow::Result { } /// Reads a test suite in the given path. -pub(super) fn read_suite(path: &Path) -> anyhow::Result { +pub(super) fn read_suite( + path: &Path, + ignored: &Ignored, + mut ignore_suite: bool, +) -> Result { let name = path .file_name() - .with_context(|| format!("test suite with no name found: {}", path.display()))? + .ok_or_else(|| eyre!(format!("test suite with no name found: {}", path.display())))? .to_str() - .with_context(|| format!("non-UTF-8 suite name found: {}", path.display()))?; + .ok_or_else(|| eyre!(format!("non-UTF-8 suite name found: {}", path.display())))?; + + ignore_suite |= ignored.contains_test(name); let mut suites = Vec::new(); let mut tests = Vec::new(); // TODO: iterate in parallel - for entry in path.read_dir().context("retrieving entry")? { + for entry in path.read_dir().wrap_err("retrieving entry")? { let entry = entry?; - if entry.file_type().context("retrieving file type")?.is_dir() { - suites.push(read_suite(entry.path().as_path()).with_context(|| { - let path = entry.path(); - let suite = path.display(); - format!("error reading sub-suite {suite}") - })?); + if entry.file_type().wrap_err("retrieving file type")?.is_dir() { + suites.push( + read_suite(entry.path().as_path(), ignored, ignore_suite).wrap_err_with(|| { + let path = entry.path(); + let suite = path.display(); + format!("error reading sub-suite {suite}") + })?, + ); } else if entry.file_name().to_string_lossy().contains("_FIXTURE") { continue; - } else if IGNORED.contains_file(&entry.file_name().to_string_lossy()) { - let mut test = Test::default(); - test.set_name(entry.file_name().to_string_lossy()); - tests.push(test); } else { - tests.push(read_test(entry.path().as_path()).with_context(|| { + let mut test = read_test(entry.path().as_path()).wrap_err_with(|| { let path = entry.path(); let suite = path.display(); format!("error reading test {suite}") - })?); + })?; + + if ignore_suite + || ignored.contains_any_flag(test.flags) + || ignored.contains_test(&test.name) + || test + .features + .iter() + .any(|feat| ignored.contains_feature(feat)) + { + test.set_ignored(); + } + tests.push(test); } } diff --git a/boa_tester/src/results.rs b/boa_tester/src/results.rs index c8063d28d11..f1edfa7ebc1 100644 --- a/boa_tester/src/results.rs +++ b/boa_tester/src/results.rs @@ -1,4 +1,5 @@ use super::SuiteResult; +use color_eyre::{eyre::WrapErr, Result}; use serde::{Deserialize, Serialize}; use std::{ env, fs, @@ -208,16 +209,16 @@ fn update_gh_pages_repo(path: &Path, verbose: u8) { } /// Compares the results of two test suite runs. -pub(crate) fn compare_results(base: &Path, new: &Path, markdown: bool) { +pub(crate) fn compare_results(base: &Path, new: &Path, markdown: bool) -> Result<()> { let base_results: ResultInfo = serde_json::from_reader(BufReader::new( - fs::File::open(base).expect("could not open the base results file"), + fs::File::open(base).wrap_err("could not open the base results file")?, )) - .expect("could not read the base results"); + .wrap_err("could not read the base results")?; let new_results: ResultInfo = serde_json::from_reader(BufReader::new( - fs::File::open(new).expect("could not open the new results file"), + fs::File::open(new).wrap_err("could not open the new results file")?, )) - .expect("could not read the new results"); + .wrap_err("could not read the new results")?; let base_total = base_results.results.total as isize; let new_total = new_results.results.total as isize; @@ -433,6 +434,8 @@ pub(crate) fn compare_results(base: &Path, new: &Path, markdown: bool) { } } } + + Ok(()) } /// Test differences. diff --git a/test_ignore.toml b/test_ignore.toml new file mode 100644 index 00000000000..d760c8772c4 --- /dev/null +++ b/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.NumberFormat-v3", + "Intl.NumberFormat-unified", + "Intl.ListFormat", + "Intl.DisplayNames", + "Intl.RelativeTimeFormat", + "Intl.Segmenter", + "Intl.Locale", + + # Stage 3 proposals + + # https://github.com/tc39/proposal-symbols-as-weakmap-keys + "symbols-as-weakmap-keys", + + # 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"] diff --git a/test_ignore.txt b/test_ignore.txt deleted file mode 100644 index 4c1e07f946e..00000000000 --- a/test_ignore.txt +++ /dev/null @@ -1,68 +0,0 @@ -// Not implemented yet: -flag:module - -// Non-implemented features: -feature:json-modules -feature:SharedArrayBuffer -feature:resizable-arraybuffer -feature:Temporal -feature:tail-call-optimization -feature:ShadowRealm -feature:FinalizationRegistry -feature:FinalizationRegistry.prototype.cleanupSome -feature:Atomics -feature:dynamic_import -feature:decorators -feature:array-grouping -feature:IsHTMLDDA - -// Non-implemented Intl features -feature:intl-normative-optional -feature:Intl.DurationFormat -feature:Intl.NumberFormat-v3 -feature:Intl.NumberFormat-unified -feature:Intl.ListFormat -feature:Intl.DisplayNames -feature:Intl.RelativeTimeFormat -feature:Intl.Segmenter -feature:Intl.Locale - -// Non-standard -feature:caller - -// Stage 3 proposals - -// https://github.com/tc39/proposal-symbols-as-weakmap-keys -feature:symbols-as-weakmap-keys - -// These generate a stack overflow -tco-call -tco-member - -// RegExp tests that check individual codepoints. -// They are not usefull in comparision to the cpu time they waste. -feature:regexp-unicode-property-escapes -character-class-non-whitespace-class-escape-plus-quantifier -character-class-non-whitespace-class-escape-plus-quantifier-flags-u -character-class-non-word-class-escape-flags-u -character-class-non-whitespace-class-escape -character-class-non-digit-class-escape-plus-quantifier-flags-u -character-class-non-word-class-escape -character-class-non-whitespace-class-escape-flags-u -character-class-digit-class-escape-flags-u -character-class-digit-class-escape -character-class-non-digit-class-escape-plus-quantifier -character-class-whitespace-class-escape-flags-u -character-class-whitespace-class-escape-plus-quantifier-flags-u -character-class-word-class-escape -character-class-digit-class-escape-plus-quantifier-flags-u -character-class-non-digit-class-escape -character-class-digit-class-escape-plus-quantifier -character-class-whitespace-class-escape -character-class-whitespace-class-escape-plus-quantifier -character-class-word-class-escape-plus-quantifier -character-class-non-word-class-escape-plus-quantifier -character-class-word-class-escape-flags-u -character-class-word-class-escape-plus-quantifier-flags-u -character-class-non-digit-class-escape-flags-u -character-class-non-word-class-escape-plus-quantifier-flags-u From 216545153777464df854b309d04498beb39dd91d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Nov 2022 07:11:45 +0000 Subject: [PATCH 13/24] Bump loader-utils from 2.0.3 to 2.0.4 (#2442) Bumps [loader-utils](https://github.com/webpack/loader-utils) from 2.0.3 to 2.0.4.
Release notes

Sourced from loader-utils's releases.

v2.0.4

2.0.4 (2022-11-11)

Bug Fixes

Changelog

Sourced from loader-utils's changelog.

2.0.4 (2022-11-11)

Bug Fixes

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=loader-utils&package-manager=npm_and_yarn&previous-version=2.0.3&new-version=2.0.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/boa-dev/boa/network/alerts).
--- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index a6a69cff3e2..d44c7cbadac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2579,9 +2579,9 @@ } }, "node_modules/loader-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz", - "integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==", + "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", @@ -6632,9 +6632,9 @@ "dev": true }, "loader-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz", - "integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==", + "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", From a6e5c2d4e035bd081447be1f85298d77500bb6f4 Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Thu, 17 Nov 2022 07:21:16 +0000 Subject: [PATCH 14/24] Restructure lint lists in `boa_ast` (#2433) This Pull Request restructures the lint deny/warn/allow lists in `boa_ast` and fixes some clippy lints in the crate. The most relevant change should be in `boa_ast/src/lib.rs`. I went trough all the lints that are available in rustc/rustdoc/clippy with the goal to have most in the deny list, either trough groups or individually. Some clippy lints remain allowed, because they trigger false positives (I fixed them as far as possible). I'm interested in how everyone feels about denying most lints, as it may impact individual development workflows. If we agree on how we want to structure this, I will apply the restructured lists to all other creates aswell. --- boa_ast/src/declaration/mod.rs | 36 +++--- boa_ast/src/declaration/variable.rs | 46 ++++---- boa_ast/src/expression/access.rs | 38 +++--- boa_ast/src/expression/await.rs | 2 +- boa_ast/src/expression/call.rs | 6 +- boa_ast/src/expression/identifier.rs | 4 +- boa_ast/src/expression/literal/array.rs | 2 +- boa_ast/src/expression/literal/mod.rs | 6 +- boa_ast/src/expression/literal/object.rs | 18 +-- boa_ast/src/expression/literal/template.rs | 10 +- boa_ast/src/expression/mod.rs | 110 +++++++++--------- boa_ast/src/expression/new.rs | 6 +- boa_ast/src/expression/operator/assign/mod.rs | 24 ++-- boa_ast/src/expression/operator/assign/op.rs | 2 +- boa_ast/src/expression/operator/binary/mod.rs | 6 +- boa_ast/src/expression/operator/binary/op.rs | 10 +- .../src/expression/operator/conditional.rs | 6 +- boa_ast/src/expression/operator/unary/mod.rs | 2 +- boa_ast/src/expression/operator/unary/op.rs | 2 +- boa_ast/src/expression/optional.rs | 24 ++-- boa_ast/src/expression/spread.rs | 2 +- boa_ast/src/expression/tagged_template.rs | 8 +- boa_ast/src/expression/yield.rs | 2 +- boa_ast/src/function/arrow_function.rs | 12 +- boa_ast/src/function/async_arrow_function.rs | 8 +- boa_ast/src/function/async_function.rs | 10 +- boa_ast/src/function/async_generator.rs | 10 +- boa_ast/src/function/class.rs | 62 +++++----- boa_ast/src/function/generator.rs | 10 +- boa_ast/src/function/mod.rs | 12 +- boa_ast/src/function/parameters.rs | 22 ++-- boa_ast/src/keyword.rs | 4 +- boa_ast/src/lib.rs | 85 ++++++++------ boa_ast/src/pattern.rs | 68 ++++++----- boa_ast/src/position.rs | 8 +- boa_ast/src/property.rs | 65 +++++------ boa_ast/src/statement/block.rs | 2 +- boa_ast/src/statement/if.rs | 4 +- boa_ast/src/statement/iteration/break.rs | 13 +-- boa_ast/src/statement/iteration/continue.rs | 13 +-- .../src/statement/iteration/do_while_loop.rs | 4 +- .../src/statement/iteration/for_in_loop.rs | 6 +- boa_ast/src/statement/iteration/for_loop.rs | 36 +++--- .../src/statement/iteration/for_of_loop.rs | 8 +- boa_ast/src/statement/iteration/mod.rs | 20 ++-- boa_ast/src/statement/iteration/while_loop.rs | 4 +- boa_ast/src/statement/labelled.rs | 16 +-- boa_ast/src/statement/mod.rs | 68 +++++------ boa_ast/src/statement/return.rs | 12 +- boa_ast/src/statement/switch.rs | 12 +- boa_ast/src/statement/throw.rs | 4 +- boa_ast/src/statement/try.rs | 16 +-- boa_ast/src/statement_list.rs | 24 ++-- 53 files changed, 502 insertions(+), 508 deletions(-) diff --git a/boa_ast/src/declaration/mod.rs b/boa_ast/src/declaration/mod.rs index 02f3e584c85..fa89cca1f58 100644 --- a/boa_ast/src/declaration/mod.rs +++ b/boa_ast/src/declaration/mod.rs @@ -52,12 +52,12 @@ pub enum Declaration { impl ToIndentedString for Declaration { fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { match self { - Declaration::Function(f) => f.to_indented_string(interner, indentation), - Declaration::Generator(g) => g.to_indented_string(interner, indentation), - Declaration::AsyncFunction(af) => af.to_indented_string(interner, indentation), - Declaration::AsyncGenerator(ag) => ag.to_indented_string(interner, indentation), - Declaration::Class(c) => c.to_indented_string(interner, indentation), - Declaration::Lexical(l) => { + 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 @@ -72,12 +72,12 @@ impl VisitWith for Declaration { V: Visitor<'a>, { match self { - Declaration::Function(f) => visitor.visit_function(f), - Declaration::Generator(g) => visitor.visit_generator(g), - Declaration::AsyncFunction(af) => visitor.visit_async_function(af), - Declaration::AsyncGenerator(ag) => visitor.visit_async_generator(ag), - Declaration::Class(c) => visitor.visit_class(c), - Declaration::Lexical(ld) => visitor.visit_lexical_declaration(ld), + 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), } } @@ -86,12 +86,12 @@ impl VisitWith for Declaration { V: VisitorMut<'a>, { match self { - Declaration::Function(f) => visitor.visit_function_mut(f), - Declaration::Generator(g) => visitor.visit_generator_mut(g), - Declaration::AsyncFunction(af) => visitor.visit_async_function_mut(af), - Declaration::AsyncGenerator(ag) => visitor.visit_async_generator_mut(ag), - Declaration::Class(c) => visitor.visit_class_mut(c), - Declaration::Lexical(ld) => visitor.visit_lexical_declaration_mut(ld), + 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/boa_ast/src/declaration/variable.rs b/boa_ast/src/declaration/variable.rs index 921f57ffe1e..e3012a7e270 100644 --- a/boa_ast/src/declaration/variable.rs +++ b/boa_ast/src/declaration/variable.rs @@ -51,7 +51,7 @@ pub struct VarDeclaration(pub VariableList); impl From for Statement { fn from(var: VarDeclaration) -> Self { - Statement::Var(var) + Self::Var(var) } } @@ -110,16 +110,16 @@ pub enum LexicalDeclaration { impl LexicalDeclaration { /// Gets the inner variable list of the `LexicalDeclaration` #[must_use] - pub fn variable_list(&self) -> &VariableList { + pub const fn variable_list(&self) -> &VariableList { match self { - LexicalDeclaration::Const(list) | LexicalDeclaration::Let(list) => list, + Self::Const(list) | Self::Let(list) => list, } } } impl From for Declaration { fn from(lex: LexicalDeclaration) -> Self { - Declaration::Lexical(lex) + Self::Lexical(lex) } } @@ -128,8 +128,8 @@ impl ToInternedString for LexicalDeclaration { format!( "{} {}", match &self { - LexicalDeclaration::Let(_) => "let", - LexicalDeclaration::Const(_) => "const", + Self::Let(_) => "let", + Self::Const(_) => "const", }, self.variable_list().to_interned_string(interner) ) @@ -142,9 +142,7 @@ impl VisitWith for LexicalDeclaration { V: Visitor<'a>, { match self { - LexicalDeclaration::Const(vars) | LexicalDeclaration::Let(vars) => { - visitor.visit_variable_list(vars) - } + Self::Const(vars) | Self::Let(vars) => visitor.visit_variable_list(vars), } } @@ -153,9 +151,7 @@ impl VisitWith for LexicalDeclaration { V: VisitorMut<'a>, { match self { - LexicalDeclaration::Const(vars) | LexicalDeclaration::Let(vars) => { - visitor.visit_variable_list_mut(vars) - } + Self::Const(vars) | Self::Let(vars) => visitor.visit_variable_list_mut(vars), } } } @@ -176,7 +172,7 @@ impl VariableList { return None; } - Some(VariableList { list }) + Some(Self { list }) } } @@ -228,7 +224,7 @@ impl TryFrom> for VariableList { type Error = TryFromVariableListError; fn try_from(value: Box<[Variable]>) -> Result { - VariableList::new(value).ok_or(TryFromVariableListError(())) + Self::new(value).ok_or(TryFromVariableListError(())) } } @@ -236,7 +232,7 @@ impl TryFrom> for VariableList { type Error = TryFromVariableListError; fn try_from(value: Vec) -> Result { - VariableList::try_from(value.into_boxed_slice()) + Self::try_from(value.into_boxed_slice()) } } @@ -275,7 +271,7 @@ impl Variable { /// Creates a new variable declaration from a `BindingIdentifier`. #[inline] #[must_use] - pub fn from_identifier(ident: Identifier, init: Option) -> Self { + pub const fn from_identifier(ident: Identifier, init: Option) -> Self { Self { binding: Binding::Identifier(ident), init, @@ -285,7 +281,7 @@ impl Variable { /// Creates a new variable declaration from a `Pattern`. #[inline] #[must_use] - pub fn from_pattern(pattern: Pattern, init: Option) -> Self { + pub const fn from_pattern(pattern: Pattern, init: Option) -> Self { Self { binding: Binding::Pattern(pattern), init, @@ -293,14 +289,14 @@ impl Variable { } /// Gets the variable declaration binding. #[must_use] - pub fn binding(&self) -> &Binding { + pub const fn binding(&self) -> &Binding { &self.binding } /// Gets the initialization expression for the variable declaration, if any. #[inline] #[must_use] - pub fn init(&self) -> Option<&Expression> { + pub const fn init(&self) -> Option<&Expression> { self.init.as_ref() } } @@ -360,8 +356,8 @@ impl From for Binding { impl ToInternedString for Binding { fn to_interned_string(&self, interner: &Interner) -> String { match self { - Binding::Identifier(id) => id.to_interned_string(interner), - Binding::Pattern(ref pattern) => pattern.to_interned_string(interner), + Self::Identifier(id) => id.to_interned_string(interner), + Self::Pattern(ref pattern) => pattern.to_interned_string(interner), } } } @@ -372,8 +368,8 @@ impl VisitWith for Binding { V: Visitor<'a>, { match self { - Binding::Identifier(id) => visitor.visit_identifier(id), - Binding::Pattern(pattern) => visitor.visit_pattern(pattern), + Self::Identifier(id) => visitor.visit_identifier(id), + Self::Pattern(pattern) => visitor.visit_pattern(pattern), } } @@ -382,8 +378,8 @@ impl VisitWith for Binding { V: VisitorMut<'a>, { match self { - Binding::Identifier(id) => visitor.visit_identifier_mut(id), - Binding::Pattern(pattern) => visitor.visit_pattern_mut(pattern), + Self::Identifier(id) => visitor.visit_identifier_mut(id), + Self::Pattern(pattern) => visitor.visit_pattern_mut(pattern), } } } diff --git a/boa_ast/src/expression/access.rs b/boa_ast/src/expression/access.rs index b66f2a11cff..9a8abe348a4 100644 --- a/boa_ast/src/expression/access.rs +++ b/boa_ast/src/expression/access.rs @@ -53,8 +53,8 @@ impl VisitWith for PropertyAccessField { V: Visitor<'a>, { match self { - PropertyAccessField::Const(sym) => visitor.visit_sym(sym), - PropertyAccessField::Expr(expr) => visitor.visit_expression(expr), + Self::Const(sym) => visitor.visit_sym(sym), + Self::Expr(expr) => visitor.visit_expression(expr), } } @@ -63,8 +63,8 @@ impl VisitWith for PropertyAccessField { V: VisitorMut<'a>, { match self { - PropertyAccessField::Const(sym) => visitor.visit_sym_mut(sym), - PropertyAccessField::Expr(expr) => visitor.visit_expression_mut(&mut *expr), + Self::Const(sym) => visitor.visit_sym_mut(sym), + Self::Expr(expr) => visitor.visit_expression_mut(&mut *expr), } } } @@ -88,9 +88,9 @@ impl ToInternedString for PropertyAccess { #[inline] fn to_interned_string(&self, interner: &Interner) -> String { match self { - PropertyAccess::Simple(s) => s.to_interned_string(interner), - PropertyAccess::Private(p) => p.to_interned_string(interner), - PropertyAccess::Super(s) => s.to_interned_string(interner), + Self::Simple(s) => s.to_interned_string(interner), + Self::Private(p) => p.to_interned_string(interner), + Self::Super(s) => s.to_interned_string(interner), } } } @@ -108,9 +108,9 @@ impl VisitWith for PropertyAccess { V: Visitor<'a>, { match self { - PropertyAccess::Simple(spa) => visitor.visit_simple_property_access(spa), - PropertyAccess::Private(ppa) => visitor.visit_private_property_access(ppa), - PropertyAccess::Super(supa) => visitor.visit_super_property_access(supa), + 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), } } @@ -119,9 +119,9 @@ impl VisitWith for PropertyAccess { V: VisitorMut<'a>, { match self { - PropertyAccess::Simple(spa) => visitor.visit_simple_property_access_mut(spa), - PropertyAccess::Private(ppa) => visitor.visit_private_property_access_mut(ppa), - PropertyAccess::Super(supa) => visitor.visit_super_property_access_mut(supa), + 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), } } } @@ -139,14 +139,14 @@ impl SimplePropertyAccess { /// Gets the target object of the property access. #[inline] #[must_use] - pub fn target(&self) -> &Expression { + pub const fn target(&self) -> &Expression { &self.target } /// Gets the accessed field of the target object. #[inline] #[must_use] - pub fn field(&self) -> &PropertyAccessField { + pub const fn field(&self) -> &PropertyAccessField { &self.field } @@ -233,14 +233,14 @@ impl PrivatePropertyAccess { /// Gets the original object from where to get the field from. #[inline] #[must_use] - pub fn target(&self) -> &Expression { + pub const fn target(&self) -> &Expression { &self.target } /// Gets the name of the field to retrieve. #[inline] #[must_use] - pub fn field(&self) -> Sym { + pub const fn field(&self) -> Sym { self.field } } @@ -298,14 +298,14 @@ pub struct SuperPropertyAccess { impl SuperPropertyAccess { /// Creates a new property access field node. #[must_use] - pub fn new(field: PropertyAccessField) -> Self { + pub const fn new(field: PropertyAccessField) -> Self { Self { field } } /// Gets the name of the field to retrieve. #[inline] #[must_use] - pub fn field(&self) -> &PropertyAccessField { + pub const fn field(&self) -> &PropertyAccessField { &self.field } } diff --git a/boa_ast/src/expression/await.rs b/boa_ast/src/expression/await.rs index 8c8786311ad..079bf831c31 100644 --- a/boa_ast/src/expression/await.rs +++ b/boa_ast/src/expression/await.rs @@ -26,7 +26,7 @@ impl Await { /// Return the target expression that should be awaited. #[inline] #[must_use] - pub fn target(&self) -> &Expression { + pub const fn target(&self) -> &Expression { &self.target } } diff --git a/boa_ast/src/expression/call.rs b/boa_ast/src/expression/call.rs index 3787ad99d84..5b978242615 100644 --- a/boa_ast/src/expression/call.rs +++ b/boa_ast/src/expression/call.rs @@ -42,14 +42,14 @@ impl Call { /// Gets the target function of this call expression. #[inline] #[must_use] - pub fn function(&self) -> &Expression { + pub const fn function(&self) -> &Expression { &self.function } /// Retrieves the arguments passed to the function. #[inline] #[must_use] - pub fn args(&self) -> &[Expression] { + pub const fn args(&self) -> &[Expression] { &self.args } } @@ -122,7 +122,7 @@ impl SuperCall { /// Retrieves the arguments of the super call. #[must_use] - pub fn arguments(&self) -> &[Expression] { + pub const fn arguments(&self) -> &[Expression] { &self.args } } diff --git a/boa_ast/src/expression/identifier.rs b/boa_ast/src/expression/identifier.rs index 0bf6844744f..0f7f73a05b7 100644 --- a/boa_ast/src/expression/identifier.rs +++ b/boa_ast/src/expression/identifier.rs @@ -68,14 +68,14 @@ impl Identifier { /// Creates a new identifier AST Expression. #[inline] #[must_use] - pub fn new(ident: Sym) -> Self { + pub const fn new(ident: Sym) -> Self { Self { ident } } /// Retrieves the identifier's string symbol in the interner. #[inline] #[must_use] - pub fn sym(self) -> Sym { + pub const fn sym(self) -> Sym { self.ident } } diff --git a/boa_ast/src/expression/literal/array.rs b/boa_ast/src/expression/literal/array.rs index 631d0c9fb3a..2f599d12cf8 100644 --- a/boa_ast/src/expression/literal/array.rs +++ b/boa_ast/src/expression/literal/array.rs @@ -47,7 +47,7 @@ impl ArrayLiteral { /// Indicates if a spread operator in the array literal has a trailing comma. /// This is a syntax error in some cases. #[must_use] - pub fn has_trailing_comma_spread(&self) -> bool { + pub const fn has_trailing_comma_spread(&self) -> bool { self.has_trailing_comma_spread } diff --git a/boa_ast/src/expression/literal/mod.rs b/boa_ast/src/expression/literal/mod.rs index 60951351d3b..beed8d8f900 100644 --- a/boa_ast/src/expression/literal/mod.rs +++ b/boa_ast/src/expression/literal/mod.rs @@ -167,7 +167,7 @@ impl From for Literal { impl From for Expression { #[inline] fn from(lit: Literal) -> Self { - Expression::Literal(lit) + Self::Literal(lit) } } @@ -193,7 +193,7 @@ impl VisitWith for Literal { where V: Visitor<'a>, { - if let Literal::String(sym) = self { + if let Self::String(sym) = self { visitor.visit_sym(sym) } else { ControlFlow::Continue(()) @@ -204,7 +204,7 @@ impl VisitWith for Literal { where V: VisitorMut<'a>, { - if let Literal::String(sym) = self { + if let Self::String(sym) = self { visitor.visit_sym_mut(sym) } else { ControlFlow::Continue(()) diff --git a/boa_ast/src/expression/literal/object.rs b/boa_ast/src/expression/literal/object.rs index 1736a2e31f2..59c7587754a 100644 --- a/boa_ast/src/expression/literal/object.rs +++ b/boa_ast/src/expression/literal/object.rs @@ -43,7 +43,7 @@ impl ObjectLiteral { /// Gets the object literal properties #[inline] #[must_use] - pub fn properties(&self) -> &[PropertyDefinition] { + pub const fn properties(&self) -> &[PropertyDefinition] { &self.properties } @@ -121,18 +121,12 @@ impl ObjectLiteral { return None; } excluded_keys.push(*ident); - bindings.push(ObjectPatternElement::SingleName { - ident: *ident, - name: PropertyName::Literal(name), - default_init: Some(assign.rhs().clone()), - }); - } else { - bindings.push(ObjectPatternElement::SingleName { - ident: *ident, - name: PropertyName::Literal(name), - default_init: Some(assign.rhs().clone()), - }); } + bindings.push(ObjectPatternElement::SingleName { + ident: *ident, + name: PropertyName::Literal(name), + default_init: Some(assign.rhs().clone()), + }); } else { return None; } diff --git a/boa_ast/src/expression/literal/template.rs b/boa_ast/src/expression/literal/template.rs index 7c3f4bc189c..e899c4564d2 100644 --- a/boa_ast/src/expression/literal/template.rs +++ b/boa_ast/src/expression/literal/template.rs @@ -60,7 +60,7 @@ impl TemplateLiteral { /// Gets the element list of this `TemplateLiteral`. #[must_use] - pub fn elements(&self) -> &[TemplateElement] { + pub const fn elements(&self) -> &[TemplateElement] { &self.elements } } @@ -116,8 +116,8 @@ impl VisitWith for TemplateElement { V: Visitor<'a>, { match self { - TemplateElement::String(sym) => visitor.visit_sym(sym), - TemplateElement::Expr(expr) => visitor.visit_expression(expr), + Self::String(sym) => visitor.visit_sym(sym), + Self::Expr(expr) => visitor.visit_expression(expr), } } @@ -126,8 +126,8 @@ impl VisitWith for TemplateElement { V: VisitorMut<'a>, { match self { - TemplateElement::String(sym) => visitor.visit_sym_mut(sym), - TemplateElement::Expr(expr) => visitor.visit_expression_mut(expr), + Self::String(sym) => visitor.visit_sym_mut(sym), + Self::Expr(expr) => visitor.visit_expression_mut(expr), } } } diff --git a/boa_ast/src/expression/mod.rs b/boa_ast/src/expression/mod.rs index ad048080947..47b389b8067 100644 --- a/boa_ast/src/expression/mod.rs +++ b/boa_ast/src/expression/mod.rs @@ -200,7 +200,7 @@ impl Expression { impl From for Statement { #[inline] fn from(expr: Expression) -> Self { - Statement::Expression(expr) + Self::Expression(expr) } } @@ -217,33 +217,33 @@ impl VisitWith for Expression { V: Visitor<'a>, { match self { - Expression::Identifier(id) => visitor.visit_identifier(id), - Expression::Literal(lit) => visitor.visit_literal(lit), - Expression::ArrayLiteral(arlit) => visitor.visit_array_literal(arlit), - Expression::ObjectLiteral(olit) => visitor.visit_object_literal(olit), - Expression::Spread(sp) => visitor.visit_spread(sp), - Expression::Function(f) => visitor.visit_function(f), - Expression::ArrowFunction(af) => visitor.visit_arrow_function(af), - Expression::AsyncArrowFunction(af) => visitor.visit_async_arrow_function(af), - Expression::Generator(g) => visitor.visit_generator(g), - Expression::AsyncFunction(af) => visitor.visit_async_function(af), - Expression::AsyncGenerator(ag) => visitor.visit_async_generator(ag), - Expression::Class(c) => visitor.visit_class(c), - Expression::TemplateLiteral(tlit) => visitor.visit_template_literal(tlit), - Expression::PropertyAccess(pa) => visitor.visit_property_access(pa), - Expression::New(n) => visitor.visit_new(n), - Expression::Call(c) => visitor.visit_call(c), - Expression::SuperCall(sc) => visitor.visit_super_call(sc), - Expression::Optional(opt) => visitor.visit_optional(opt), - Expression::TaggedTemplate(tt) => visitor.visit_tagged_template(tt), - Expression::Assign(a) => visitor.visit_assign(a), - Expression::Unary(u) => visitor.visit_unary(u), - Expression::Binary(b) => visitor.visit_binary(b), - Expression::Conditional(c) => visitor.visit_conditional(c), - Expression::Await(a) => visitor.visit_await(a), - Expression::Yield(y) => visitor.visit_yield(y), - Expression::FormalParameterList(fpl) => visitor.visit_formal_parameter_list(fpl), - Expression::This | Expression::NewTarget => { + 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(()) } @@ -255,33 +255,33 @@ impl VisitWith for Expression { V: VisitorMut<'a>, { match self { - Expression::Identifier(id) => visitor.visit_identifier_mut(id), - Expression::Literal(lit) => visitor.visit_literal_mut(lit), - Expression::ArrayLiteral(arlit) => visitor.visit_array_literal_mut(arlit), - Expression::ObjectLiteral(olit) => visitor.visit_object_literal_mut(olit), - Expression::Spread(sp) => visitor.visit_spread_mut(sp), - Expression::Function(f) => visitor.visit_function_mut(f), - Expression::ArrowFunction(af) => visitor.visit_arrow_function_mut(af), - Expression::AsyncArrowFunction(af) => visitor.visit_async_arrow_function_mut(af), - Expression::Generator(g) => visitor.visit_generator_mut(g), - Expression::AsyncFunction(af) => visitor.visit_async_function_mut(af), - Expression::AsyncGenerator(ag) => visitor.visit_async_generator_mut(ag), - Expression::Class(c) => visitor.visit_class_mut(c), - Expression::TemplateLiteral(tlit) => visitor.visit_template_literal_mut(tlit), - Expression::PropertyAccess(pa) => visitor.visit_property_access_mut(pa), - Expression::New(n) => visitor.visit_new_mut(n), - Expression::Call(c) => visitor.visit_call_mut(c), - Expression::SuperCall(sc) => visitor.visit_super_call_mut(sc), - Expression::Optional(opt) => visitor.visit_optional_mut(opt), - Expression::TaggedTemplate(tt) => visitor.visit_tagged_template_mut(tt), - Expression::Assign(a) => visitor.visit_assign_mut(a), - Expression::Unary(u) => visitor.visit_unary_mut(u), - Expression::Binary(b) => visitor.visit_binary_mut(b), - Expression::Conditional(c) => visitor.visit_conditional_mut(c), - Expression::Await(a) => visitor.visit_await_mut(a), - Expression::Yield(y) => visitor.visit_yield_mut(y), - Expression::FormalParameterList(fpl) => visitor.visit_formal_parameter_list_mut(fpl), - Expression::This | Expression::NewTarget => { + 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/boa_ast/src/expression/new.rs b/boa_ast/src/expression/new.rs index f93e7a9bea2..4b77e8323e6 100644 --- a/boa_ast/src/expression/new.rs +++ b/boa_ast/src/expression/new.rs @@ -31,20 +31,20 @@ impl New { /// Gets the constructor of the new expression. #[inline] #[must_use] - pub fn constructor(&self) -> &Expression { + pub const fn constructor(&self) -> &Expression { self.call.function() } /// Retrieves the arguments passed to the constructor. #[inline] #[must_use] - pub fn arguments(&self) -> &[Expression] { + pub const fn arguments(&self) -> &[Expression] { self.call.args() } /// Returns the inner call expression. #[must_use] - pub fn call(&self) -> &Call { + pub const fn call(&self) -> &Call { &self.call } } diff --git a/boa_ast/src/expression/operator/assign/mod.rs b/boa_ast/src/expression/operator/assign/mod.rs index a2bbc692461..588c844946f 100644 --- a/boa_ast/src/expression/operator/assign/mod.rs +++ b/boa_ast/src/expression/operator/assign/mod.rs @@ -50,21 +50,21 @@ impl Assign { /// Gets the operator of the assignment operation. #[inline] #[must_use] - pub fn op(&self) -> AssignOp { + pub const fn op(&self) -> AssignOp { self.op } /// Gets the left hand side of the assignment operation. #[inline] #[must_use] - pub fn lhs(&self) -> &AssignTarget { + pub const fn lhs(&self) -> &AssignTarget { &self.lhs } /// Gets the right hand side of the assignment operation. #[inline] #[must_use] - pub fn rhs(&self) -> &Expression { + pub const fn rhs(&self) -> &Expression { &self.rhs } } @@ -151,9 +151,9 @@ impl ToInternedString for AssignTarget { #[inline] fn to_interned_string(&self, interner: &Interner) -> String { match self { - AssignTarget::Identifier(id) => id.to_interned_string(interner), - AssignTarget::Access(access) => access.to_interned_string(interner), - AssignTarget::Pattern(pattern) => pattern.to_interned_string(interner), + Self::Identifier(id) => id.to_interned_string(interner), + Self::Access(access) => access.to_interned_string(interner), + Self::Pattern(pattern) => pattern.to_interned_string(interner), } } } @@ -171,9 +171,9 @@ impl VisitWith for AssignTarget { V: Visitor<'a>, { match self { - AssignTarget::Identifier(id) => visitor.visit_identifier(id), - AssignTarget::Access(pa) => visitor.visit_property_access(pa), - AssignTarget::Pattern(pat) => visitor.visit_pattern(pat), + Self::Identifier(id) => visitor.visit_identifier(id), + Self::Access(pa) => visitor.visit_property_access(pa), + Self::Pattern(pat) => visitor.visit_pattern(pat), } } @@ -182,9 +182,9 @@ impl VisitWith for AssignTarget { V: VisitorMut<'a>, { match self { - AssignTarget::Identifier(id) => visitor.visit_identifier_mut(id), - AssignTarget::Access(pa) => visitor.visit_property_access_mut(pa), - AssignTarget::Pattern(pat) => visitor.visit_pattern_mut(pat), + 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/boa_ast/src/expression/operator/assign/op.rs b/boa_ast/src/expression/operator/assign/op.rs index a2c25df8cf5..e574b1e9137 100644 --- a/boa_ast/src/expression/operator/assign/op.rs +++ b/boa_ast/src/expression/operator/assign/op.rs @@ -214,7 +214,7 @@ pub enum AssignOp { impl AssignOp { /// Retrieves the operation as a static string. - fn as_str(self) -> &'static str { + const fn as_str(self) -> &'static str { match self { Self::Assign => "=", Self::Add => "+=", diff --git a/boa_ast/src/expression/operator/binary/mod.rs b/boa_ast/src/expression/operator/binary/mod.rs index 2e280392641..c39466056fb 100644 --- a/boa_ast/src/expression/operator/binary/mod.rs +++ b/boa_ast/src/expression/operator/binary/mod.rs @@ -53,21 +53,21 @@ impl Binary { /// Gets the binary operation of the Expression. #[inline] #[must_use] - pub fn op(&self) -> BinaryOp { + pub const fn op(&self) -> BinaryOp { self.op } /// Gets the left hand side of the binary operation. #[inline] #[must_use] - pub fn lhs(&self) -> &Expression { + pub const fn lhs(&self) -> &Expression { &self.lhs } /// Gets the right hand side of the binary operation. #[inline] #[must_use] - pub fn rhs(&self) -> &Expression { + pub const fn rhs(&self) -> &Expression { &self.rhs } } diff --git a/boa_ast/src/expression/operator/binary/op.rs b/boa_ast/src/expression/operator/binary/op.rs index 60ace261298..9bba78e85b1 100644 --- a/boa_ast/src/expression/operator/binary/op.rs +++ b/boa_ast/src/expression/operator/binary/op.rs @@ -61,7 +61,7 @@ impl From for BinaryOp { impl BinaryOp { /// Retrieves the operation as a static string. - fn as_str(self) -> &'static str { + const fn as_str(self) -> &'static str { match self { Self::Arithmetic(ref op) => op.as_str(), Self::Bitwise(ref op) => op.as_str(), @@ -171,7 +171,7 @@ pub enum ArithmeticOp { impl ArithmeticOp { /// Retrieves the operation as a static string. - fn as_str(self) -> &'static str { + const fn as_str(self) -> &'static str { match self { Self::Add => "+", Self::Sub => "-", @@ -287,7 +287,7 @@ pub enum BitwiseOp { impl BitwiseOp { /// Retrieves the operation as a static string. - fn as_str(self) -> &'static str { + const fn as_str(self) -> &'static str { match self { Self::And => "&", Self::Or => "|", @@ -481,7 +481,7 @@ pub enum RelationalOp { impl RelationalOp { /// Retrieves the operation as a static string. - fn as_str(self) -> &'static str { + const fn as_str(self) -> &'static str { match self { Self::Equal => "==", Self::NotEqual => "!=", @@ -561,7 +561,7 @@ pub enum LogicalOp { impl LogicalOp { /// Retrieves the operation as a static string. - fn as_str(self) -> &'static str { + const fn as_str(self) -> &'static str { match self { Self::And => "&&", Self::Or => "||", diff --git a/boa_ast/src/expression/operator/conditional.rs b/boa_ast/src/expression/operator/conditional.rs index a0ac5711d61..f18b8d8f6a1 100644 --- a/boa_ast/src/expression/operator/conditional.rs +++ b/boa_ast/src/expression/operator/conditional.rs @@ -33,21 +33,21 @@ impl Conditional { /// Gets the condition of the `Conditional` expression. #[inline] #[must_use] - pub fn condition(&self) -> &Expression { + pub const fn condition(&self) -> &Expression { &self.condition } /// Gets the expression returned if `condition` is truthy. #[inline] #[must_use] - pub fn if_true(&self) -> &Expression { + pub const fn if_true(&self) -> &Expression { &self.if_true } /// Gets the expression returned if `condition` is falsy. #[inline] #[must_use] - pub fn if_false(&self) -> &Expression { + pub const fn if_false(&self) -> &Expression { &self.if_false } diff --git a/boa_ast/src/expression/operator/unary/mod.rs b/boa_ast/src/expression/operator/unary/mod.rs index ecf77b0f9c6..9c133e86e76 100644 --- a/boa_ast/src/expression/operator/unary/mod.rs +++ b/boa_ast/src/expression/operator/unary/mod.rs @@ -50,7 +50,7 @@ impl Unary { /// Gets the unary operation of the Expression. #[inline] #[must_use] - pub fn op(&self) -> UnaryOp { + pub const fn op(&self) -> UnaryOp { self.op } diff --git a/boa_ast/src/expression/operator/unary/op.rs b/boa_ast/src/expression/operator/unary/op.rs index ce3c7f607c9..f4e2461efd2 100644 --- a/boa_ast/src/expression/operator/unary/op.rs +++ b/boa_ast/src/expression/operator/unary/op.rs @@ -193,7 +193,7 @@ pub enum UnaryOp { impl UnaryOp { /// Retrieves the operation as a static string. - fn as_str(self) -> &'static str { + const fn as_str(self) -> &'static str { match self { Self::IncrementPost | Self::IncrementPre => "++", Self::DecrementPost | Self::DecrementPre => "--", diff --git a/boa_ast/src/expression/optional.rs b/boa_ast/src/expression/optional.rs index 7d7ff48cdd6..b7d16b3ea91 100644 --- a/boa_ast/src/expression/optional.rs +++ b/boa_ast/src/expression/optional.rs @@ -35,11 +35,9 @@ impl VisitWith for OptionalOperationKind { V: Visitor<'a>, { match self { - OptionalOperationKind::SimplePropertyAccess { field } => { - visitor.visit_property_access_field(field) - } - OptionalOperationKind::PrivatePropertyAccess { field } => visitor.visit_sym(field), - OptionalOperationKind::Call { args } => { + Self::SimplePropertyAccess { field } => visitor.visit_property_access_field(field), + Self::PrivatePropertyAccess { field } => visitor.visit_sym(field), + Self::Call { args } => { for arg in args.iter() { try_break!(visitor.visit_expression(arg)); } @@ -53,11 +51,9 @@ impl VisitWith for OptionalOperationKind { V: VisitorMut<'a>, { match self { - OptionalOperationKind::SimplePropertyAccess { field } => { - visitor.visit_property_access_field_mut(field) - } - OptionalOperationKind::PrivatePropertyAccess { field } => visitor.visit_sym_mut(field), - OptionalOperationKind::Call { args } => { + Self::SimplePropertyAccess { field } => visitor.visit_property_access_field_mut(field), + Self::PrivatePropertyAccess { field } => visitor.visit_sym_mut(field), + Self::Call { args } => { for arg in args.iter_mut() { try_break!(visitor.visit_expression_mut(arg)); } @@ -85,13 +81,13 @@ impl OptionalOperation { /// Creates a new `OptionalOperation`. #[inline] #[must_use] - pub fn new(kind: OptionalOperationKind, shorted: bool) -> Self { + pub const fn new(kind: OptionalOperationKind, shorted: bool) -> Self { Self { kind, shorted } } /// Gets the kind of operation. #[inline] #[must_use] - pub fn kind(&self) -> &OptionalOperationKind { + pub const fn kind(&self) -> &OptionalOperationKind { &self.kind } @@ -99,7 +95,7 @@ impl OptionalOperation { /// `undefined` or `null`. #[inline] #[must_use] - pub fn shorted(&self) -> bool { + pub const fn shorted(&self) -> bool { self.shorted } } @@ -236,7 +232,7 @@ impl Optional { impl From for Expression { fn from(opt: Optional) -> Self { - Expression::Optional(opt) + Self::Optional(opt) } } diff --git a/boa_ast/src/expression/spread.rs b/boa_ast/src/expression/spread.rs index fc86dde42ad..95d4ed4b17e 100644 --- a/boa_ast/src/expression/spread.rs +++ b/boa_ast/src/expression/spread.rs @@ -33,7 +33,7 @@ impl Spread { /// Gets the target expression to be expanded by the spread operator. #[inline] #[must_use] - pub fn target(&self) -> &Expression { + pub const fn target(&self) -> &Expression { &self.target } diff --git a/boa_ast/src/expression/tagged_template.rs b/boa_ast/src/expression/tagged_template.rs index 2a01bbbf200..68d79f61ac1 100644 --- a/boa_ast/src/expression/tagged_template.rs +++ b/boa_ast/src/expression/tagged_template.rs @@ -45,28 +45,28 @@ impl TaggedTemplate { /// Gets the tag function of the template. #[inline] #[must_use] - pub fn tag(&self) -> &Expression { + pub const fn tag(&self) -> &Expression { &self.tag } /// Gets the inner raw strings of the template. #[inline] #[must_use] - pub fn raws(&self) -> &[Sym] { + pub const fn raws(&self) -> &[Sym] { &self.raws } /// Gets the cooked strings of the template. #[inline] #[must_use] - pub fn cookeds(&self) -> &[Option] { + pub const fn cookeds(&self) -> &[Option] { &self.cookeds } /// Gets the interpolated expressions of the template. #[inline] #[must_use] - pub fn exprs(&self) -> &[Expression] { + pub const fn exprs(&self) -> &[Expression] { &self.exprs } } diff --git a/boa_ast/src/expression/yield.rs b/boa_ast/src/expression/yield.rs index 7357e00c64d..91c2cd01b4a 100644 --- a/boa_ast/src/expression/yield.rs +++ b/boa_ast/src/expression/yield.rs @@ -31,7 +31,7 @@ impl Yield { /// Returns `true` if this `Yield` statement delegates to another generator or iterable object. #[inline] #[must_use] - pub fn delegate(&self) -> bool { + pub const fn delegate(&self) -> bool { self.delegate } diff --git a/boa_ast/src/function/arrow_function.rs b/boa_ast/src/function/arrow_function.rs index c02c3a8e2a3..0c5a5b5b85d 100644 --- a/boa_ast/src/function/arrow_function.rs +++ b/boa_ast/src/function/arrow_function.rs @@ -31,7 +31,11 @@ impl ArrowFunction { /// Creates a new `ArrowFunctionDecl` AST Expression. #[inline] #[must_use] - pub fn new(name: Option, params: FormalParameterList, body: StatementList) -> Self { + pub const fn new( + name: Option, + params: FormalParameterList, + body: StatementList, + ) -> Self { Self { name, parameters: params, @@ -42,7 +46,7 @@ impl ArrowFunction { /// Gets the name of the function declaration. #[inline] #[must_use] - pub fn name(&self) -> Option { + pub const fn name(&self) -> Option { self.name } @@ -55,14 +59,14 @@ impl ArrowFunction { /// Gets the list of parameters of the arrow function. #[inline] #[must_use] - pub fn parameters(&self) -> &FormalParameterList { + pub const fn parameters(&self) -> &FormalParameterList { &self.parameters } /// Gets the body of the arrow function. #[inline] #[must_use] - pub fn body(&self) -> &StatementList { + pub const fn body(&self) -> &StatementList { &self.body } } diff --git a/boa_ast/src/function/async_arrow_function.rs b/boa_ast/src/function/async_arrow_function.rs index 191de2f5422..378dbfe8c07 100644 --- a/boa_ast/src/function/async_arrow_function.rs +++ b/boa_ast/src/function/async_arrow_function.rs @@ -31,7 +31,7 @@ impl AsyncArrowFunction { /// Creates a new `AsyncArrowFunction` AST Expression. #[inline] #[must_use] - pub fn new( + pub const fn new( name: Option, parameters: FormalParameterList, body: StatementList, @@ -46,7 +46,7 @@ impl AsyncArrowFunction { /// Gets the name of the function declaration. #[inline] #[must_use] - pub fn name(&self) -> Option { + pub const fn name(&self) -> Option { self.name } @@ -60,14 +60,14 @@ impl AsyncArrowFunction { /// Gets the list of parameters of the arrow function. #[inline] #[must_use] - pub fn parameters(&self) -> &FormalParameterList { + pub const fn parameters(&self) -> &FormalParameterList { &self.parameters } /// Gets the body of the arrow function. #[inline] #[must_use] - pub fn body(&self) -> &StatementList { + pub const fn body(&self) -> &StatementList { &self.body } } diff --git a/boa_ast/src/function/async_function.rs b/boa_ast/src/function/async_function.rs index 17df80504f8..74100ee8739 100644 --- a/boa_ast/src/function/async_function.rs +++ b/boa_ast/src/function/async_function.rs @@ -33,7 +33,7 @@ impl AsyncFunction { /// Creates a new function expression #[inline] #[must_use] - pub fn new( + pub const fn new( name: Option, parameters: FormalParameterList, body: StatementList, @@ -50,28 +50,28 @@ impl AsyncFunction { /// Gets the name of the function declaration. #[inline] #[must_use] - pub fn name(&self) -> Option { + pub const fn name(&self) -> Option { self.name } /// Gets the list of parameters of the function declaration. #[inline] #[must_use] - pub fn parameters(&self) -> &FormalParameterList { + pub const fn parameters(&self) -> &FormalParameterList { &self.parameters } /// Gets the body of the function declaration. #[inline] #[must_use] - pub fn body(&self) -> &StatementList { + pub const fn body(&self) -> &StatementList { &self.body } /// Returns whether the function expression has a binding identifier. #[inline] #[must_use] - pub fn has_binding_identifier(&self) -> bool { + pub const fn has_binding_identifier(&self) -> bool { self.has_binding_identifier } } diff --git a/boa_ast/src/function/async_generator.rs b/boa_ast/src/function/async_generator.rs index 420a8bbd23f..f98309237af 100644 --- a/boa_ast/src/function/async_generator.rs +++ b/boa_ast/src/function/async_generator.rs @@ -32,7 +32,7 @@ impl AsyncGenerator { /// Creates a new async generator expression #[inline] #[must_use] - pub fn new( + pub const fn new( name: Option, parameters: FormalParameterList, body: StatementList, @@ -49,28 +49,28 @@ impl AsyncGenerator { /// Gets the name of the async generator expression #[inline] #[must_use] - pub fn name(&self) -> Option { + pub const fn name(&self) -> Option { self.name } /// Gets the list of parameters of the async generator expression #[inline] #[must_use] - pub fn parameters(&self) -> &FormalParameterList { + pub const fn parameters(&self) -> &FormalParameterList { &self.parameters } /// Gets the body of the async generator expression #[inline] #[must_use] - pub fn body(&self) -> &StatementList { + pub const fn body(&self) -> &StatementList { &self.body } /// Returns whether the function expression has a binding identifier. #[inline] #[must_use] - pub fn has_binding_identifier(&self) -> bool { + pub const fn has_binding_identifier(&self) -> bool { self.has_binding_identifier } } diff --git a/boa_ast/src/function/class.rs b/boa_ast/src/function/class.rs index dea5811eedf..1106371932d 100644 --- a/boa_ast/src/function/class.rs +++ b/boa_ast/src/function/class.rs @@ -52,28 +52,28 @@ impl Class { /// Returns the name of the class. #[inline] #[must_use] - pub fn name(&self) -> Option { + pub const fn name(&self) -> Option { self.name } /// Returns the super class ref of the class. #[inline] #[must_use] - pub fn super_ref(&self) -> Option<&Expression> { + pub const fn super_ref(&self) -> Option<&Expression> { self.super_ref.as_ref() } /// Returns the constructor of the class. #[inline] #[must_use] - pub fn constructor(&self) -> Option<&Function> { + 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 fn elements(&self) -> &[ClassElement] { + pub const fn elements(&self) -> &[ClassElement] { &self.elements } } @@ -90,21 +90,23 @@ impl ToIndentedString for Class { if self.elements.is_empty() && self.constructor().is_none() { return format!( "class {class_name}{} {{}}", - if let Some(sup) = &self.super_ref { - format!(" extends {}", sup.to_interned_string(interner)) - } else { - String::new() - } + 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", - if let Some(sup) = &self.super_ref { - format!("extends {}", sup.to_interned_string(interner)) - } else { - String::new() - } + 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!( @@ -438,13 +440,11 @@ impl VisitWith for ClassElement { V: Visitor<'a>, { match self { - ClassElement::MethodDefinition(pn, md) - | ClassElement::StaticMethodDefinition(pn, md) => { + Self::MethodDefinition(pn, md) | Self::StaticMethodDefinition(pn, md) => { try_break!(visitor.visit_property_name(pn)); visitor.visit_method_definition(md) } - ClassElement::FieldDefinition(pn, maybe_expr) - | ClassElement::StaticFieldDefinition(pn, maybe_expr) => { + 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) @@ -452,13 +452,13 @@ impl VisitWith for ClassElement { ControlFlow::Continue(()) } } - ClassElement::PrivateMethodDefinition(sym, md) - | ClassElement::PrivateStaticMethodDefinition(sym, md) => { + Self::PrivateMethodDefinition(sym, md) + | Self::PrivateStaticMethodDefinition(sym, md) => { try_break!(visitor.visit_sym(sym)); visitor.visit_method_definition(md) } - ClassElement::PrivateFieldDefinition(sym, maybe_expr) - | ClassElement::PrivateStaticFieldDefinition(sym, maybe_expr) => { + Self::PrivateFieldDefinition(sym, maybe_expr) + | Self::PrivateStaticFieldDefinition(sym, maybe_expr) => { try_break!(visitor.visit_sym(sym)); if let Some(expr) = maybe_expr { visitor.visit_expression(expr) @@ -466,7 +466,7 @@ impl VisitWith for ClassElement { ControlFlow::Continue(()) } } - ClassElement::StaticBlock(sl) => visitor.visit_statement_list(sl), + Self::StaticBlock(sl) => visitor.visit_statement_list(sl), } } @@ -475,13 +475,11 @@ impl VisitWith for ClassElement { V: VisitorMut<'a>, { match self { - ClassElement::MethodDefinition(pn, md) - | ClassElement::StaticMethodDefinition(pn, md) => { + Self::MethodDefinition(pn, md) | Self::StaticMethodDefinition(pn, md) => { try_break!(visitor.visit_property_name_mut(pn)); visitor.visit_method_definition_mut(md) } - ClassElement::FieldDefinition(pn, maybe_expr) - | ClassElement::StaticFieldDefinition(pn, maybe_expr) => { + 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) @@ -489,13 +487,13 @@ impl VisitWith for ClassElement { ControlFlow::Continue(()) } } - ClassElement::PrivateMethodDefinition(sym, md) - | ClassElement::PrivateStaticMethodDefinition(sym, md) => { + Self::PrivateMethodDefinition(sym, md) + | Self::PrivateStaticMethodDefinition(sym, md) => { try_break!(visitor.visit_sym_mut(sym)); visitor.visit_method_definition_mut(md) } - ClassElement::PrivateFieldDefinition(sym, maybe_expr) - | ClassElement::PrivateStaticFieldDefinition(sym, maybe_expr) => { + Self::PrivateFieldDefinition(sym, maybe_expr) + | Self::PrivateStaticFieldDefinition(sym, maybe_expr) => { try_break!(visitor.visit_sym_mut(sym)); if let Some(expr) = maybe_expr { visitor.visit_expression_mut(expr) @@ -503,7 +501,7 @@ impl VisitWith for ClassElement { ControlFlow::Continue(()) } } - ClassElement::StaticBlock(sl) => visitor.visit_statement_list_mut(sl), + Self::StaticBlock(sl) => visitor.visit_statement_list_mut(sl), } } } diff --git a/boa_ast/src/function/generator.rs b/boa_ast/src/function/generator.rs index 5478633d46e..9f18623c38b 100644 --- a/boa_ast/src/function/generator.rs +++ b/boa_ast/src/function/generator.rs @@ -34,7 +34,7 @@ impl Generator { /// Creates a new generator expression #[inline] #[must_use] - pub fn new( + pub const fn new( name: Option, parameters: FormalParameterList, body: StatementList, @@ -51,28 +51,28 @@ impl Generator { /// Gets the name of the generator declaration. #[inline] #[must_use] - pub fn name(&self) -> Option { + pub const fn name(&self) -> Option { self.name } /// Gets the list of parameters of the generator declaration. #[inline] #[must_use] - pub fn parameters(&self) -> &FormalParameterList { + pub const fn parameters(&self) -> &FormalParameterList { &self.parameters } /// Gets the body of the generator declaration. #[inline] #[must_use] - pub fn body(&self) -> &StatementList { + pub const fn body(&self) -> &StatementList { &self.body } /// Returns whether the function expression has a binding identifier. #[inline] #[must_use] - pub fn has_binding_identifier(&self) -> bool { + pub const fn has_binding_identifier(&self) -> bool { self.has_binding_identifier } } diff --git a/boa_ast/src/function/mod.rs b/boa_ast/src/function/mod.rs index 4aa1d06c395..c043f07b248 100644 --- a/boa_ast/src/function/mod.rs +++ b/boa_ast/src/function/mod.rs @@ -70,7 +70,7 @@ impl Function { /// Creates a new function expression. #[inline] #[must_use] - pub fn new( + pub const fn new( name: Option, parameters: FormalParameterList, body: StatementList, @@ -86,7 +86,7 @@ impl Function { /// Creates a new function expression with an expression binding identifier. #[inline] #[must_use] - pub fn new_with_binding_identifier( + pub const fn new_with_binding_identifier( name: Option, parameters: FormalParameterList, body: StatementList, @@ -103,28 +103,28 @@ impl Function { /// Gets the name of the function declaration. #[inline] #[must_use] - pub fn name(&self) -> Option { + pub const fn name(&self) -> Option { self.name } /// Gets the list of parameters of the function declaration. #[inline] #[must_use] - pub fn parameters(&self) -> &FormalParameterList { + pub const fn parameters(&self) -> &FormalParameterList { &self.parameters } /// Gets the body of the function declaration. #[inline] #[must_use] - pub fn body(&self) -> &StatementList { + pub const fn body(&self) -> &StatementList { &self.body } /// Returns whether the function expression has a binding identifier. #[inline] #[must_use] - pub fn has_binding_identifier(&self) -> bool { + pub const fn has_binding_identifier(&self) -> bool { self.has_binding_identifier } } diff --git a/boa_ast/src/function/parameters.rs b/boa_ast/src/function/parameters.rs index 9d80ebd86b7..eba5b034fe9 100644 --- a/boa_ast/src/function/parameters.rs +++ b/boa_ast/src/function/parameters.rs @@ -81,46 +81,46 @@ impl FormalParameterList { /// Returns the length of the parameter list. /// Note that this is not equal to the length of the parameters slice. #[must_use] - pub fn length(&self) -> u32 { + pub const fn length(&self) -> u32 { self.length } /// Returns the parameter list flags. #[must_use] - pub fn flags(&self) -> FormalParameterListFlags { + pub const fn flags(&self) -> FormalParameterListFlags { self.flags } /// Indicates if the parameter list is simple. #[must_use] - pub fn is_simple(&self) -> bool { + pub const fn is_simple(&self) -> bool { self.flags.contains(FormalParameterListFlags::IS_SIMPLE) } /// Indicates if the parameter list has duplicate parameters. #[must_use] - pub fn has_duplicates(&self) -> bool { + 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 fn has_rest_parameter(&self) -> bool { + 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 fn has_expressions(&self) -> bool { + 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 fn has_arguments(&self) -> bool { + pub const fn has_arguments(&self) -> bool { self.flags.contains(FormalParameterListFlags::HAS_ARGUMENTS) } } @@ -235,25 +235,25 @@ impl FormalParameter { /// Gets the variable of the formal parameter #[must_use] - pub fn variable(&self) -> &Variable { + pub const fn variable(&self) -> &Variable { &self.variable } /// Gets the initialization node of the formal parameter, if any. #[must_use] - pub fn init(&self) -> Option<&Expression> { + pub const fn init(&self) -> Option<&Expression> { self.variable.init() } /// Returns `true` if the parameter is a rest parameter. #[must_use] - pub fn is_rest_param(&self) -> bool { + pub const fn is_rest_param(&self) -> bool { self.is_rest_param } /// Returns `true` if the parameter is an identifier. #[must_use] - pub fn is_identifier(&self) -> bool { + pub const fn is_identifier(&self) -> bool { matches!(&self.variable.binding(), Binding::Identifier(_)) } } diff --git a/boa_ast/src/keyword.rs b/boa_ast/src/keyword.rs index a59527929b6..a90ac7f73be 100644 --- a/boa_ast/src/keyword.rs +++ b/boa_ast/src/keyword.rs @@ -478,7 +478,7 @@ impl Keyword { /// Gets the keyword as a binary operation, if this keyword is the `in` or the `instanceof` /// keywords. #[must_use] - pub fn as_binary_op(self) -> Option { + pub const fn as_binary_op(self) -> Option { match self { Self::In => Some(BinaryOp::Relational(RelationalOp::In)), Self::InstanceOf => Some(BinaryOp::Relational(RelationalOp::InstanceOf)), @@ -488,7 +488,7 @@ impl Keyword { /// Gets the keyword as a tuple of strings. #[must_use] - pub fn as_str(self) -> (&'static str, &'static [u16]) { + pub const fn as_str(self) -> (&'static str, &'static [u16]) { match self { Self::Await => ("await", utf16!("await")), Self::Async => ("async", utf16!("async")), diff --git a/boa_ast/src/lib.rs b/boa_ast/src/lib.rs index c9781d2b546..61bb575bcca 100644 --- a/boa_ast/src/lib.rs +++ b/boa_ast/src/lib.rs @@ -12,44 +12,63 @@ //! [early]: https://tc39.es/ecma262/#sec-static-semantic-rules #![cfg_attr(not(test), forbid(clippy::unwrap_used))] -#![warn( - clippy::perf, - clippy::single_match_else, - clippy::dbg_macro, - clippy::doc_markdown, - clippy::wildcard_imports, - clippy::struct_excessive_bools, - clippy::doc_markdown, - clippy::semicolon_if_nothing_returned, - clippy::pedantic -)] +#![warn(missing_docs, clippy::dbg_macro)] #![deny( - clippy::all, - clippy::cast_lossless, - clippy::redundant_closure_for_method_calls, - clippy::unnested_or_patterns, - clippy::trivially_copy_pass_by_ref, - clippy::needless_pass_by_value, - clippy::match_wildcard_for_single_variants, - clippy::map_unwrap_or, - unused_qualifications, + // 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, - unreachable_pub, - trivial_numeric_casts, + unused_qualifications, + unused_tuple_struct_fields, + variant_size_differences, + + // rustdoc lints https://doc.rust-lang.org/rustdoc/lints.html rustdoc::broken_intra_doc_links, - missing_debug_implementations, - missing_copy_implementations, - deprecated_in_future, - meta_variable_misuse, - non_ascii_idents, - rust_2018_compatibility, - rust_2018_idioms, - future_incompatible, - nonstandard_style, - missing_docs + 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 )] -#![allow(clippy::module_name_repetitions, clippy::too_many_lines)] mod position; mod punctuator; diff --git a/boa_ast/src/pattern.rs b/boa_ast/src/pattern.rs index bd9b8694306..a8b1644b33c 100644 --- a/boa_ast/src/pattern.rs +++ b/boa_ast/src/pattern.rs @@ -47,13 +47,13 @@ pub enum Pattern { impl From for Pattern { fn from(obj: ObjectPattern) -> Self { - Pattern::Object(obj) + Self::Object(obj) } } impl From for Pattern { fn from(obj: ArrayPattern) -> Self { - Pattern::Array(obj) + Self::Array(obj) } } @@ -71,8 +71,8 @@ impl From> for Pattern { impl ToInternedString for Pattern { fn to_interned_string(&self, interner: &Interner) -> String { match &self { - Pattern::Object(o) => o.to_interned_string(interner), - Pattern::Array(a) => a.to_interned_string(interner), + Self::Object(o) => o.to_interned_string(interner), + Self::Array(a) => a.to_interned_string(interner), } } } @@ -83,8 +83,8 @@ impl VisitWith for Pattern { V: Visitor<'a>, { match self { - Pattern::Object(op) => visitor.visit_object_pattern(op), - Pattern::Array(ap) => visitor.visit_array_pattern(ap), + Self::Object(op) => visitor.visit_object_pattern(op), + Self::Array(ap) => visitor.visit_array_pattern(ap), } } @@ -93,8 +93,8 @@ impl VisitWith for Pattern { V: VisitorMut<'a>, { match self { - Pattern::Object(op) => visitor.visit_object_pattern_mut(op), - Pattern::Array(ap) => visitor.visit_array_pattern_mut(ap), + Self::Object(op) => visitor.visit_object_pattern_mut(op), + Self::Array(ap) => visitor.visit_array_pattern_mut(ap), } } } @@ -151,14 +151,14 @@ impl ObjectPattern { /// Gets the bindings for the object binding pattern. #[inline] #[must_use] - pub fn bindings(&self) -> &[ObjectPatternElement] { + pub const fn bindings(&self) -> &[ObjectPatternElement] { &self.0 } /// Returns true if the object binding pattern has a rest element. #[inline] #[must_use] - pub fn has_rest(&self) -> bool { + pub const fn has_rest(&self) -> bool { matches!( self.0.last(), Some(ObjectPatternElement::RestProperty { .. }) @@ -239,7 +239,7 @@ impl ArrayPattern { /// Gets the bindings for the array binding pattern. #[inline] #[must_use] - pub fn bindings(&self) -> &[ArrayPatternElement] { + pub const fn bindings(&self) -> &[ArrayPatternElement] { &self.0 } } @@ -468,7 +468,7 @@ impl VisitWith for ObjectPatternElement { V: Visitor<'a>, { match self { - ObjectPatternElement::SingleName { + Self::SingleName { name, ident, default_init, @@ -481,8 +481,8 @@ impl VisitWith for ObjectPatternElement { ControlFlow::Continue(()) } } - ObjectPatternElement::RestProperty { ident, .. } => visitor.visit_identifier(ident), - ObjectPatternElement::AssignmentPropertyAccess { + Self::RestProperty { ident, .. } => visitor.visit_identifier(ident), + Self::AssignmentPropertyAccess { name, access, default_init, @@ -495,10 +495,10 @@ impl VisitWith for ObjectPatternElement { ControlFlow::Continue(()) } } - ObjectPatternElement::AssignmentRestPropertyAccess { access, .. } => { + Self::AssignmentRestPropertyAccess { access, .. } => { visitor.visit_property_access(access) } - ObjectPatternElement::Pattern { + Self::Pattern { name, pattern, default_init, @@ -519,7 +519,7 @@ impl VisitWith for ObjectPatternElement { V: VisitorMut<'a>, { match self { - ObjectPatternElement::SingleName { + Self::SingleName { name, ident, default_init, @@ -532,8 +532,8 @@ impl VisitWith for ObjectPatternElement { ControlFlow::Continue(()) } } - ObjectPatternElement::RestProperty { ident, .. } => visitor.visit_identifier_mut(ident), - ObjectPatternElement::AssignmentPropertyAccess { + Self::RestProperty { ident, .. } => visitor.visit_identifier_mut(ident), + Self::AssignmentPropertyAccess { name, access, default_init, @@ -546,10 +546,10 @@ impl VisitWith for ObjectPatternElement { ControlFlow::Continue(()) } } - ObjectPatternElement::AssignmentRestPropertyAccess { access, .. } => { + Self::AssignmentRestPropertyAccess { access, .. } => { visitor.visit_property_access_mut(access) } - ObjectPatternElement::Pattern { + Self::Pattern { name, pattern, default_init, @@ -712,7 +712,7 @@ impl VisitWith for ArrayPatternElement { V: Visitor<'a>, { match self { - ArrayPatternElement::SingleName { + Self::SingleName { ident, default_init, } => { @@ -723,11 +723,10 @@ impl VisitWith for ArrayPatternElement { ControlFlow::Continue(()) } } - ArrayPatternElement::PropertyAccess { access } - | ArrayPatternElement::PropertyAccessRest { access } => { + Self::PropertyAccess { access } | Self::PropertyAccessRest { access } => { visitor.visit_property_access(access) } - ArrayPatternElement::Pattern { + Self::Pattern { pattern, default_init, } => { @@ -738,9 +737,9 @@ impl VisitWith for ArrayPatternElement { ControlFlow::Continue(()) } } - ArrayPatternElement::SingleNameRest { ident } => visitor.visit_identifier(ident), - ArrayPatternElement::PatternRest { pattern } => visitor.visit_pattern(pattern), - ArrayPatternElement::Elision => { + 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(()) } @@ -752,7 +751,7 @@ impl VisitWith for ArrayPatternElement { V: VisitorMut<'a>, { match self { - ArrayPatternElement::SingleName { + Self::SingleName { ident, default_init, } => { @@ -763,11 +762,10 @@ impl VisitWith for ArrayPatternElement { ControlFlow::Continue(()) } } - ArrayPatternElement::PropertyAccess { access } - | ArrayPatternElement::PropertyAccessRest { access } => { + Self::PropertyAccess { access } | Self::PropertyAccessRest { access } => { visitor.visit_property_access_mut(access) } - ArrayPatternElement::Pattern { + Self::Pattern { pattern, default_init, } => { @@ -778,9 +776,9 @@ impl VisitWith for ArrayPatternElement { ControlFlow::Continue(()) } } - ArrayPatternElement::SingleNameRest { ident } => visitor.visit_identifier_mut(ident), - ArrayPatternElement::PatternRest { pattern } => visitor.visit_pattern_mut(pattern), - ArrayPatternElement::Elision => { + 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/boa_ast/src/position.rs b/boa_ast/src/position.rs index dafba2fb30c..5c7a0956b4b 100644 --- a/boa_ast/src/position.rs +++ b/boa_ast/src/position.rs @@ -30,14 +30,14 @@ impl Position { /// Gets the line number of the position. #[inline] #[must_use] - pub fn line_number(self) -> u32 { + pub const fn line_number(self) -> u32 { self.line_number.get() } /// Gets the column number of the position. #[inline] #[must_use] - pub fn column_number(self) -> u32 { + pub const fn column_number(self) -> u32 { self.column_number.get() } } @@ -79,14 +79,14 @@ impl Span { /// Gets the starting position of the span. #[inline] #[must_use] - pub fn start(self) -> Position { + pub const fn start(self) -> Position { self.start } /// Gets the final position of the span. #[inline] #[must_use] - pub fn end(self) -> Position { + pub const fn end(self) -> Position { self.end } diff --git a/boa_ast/src/property.rs b/boa_ast/src/property.rs index c6537199038..6f5b5d063fd 100644 --- a/boa_ast/src/property.rs +++ b/boa_ast/src/property.rs @@ -86,17 +86,17 @@ impl VisitWith for PropertyDefinition { V: Visitor<'a>, { match self { - PropertyDefinition::IdentifierReference(id) => visitor.visit_identifier(id), - PropertyDefinition::Property(pn, expr) => { + Self::IdentifierReference(id) => visitor.visit_identifier(id), + Self::Property(pn, expr) => { try_break!(visitor.visit_property_name(pn)); visitor.visit_expression(expr) } - PropertyDefinition::MethodDefinition(pn, md) => { + Self::MethodDefinition(pn, md) => { try_break!(visitor.visit_property_name(pn)); visitor.visit_method_definition(md) } - PropertyDefinition::SpreadObject(expr) => visitor.visit_expression(expr), - PropertyDefinition::CoverInitializedName(id, expr) => { + Self::SpreadObject(expr) => visitor.visit_expression(expr), + Self::CoverInitializedName(id, expr) => { try_break!(visitor.visit_identifier(id)); visitor.visit_expression(expr) } @@ -108,17 +108,17 @@ impl VisitWith for PropertyDefinition { V: VisitorMut<'a>, { match self { - PropertyDefinition::IdentifierReference(id) => visitor.visit_identifier_mut(id), - PropertyDefinition::Property(pn, expr) => { + 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) } - PropertyDefinition::MethodDefinition(pn, md) => { + Self::MethodDefinition(pn, md) => { try_break!(visitor.visit_property_name_mut(pn)); visitor.visit_method_definition_mut(md) } - PropertyDefinition::SpreadObject(expr) => visitor.visit_expression_mut(expr), - PropertyDefinition::CoverInitializedName(id, expr) => { + Self::SpreadObject(expr) => visitor.visit_expression_mut(expr), + Self::CoverInitializedName(id, expr) => { try_break!(visitor.visit_identifier_mut(id)); visitor.visit_expression_mut(expr) } @@ -219,12 +219,10 @@ impl VisitWith for MethodDefinition { V: Visitor<'a>, { match self { - MethodDefinition::Get(f) | MethodDefinition::Set(f) | MethodDefinition::Ordinary(f) => { - visitor.visit_function(f) - } - MethodDefinition::Generator(g) => visitor.visit_generator(g), - MethodDefinition::AsyncGenerator(ag) => visitor.visit_async_generator(ag), - MethodDefinition::Async(af) => visitor.visit_async_function(af), + 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), } } @@ -233,12 +231,10 @@ impl VisitWith for MethodDefinition { V: VisitorMut<'a>, { match self { - MethodDefinition::Get(f) | MethodDefinition::Set(f) | MethodDefinition::Ordinary(f) => { - visitor.visit_function_mut(f) - } - MethodDefinition::Generator(g) => visitor.visit_generator_mut(g), - MethodDefinition::AsyncGenerator(ag) => visitor.visit_async_generator_mut(ag), - MethodDefinition::Async(af) => visitor.visit_async_function_mut(af), + 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), } } } @@ -273,7 +269,7 @@ pub enum PropertyName { impl PropertyName { /// Returns the literal property name if it exists. #[must_use] - pub fn literal(&self) -> Option { + pub const fn literal(&self) -> Option { if let Self::Literal(sym) = self { Some(*sym) } else { @@ -283,7 +279,7 @@ impl PropertyName { /// Returns the expression if the property name is computed. #[must_use] - pub fn computed(&self) -> Option<&Expression> { + pub const fn computed(&self) -> Option<&Expression> { if let Self::Computed(expr) = self { Some(expr) } else { @@ -293,11 +289,12 @@ impl PropertyName { /// Returns either the literal property name or the computed const string property name. #[must_use] - pub fn prop_name(&self) -> Option { + pub const fn prop_name(&self) -> Option { match self { - PropertyName::Literal(sym) - | PropertyName::Computed(Expression::Literal(Literal::String(sym))) => Some(*sym), - PropertyName::Computed(_) => None, + Self::Literal(sym) | Self::Computed(Expression::Literal(Literal::String(sym))) => { + Some(*sym) + } + Self::Computed(_) => None, } } } @@ -305,8 +302,8 @@ impl PropertyName { impl ToInternedString for PropertyName { fn to_interned_string(&self, interner: &Interner) -> String { match self { - PropertyName::Literal(key) => interner.resolve_expect(*key).to_string(), - PropertyName::Computed(key) => key.to_interned_string(interner), + Self::Literal(key) => interner.resolve_expect(*key).to_string(), + Self::Computed(key) => key.to_interned_string(interner), } } } @@ -329,8 +326,8 @@ impl VisitWith for PropertyName { V: Visitor<'a>, { match self { - PropertyName::Literal(sym) => visitor.visit_sym(sym), - PropertyName::Computed(expr) => visitor.visit_expression(expr), + Self::Literal(sym) => visitor.visit_sym(sym), + Self::Computed(expr) => visitor.visit_expression(expr), } } @@ -339,8 +336,8 @@ impl VisitWith for PropertyName { V: VisitorMut<'a>, { match self { - PropertyName::Literal(sym) => visitor.visit_sym_mut(sym), - PropertyName::Computed(expr) => visitor.visit_expression_mut(expr), + Self::Literal(sym) => visitor.visit_sym_mut(sym), + Self::Computed(expr) => visitor.visit_expression_mut(expr), } } } diff --git a/boa_ast/src/statement/block.rs b/boa_ast/src/statement/block.rs index afd402ae33e..ca7f4ff548c 100644 --- a/boa_ast/src/statement/block.rs +++ b/boa_ast/src/statement/block.rs @@ -34,7 +34,7 @@ impl Block { /// Gets the list of statements and declarations in this block. #[inline] #[must_use] - pub fn statement_list(&self) -> &StatementList { + pub const fn statement_list(&self) -> &StatementList { &self.statements } } diff --git a/boa_ast/src/statement/if.rs b/boa_ast/src/statement/if.rs index f41cbae6614..0a9445ac74b 100644 --- a/boa_ast/src/statement/if.rs +++ b/boa_ast/src/statement/if.rs @@ -38,14 +38,14 @@ impl If { /// Gets the condition of the if statement. #[inline] #[must_use] - pub fn cond(&self) -> &Expression { + pub const fn cond(&self) -> &Expression { &self.condition } /// Gets the body to execute if the condition is true. #[inline] #[must_use] - pub fn body(&self) -> &Statement { + pub const fn body(&self) -> &Statement { &self.body } diff --git a/boa_ast/src/statement/iteration/break.rs b/boa_ast/src/statement/iteration/break.rs index 578f91a9027..f1f5f821c0a 100644 --- a/boa_ast/src/statement/iteration/break.rs +++ b/boa_ast/src/statement/iteration/break.rs @@ -28,24 +28,23 @@ pub struct Break { impl Break { /// Creates a `Break` AST node. #[must_use] - pub fn new(label: Option) -> Self { + pub const fn new(label: Option) -> Self { Self { label } } /// Gets the label of the break statement, if any. #[must_use] - pub fn label(&self) -> Option { + pub const fn label(&self) -> Option { self.label } } impl ToInternedString for Break { fn to_interned_string(&self, interner: &Interner) -> String { - if let Some(label) = self.label { - format!("break {}", interner.resolve_expect(label)) - } else { - "break".to_owned() - } + self.label.map_or_else( + || "break".to_owned(), + |label| format!("break {}", interner.resolve_expect(label)), + ) } } diff --git a/boa_ast/src/statement/iteration/continue.rs b/boa_ast/src/statement/iteration/continue.rs index ed2a19f527e..ed5ddb2f2c0 100644 --- a/boa_ast/src/statement/iteration/continue.rs +++ b/boa_ast/src/statement/iteration/continue.rs @@ -26,24 +26,23 @@ pub struct Continue { impl Continue { /// Creates a `Continue` AST node. #[must_use] - pub fn new(label: Option) -> Self { + pub const fn new(label: Option) -> Self { Self { label } } /// Gets the label of this `Continue` statement. #[must_use] - pub fn label(&self) -> Option { + pub const fn label(&self) -> Option { self.label } } impl ToInternedString for Continue { fn to_interned_string(&self, interner: &Interner) -> String { - if let Some(label) = self.label { - format!("continue {}", interner.resolve_expect(label)) - } else { - "continue".to_owned() - } + self.label.map_or_else( + || "continue".to_owned(), + |label| format!("continue {}", interner.resolve_expect(label)), + ) } } diff --git a/boa_ast/src/statement/iteration/do_while_loop.rs b/boa_ast/src/statement/iteration/do_while_loop.rs index 34fd03f33a4..f3e0b3062b7 100644 --- a/boa_ast/src/statement/iteration/do_while_loop.rs +++ b/boa_ast/src/statement/iteration/do_while_loop.rs @@ -31,14 +31,14 @@ impl DoWhileLoop { /// Gets the body of the do-while loop. #[inline] #[must_use] - pub fn body(&self) -> &Statement { + pub const fn body(&self) -> &Statement { &self.body } /// Gets the condition of the do-while loop. #[inline] #[must_use] - pub fn cond(&self) -> &Expression { + pub const fn cond(&self) -> &Expression { &self.condition } /// Creates a `DoWhileLoop` AST node. diff --git a/boa_ast/src/statement/iteration/for_in_loop.rs b/boa_ast/src/statement/iteration/for_in_loop.rs index 41e11083eff..9c2774accee 100644 --- a/boa_ast/src/statement/iteration/for_in_loop.rs +++ b/boa_ast/src/statement/iteration/for_in_loop.rs @@ -38,21 +38,21 @@ impl ForInLoop { /// Gets the initializer of the for...in loop. #[inline] #[must_use] - pub fn initializer(&self) -> &IterableLoopInitializer { + pub const fn initializer(&self) -> &IterableLoopInitializer { &self.initializer } /// Gets the target object of the for...in loop. #[inline] #[must_use] - pub fn target(&self) -> &Expression { + pub const fn target(&self) -> &Expression { &self.target } /// Gets the body of the for...in loop. #[inline] #[must_use] - pub fn body(&self) -> &Statement { + pub const fn body(&self) -> &Statement { &self.body } } diff --git a/boa_ast/src/statement/iteration/for_loop.rs b/boa_ast/src/statement/iteration/for_loop.rs index ea661100447..d0c79dc09a2 100644 --- a/boa_ast/src/statement/iteration/for_loop.rs +++ b/boa_ast/src/statement/iteration/for_loop.rs @@ -44,28 +44,28 @@ impl ForLoop { /// Gets the initialization node. #[inline] #[must_use] - pub fn init(&self) -> Option<&ForLoopInitializer> { + pub const fn init(&self) -> Option<&ForLoopInitializer> { self.inner.init() } /// Gets the loop condition node. #[inline] #[must_use] - pub fn condition(&self) -> Option<&Expression> { + pub const fn condition(&self) -> Option<&Expression> { self.inner.condition() } /// Gets the final expression node. #[inline] #[must_use] - pub fn final_expr(&self) -> Option<&Expression> { + pub const fn final_expr(&self) -> Option<&Expression> { self.inner.final_expr() } /// Gets the body of the for loop. #[inline] #[must_use] - pub fn body(&self) -> &Statement { + pub const fn body(&self) -> &Statement { self.inner.body() } } @@ -148,7 +148,7 @@ struct InnerForLoop { impl InnerForLoop { /// Creates a new inner for loop. #[inline] - fn new( + const fn new( init: Option, condition: Option, final_expr: Option, @@ -164,25 +164,25 @@ impl InnerForLoop { /// Gets the initialization node. #[inline] - fn init(&self) -> Option<&ForLoopInitializer> { + const fn init(&self) -> Option<&ForLoopInitializer> { self.init.as_ref() } /// Gets the loop condition node. #[inline] - fn condition(&self) -> Option<&Expression> { + const fn condition(&self) -> Option<&Expression> { self.condition.as_ref() } /// Gets the final expression node. #[inline] - fn final_expr(&self) -> Option<&Expression> { + const fn final_expr(&self) -> Option<&Expression> { self.final_expr.as_ref() } /// Gets the body of the for loop. #[inline] - fn body(&self) -> &Statement { + const fn body(&self) -> &Statement { &self.body } } @@ -220,21 +220,21 @@ impl ToInternedString for ForLoopInitializer { impl From for ForLoopInitializer { #[inline] fn from(expr: Expression) -> Self { - ForLoopInitializer::Expression(expr) + Self::Expression(expr) } } impl From for ForLoopInitializer { #[inline] fn from(list: LexicalDeclaration) -> Self { - ForLoopInitializer::Lexical(list) + Self::Lexical(list) } } impl From for ForLoopInitializer { #[inline] fn from(list: VarDeclaration) -> Self { - ForLoopInitializer::Var(list) + Self::Var(list) } } @@ -244,9 +244,9 @@ impl VisitWith for ForLoopInitializer { V: Visitor<'a>, { match self { - ForLoopInitializer::Expression(expr) => visitor.visit_expression(expr), - ForLoopInitializer::Var(vd) => visitor.visit_var_declaration(vd), - ForLoopInitializer::Lexical(ld) => visitor.visit_lexical_declaration(ld), + Self::Expression(expr) => visitor.visit_expression(expr), + Self::Var(vd) => visitor.visit_var_declaration(vd), + Self::Lexical(ld) => visitor.visit_lexical_declaration(ld), } } @@ -255,9 +255,9 @@ impl VisitWith for ForLoopInitializer { V: VisitorMut<'a>, { match self { - ForLoopInitializer::Expression(expr) => visitor.visit_expression_mut(expr), - ForLoopInitializer::Var(vd) => visitor.visit_var_declaration_mut(vd), - ForLoopInitializer::Lexical(ld) => visitor.visit_lexical_declaration_mut(ld), + 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/boa_ast/src/statement/iteration/for_of_loop.rs b/boa_ast/src/statement/iteration/for_of_loop.rs index ead1768106c..d5b02aa4975 100644 --- a/boa_ast/src/statement/iteration/for_of_loop.rs +++ b/boa_ast/src/statement/iteration/for_of_loop.rs @@ -50,28 +50,28 @@ impl ForOfLoop { /// Gets the initializer of the for...of loop. #[inline] #[must_use] - pub fn initializer(&self) -> &IterableLoopInitializer { + pub const fn initializer(&self) -> &IterableLoopInitializer { &self.init } /// Gets the iterable expression of the for...of loop. #[inline] #[must_use] - pub fn iterable(&self) -> &Expression { + pub const fn iterable(&self) -> &Expression { &self.iterable } /// Gets the body to execute in the for...of loop. #[inline] #[must_use] - pub fn body(&self) -> &Statement { + 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 fn r#await(&self) -> bool { + pub const fn r#await(&self) -> bool { self.r#await } } diff --git a/boa_ast/src/statement/iteration/mod.rs b/boa_ast/src/statement/iteration/mod.rs index 4377bedc694..de7ebc7c87d 100644 --- a/boa_ast/src/statement/iteration/mod.rs +++ b/boa_ast/src/statement/iteration/mod.rs @@ -73,12 +73,10 @@ impl VisitWith for IterableLoopInitializer { V: Visitor<'a>, { match self { - IterableLoopInitializer::Identifier(id) => visitor.visit_identifier(id), - IterableLoopInitializer::Access(pa) => visitor.visit_property_access(pa), - IterableLoopInitializer::Var(b) - | IterableLoopInitializer::Let(b) - | IterableLoopInitializer::Const(b) => visitor.visit_binding(b), - IterableLoopInitializer::Pattern(p) => visitor.visit_pattern(p), + 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), } } @@ -87,12 +85,10 @@ impl VisitWith for IterableLoopInitializer { V: VisitorMut<'a>, { match self { - IterableLoopInitializer::Identifier(id) => visitor.visit_identifier_mut(id), - IterableLoopInitializer::Access(pa) => visitor.visit_property_access_mut(pa), - IterableLoopInitializer::Var(b) - | IterableLoopInitializer::Let(b) - | IterableLoopInitializer::Const(b) => visitor.visit_binding_mut(b), - IterableLoopInitializer::Pattern(p) => visitor.visit_pattern_mut(p), + 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/boa_ast/src/statement/iteration/while_loop.rs b/boa_ast/src/statement/iteration/while_loop.rs index ab568182100..00494d66ae8 100644 --- a/boa_ast/src/statement/iteration/while_loop.rs +++ b/boa_ast/src/statement/iteration/while_loop.rs @@ -40,14 +40,14 @@ impl WhileLoop { /// Gets the condition of the while loop. #[inline] #[must_use] - pub fn condition(&self) -> &Expression { + pub const fn condition(&self) -> &Expression { &self.condition } /// Gets the body of the while loop. #[inline] #[must_use] - pub fn body(&self) -> &Statement { + pub const fn body(&self) -> &Statement { &self.body } } diff --git a/boa_ast/src/statement/labelled.rs b/boa_ast/src/statement/labelled.rs index 89884ff8537..1fb5cdd3c4e 100644 --- a/boa_ast/src/statement/labelled.rs +++ b/boa_ast/src/statement/labelled.rs @@ -29,8 +29,8 @@ pub enum LabelledItem { impl LabelledItem { pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { match self { - LabelledItem::Function(f) => f.to_indented_string(interner, indentation), - LabelledItem::Statement(stmt) => stmt.to_indented_string(interner, indentation), + Self::Function(f) => f.to_indented_string(interner, indentation), + Self::Statement(stmt) => stmt.to_indented_string(interner, indentation), } } } @@ -59,8 +59,8 @@ impl VisitWith for LabelledItem { V: Visitor<'a>, { match self { - LabelledItem::Function(f) => visitor.visit_function(f), - LabelledItem::Statement(s) => visitor.visit_statement(s), + Self::Function(f) => visitor.visit_function(f), + Self::Statement(s) => visitor.visit_statement(s), } } @@ -69,8 +69,8 @@ impl VisitWith for LabelledItem { V: VisitorMut<'a>, { match self { - LabelledItem::Function(f) => visitor.visit_function_mut(f), - LabelledItem::Statement(s) => visitor.visit_statement_mut(s), + Self::Function(f) => visitor.visit_function_mut(f), + Self::Statement(s) => visitor.visit_statement_mut(s), } } } @@ -101,13 +101,13 @@ impl Labelled { /// Gets the labelled item. #[must_use] - pub fn item(&self) -> &LabelledItem { + pub const fn item(&self) -> &LabelledItem { &self.item } /// Gets the label name. #[must_use] - pub fn label(&self) -> Sym { + pub const fn label(&self) -> Sym { self.label } diff --git a/boa_ast/src/statement/mod.rs b/boa_ast/src/statement/mod.rs index f412a47b98b..bbd610b190a 100644 --- a/boa_ast/src/statement/mod.rs +++ b/boa_ast/src/statement/mod.rs @@ -188,26 +188,26 @@ impl VisitWith for Statement { V: Visitor<'a>, { match self { - Statement::Block(b) => visitor.visit_block(b), - Statement::Var(v) => visitor.visit_var_declaration(v), - Statement::Empty => { + 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(()) } - Statement::Expression(e) => visitor.visit_expression(e), - Statement::If(i) => visitor.visit_if(i), - Statement::DoWhileLoop(dw) => visitor.visit_do_while_loop(dw), - Statement::WhileLoop(w) => visitor.visit_while_loop(w), - Statement::ForLoop(f) => visitor.visit_for_loop(f), - Statement::ForInLoop(fi) => visitor.visit_for_in_loop(fi), - Statement::ForOfLoop(fo) => visitor.visit_for_of_loop(fo), - Statement::Switch(s) => visitor.visit_switch(s), - Statement::Continue(c) => visitor.visit_continue(c), - Statement::Break(b) => visitor.visit_break(b), - Statement::Return(r) => visitor.visit_return(r), - Statement::Labelled(l) => visitor.visit_labelled(l), - Statement::Throw(th) => visitor.visit_throw(th), - Statement::Try(tr) => visitor.visit_try(tr), + 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), } } @@ -216,26 +216,26 @@ impl VisitWith for Statement { V: VisitorMut<'a>, { match self { - Statement::Block(b) => visitor.visit_block_mut(b), - Statement::Var(v) => visitor.visit_var_declaration_mut(v), - Statement::Empty => { + 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(()) } - Statement::Expression(e) => visitor.visit_expression_mut(e), - Statement::If(i) => visitor.visit_if_mut(i), - Statement::DoWhileLoop(dw) => visitor.visit_do_while_loop_mut(dw), - Statement::WhileLoop(w) => visitor.visit_while_loop_mut(w), - Statement::ForLoop(f) => visitor.visit_for_loop_mut(f), - Statement::ForInLoop(fi) => visitor.visit_for_in_loop_mut(fi), - Statement::ForOfLoop(fo) => visitor.visit_for_of_loop_mut(fo), - Statement::Switch(s) => visitor.visit_switch_mut(s), - Statement::Continue(c) => visitor.visit_continue_mut(c), - Statement::Break(b) => visitor.visit_break_mut(b), - Statement::Return(r) => visitor.visit_return_mut(r), - Statement::Labelled(l) => visitor.visit_labelled_mut(l), - Statement::Throw(th) => visitor.visit_throw_mut(th), - Statement::Try(tr) => visitor.visit_try_mut(tr), + 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/boa_ast/src/statement/return.rs b/boa_ast/src/statement/return.rs index 6cd3d36ba22..9477a2805bf 100644 --- a/boa_ast/src/statement/return.rs +++ b/boa_ast/src/statement/return.rs @@ -33,13 +33,13 @@ pub struct Return { impl Return { /// Gets the target expression value of this `Return` statement. #[must_use] - pub fn target(&self) -> Option<&Expression> { + pub const fn target(&self) -> Option<&Expression> { self.target.as_ref() } /// Creates a `Return` AST node. #[must_use] - pub fn new(expression: Option) -> Self { + pub const fn new(expression: Option) -> Self { Self { target: expression } } } @@ -52,10 +52,10 @@ impl From for Statement { impl ToInternedString for Return { fn to_interned_string(&self, interner: &Interner) -> String { - match self.target() { - Some(ex) => format!("return {}", ex.to_interned_string(interner)), - None => "return".to_owned(), - } + self.target().map_or_else( + || "return".to_owned(), + |ex| format!("return {}", ex.to_interned_string(interner)), + ) } } diff --git a/boa_ast/src/statement/switch.rs b/boa_ast/src/statement/switch.rs index 77ce5d79442..6da9f8e2c14 100644 --- a/boa_ast/src/statement/switch.rs +++ b/boa_ast/src/statement/switch.rs @@ -30,21 +30,21 @@ impl Case { /// Creates a `Case` AST node. #[inline] #[must_use] - pub fn new(condition: Expression, body: StatementList) -> Self { + pub const fn new(condition: Expression, body: StatementList) -> Self { Self { condition, body } } /// Gets the condition of the case. #[inline] #[must_use] - pub fn condition(&self) -> &Expression { + pub const fn condition(&self) -> &Expression { &self.condition } /// Gets the statement listin the body of the case. #[inline] #[must_use] - pub fn body(&self) -> &StatementList { + pub const fn body(&self) -> &StatementList { &self.body } } @@ -107,21 +107,21 @@ impl Switch { /// Gets the value to switch. #[inline] #[must_use] - pub fn val(&self) -> &Expression { + pub const fn val(&self) -> &Expression { &self.val } /// Gets the list of cases for the switch statement. #[inline] #[must_use] - pub fn cases(&self) -> &[Case] { + pub const fn cases(&self) -> &[Case] { &self.cases } /// Gets the default statement list, if any. #[inline] #[must_use] - pub fn default(&self) -> Option<&StatementList> { + pub const fn default(&self) -> Option<&StatementList> { self.default.as_ref() } } diff --git a/boa_ast/src/statement/throw.rs b/boa_ast/src/statement/throw.rs index e3936d5c919..90df3b04ad3 100644 --- a/boa_ast/src/statement/throw.rs +++ b/boa_ast/src/statement/throw.rs @@ -30,13 +30,13 @@ pub struct Throw { impl Throw { /// Gets the target expression of this `Throw` statement. #[must_use] - pub fn target(&self) -> &Expression { + pub const fn target(&self) -> &Expression { &self.target } /// Creates a `Throw` AST node. #[must_use] - pub fn new(target: Expression) -> Self { + pub const fn new(target: Expression) -> Self { Self { target } } } diff --git a/boa_ast/src/statement/try.rs b/boa_ast/src/statement/try.rs index c8c673e69a1..2e5a67bc87e 100644 --- a/boa_ast/src/statement/try.rs +++ b/boa_ast/src/statement/try.rs @@ -47,21 +47,21 @@ impl Try { /// Creates a new `Try` AST node. #[inline] #[must_use] - pub fn new(block: Block, handler: ErrorHandler) -> Self { + pub const fn new(block: Block, handler: ErrorHandler) -> Self { Self { block, handler } } /// Gets the `try` block. #[inline] #[must_use] - pub fn block(&self) -> &Block { + pub const fn block(&self) -> &Block { &self.block } /// Gets the `catch` block, if any. #[inline] #[must_use] - pub fn catch(&self) -> Option<&Catch> { + pub const fn catch(&self) -> Option<&Catch> { match &self.handler { ErrorHandler::Catch(c) | ErrorHandler::Full(c, _) => Some(c), ErrorHandler::Finally(_) => None, @@ -71,7 +71,7 @@ impl Try { /// Gets the `finally` block, if any. #[inline] #[must_use] - pub fn finally(&self) -> Option<&Finally> { + pub const fn finally(&self) -> Option<&Finally> { match &self.handler { ErrorHandler::Finally(f) | ErrorHandler::Full(_, f) => Some(f), ErrorHandler::Catch(_) => None, @@ -150,21 +150,21 @@ impl Catch { /// Creates a new catch block. #[inline] #[must_use] - pub fn new(parameter: Option, block: Block) -> Self { + pub const fn new(parameter: Option, block: Block) -> Self { Self { parameter, block } } /// Gets the parameter of the catch block. #[inline] #[must_use] - pub fn parameter(&self) -> Option<&Binding> { + pub const fn parameter(&self) -> Option<&Binding> { self.parameter.as_ref() } /// Retrieves the catch execution block. #[inline] #[must_use] - pub fn block(&self) -> &Block { + pub const fn block(&self) -> &Block { &self.block } } @@ -218,7 +218,7 @@ impl Finally { /// Gets the finally block. #[inline] #[must_use] - pub fn block(&self) -> &Block { + pub const fn block(&self) -> &Block { &self.block } } diff --git a/boa_ast/src/statement_list.rs b/boa_ast/src/statement_list.rs index 35221ecf070..1e6cacf6da3 100644 --- a/boa_ast/src/statement_list.rs +++ b/boa_ast/src/statement_list.rs @@ -30,7 +30,7 @@ pub enum StatementListItem { impl StatementListItem { /// Returns a node ordering based on the hoistability of each statement. #[must_use] - pub fn hoistable_order(a: &Self, b: &Self) -> Ordering { + pub const fn hoistable_order(a: &Self, b: &Self) -> Ordering { match (a, b) { ( Self::Declaration(Declaration::Function(_)), @@ -59,10 +59,10 @@ impl ToIndentedString for StatementListItem { let mut buf = " ".repeat(indentation); match self { - StatementListItem::Statement(stmt) => { + Self::Statement(stmt) => { buf.push_str(&stmt.to_no_indent_string(interner, indentation)); } - StatementListItem::Declaration(decl) => { + Self::Declaration(decl) => { buf.push_str(&decl.to_indented_string(interner, indentation)); } } @@ -74,14 +74,14 @@ impl ToIndentedString for StatementListItem { impl From for StatementListItem { #[inline] fn from(stmt: Statement) -> Self { - StatementListItem::Statement(stmt) + Self::Statement(stmt) } } impl From for StatementListItem { #[inline] fn from(decl: Declaration) -> Self { - StatementListItem::Declaration(decl) + Self::Declaration(decl) } } @@ -91,8 +91,8 @@ impl VisitWith for StatementListItem { V: Visitor<'a>, { match self { - StatementListItem::Statement(statement) => visitor.visit_statement(statement), - StatementListItem::Declaration(declaration) => visitor.visit_declaration(declaration), + Self::Statement(statement) => visitor.visit_statement(statement), + Self::Declaration(declaration) => visitor.visit_declaration(declaration), } } @@ -101,10 +101,8 @@ impl VisitWith for StatementListItem { V: VisitorMut<'a>, { match self { - StatementListItem::Statement(statement) => visitor.visit_statement_mut(statement), - StatementListItem::Declaration(declaration) => { - visitor.visit_declaration_mut(declaration) - } + Self::Statement(statement) => visitor.visit_statement_mut(statement), + Self::Declaration(declaration) => visitor.visit_declaration_mut(declaration), } } } @@ -126,14 +124,14 @@ impl StatementList { /// Gets the list of statements. #[inline] #[must_use] - pub fn statements(&self) -> &[StatementListItem] { + pub const fn statements(&self) -> &[StatementListItem] { &self.statements } /// Get the strict mode. #[inline] #[must_use] - pub fn strict(&self) -> bool { + pub const fn strict(&self) -> bool { self.strict } From 34c475e4d3d8d9be1fa197425ebb0299f8619d87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Nov 2022 13:52:01 +0000 Subject: [PATCH 15/24] Bump clap from 4.0.25 to 4.0.26 (#2444) Bumps [clap](https://github.com/clap-rs/clap) from 4.0.25 to 4.0.26.
Release notes

Sourced from clap's releases.

v4.0.26

[4.0.26] - 2022-11-16

Fixes

  • (error) Fix typos in ContextKind::as_str
Changelog

Sourced from clap's changelog.

[4.0.26] - 2022-11-16

Fixes

  • (error) Fix typos in ContextKind::as_str
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=clap&package-manager=cargo&previous-version=4.0.25&new-version=4.0.26)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- Cargo.lock | 8 ++++---- boa_cli/Cargo.toml | 2 +- boa_tester/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ecff1290e82..308292210b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,7 +109,7 @@ dependencies = [ "boa_engine", "boa_interner", "boa_parser", - "clap 4.0.25", + "clap 4.0.26", "colored", "jemallocator", "phf", @@ -239,7 +239,7 @@ dependencies = [ "boa_gc", "boa_interner", "boa_parser", - "clap 4.0.25", + "clap 4.0.26", "color-eyre", "colored", "fxhash", @@ -354,9 +354,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.0.25" +version = "4.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca505fd2c00136e0d0cd34bcd8b6bd0b59d5779aab396054b716334230c1c" +checksum = "2148adefda54e14492fb9bddcc600b4344c5d1a3123bd666dcb939c6f0e0e57e" dependencies = [ "atty", "bitflags", diff --git a/boa_cli/Cargo.toml b/boa_cli/Cargo.toml index d77fbfae731..286809b4546 100644 --- a/boa_cli/Cargo.toml +++ b/boa_cli/Cargo.toml @@ -18,7 +18,7 @@ boa_interner.workspace = true boa_parser.workspace = true rustyline = "10.0.0" rustyline-derive = "0.7.0" -clap = { version = "4.0.25", features = ["derive"] } +clap = { version = "4.0.26", features = ["derive"] } serde_json = "1.0.87" colored = "2.0.0" regex = "1.7.0" diff --git a/boa_tester/Cargo.toml b/boa_tester/Cargo.toml index 9df9f58cd36..d83bf00cf66 100644 --- a/boa_tester/Cargo.toml +++ b/boa_tester/Cargo.toml @@ -16,7 +16,7 @@ boa_engine = { workspace = true, features = ["intl"] } boa_interner.workspace = true boa_gc.workspace = true boa_parser.workspace = true -clap = { version = "4.0.25", features = ["derive"] } +clap = { version = "4.0.26", features = ["derive"] } serde = { version = "1.0.147", features = ["derive"] } serde_yaml = "0.9.14" serde_json = "1.0.87" From 6c3a9a594858919588cafec91c4e90330bee8c76 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Nov 2022 14:01:47 +0000 Subject: [PATCH 16/24] Bump webpack-cli from 4.10.0 to 5.0.0 (#2443) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [webpack-cli](https://github.com/webpack/webpack-cli) from 4.10.0 to 5.0.0.
Release notes

Sourced from webpack-cli's releases.

v5.0.0

5.0.0 (2022-11-17)

Bug Fixes

  • improve description of the --disable-interpret option (#3364) (bdb7e20)
  • remove the redundant utils export (#3343) (a9ce5d0)
  • respect NODE_PATH env variable (#3411) (83d1f58)
  • show all CLI specific flags in the minimum help output (#3354) (35843e8)

Features

  • failOnWarnings option (#3317) (c48c848)
  • update commander to v9 (#3460) (6621c02)
  • added the --define-process-env-node-env option
  • update interpret to v3 and rechoir to v0.8
  • add an option for preventing interpret (#3329) (c737383)

BREAKING CHANGES

  • the minimum supported webpack version is v5.0.0 (#3342) (b1af0dc), closes #3342
  • webpack-cli no longer supports webpack v4, the minimum supported version is webpack v5.0.0
  • webpack-cli no longer supports webpack-dev-server v3, the minimum supported version is webpack-dev-server v4.0.0
  • remove the migrate command (#3291) (56b43e4), closes #3291
  • remove the --prefetch option in favor the PrefetchPlugin plugin
  • remove the --node-env option in favor --define-process-env-node-env
  • remove the --hot option in favor of directly using the HotModuleReplacement plugin (only for build command, for serve it will work)
  • the behavior logic of the --entry option has been changed - previously it replaced your entries, now the option adds a specified entry, if you want to return the previous behavior please use webpack --entry-reset --entry './src/my-entry.js'
Changelog

Sourced from webpack-cli's changelog.

5.0.0 (2022-11-17)

Bug Fixes

  • improve description of the --disable-interpret option (#3364) (bdb7e20)
  • remove the redundant utils export (#3343) (a9ce5d0)
  • respect NODE_PATH env variable (#3411) (83d1f58)
  • show all CLI specific flags in the minimum help output (#3354) (35843e8)

Features

  • failOnWarnings option (#3317) (c48c848)
  • update commander to v9 (#3460) (6621c02)
  • added the --define-process-env-node-env option
  • update interpret to v3 and rechoir to v0.8
  • add an option for preventing interpret (#3329) (c737383)

BREAKING CHANGES

  • the minimum supported webpack version is v5.0.0 (#3342) (b1af0dc), closes #3342
  • webpack-cli no longer supports webpack v4, the minimum supported version is webpack v5.0.0
  • webpack-cli no longer supports webpack-dev-server v3, the minimum supported version is webpack-dev-server v4.0.0
  • remove the migrate command (#3291) (56b43e4), closes #3291
  • remove the --prefetch option in favor the PrefetchPlugin plugin
  • remove the --node-env option in favor --define-process-env-node-env
  • remove the --hot option in favor of directly using the HotModuleReplacement plugin (only for build command, for serve it will work)
  • the behavior logic of the --entry option has been changed - previously it replaced your entries, now the option adds a specified entry, if you want to return the previous behavior please use webpack --entry-reset --entry './src/my-entry.js'
Commits
  • 1d6ada1 chore(release): 5.0.0 (#3492)
  • 24334d9 refactor: resolve TODO for devServer.stdin
  • 49b6aea chore: peer deps in root package
  • 636ba3e chore(deps-dev): bump cspell from 6.12.0 to 6.14.2 (#3488)
  • f3016a5 chore(deps-dev): bump eslint from 8.24.0 to 8.27.0 (#3487)
  • 5782242 chore(deps-dev): bump lerna from 6.0.1 to 6.0.3 (#3486)
  • 80eb8c8 chore(deps-dev): bump @​commitlint/config-conventional (#3485)
  • 8ea9020 chore(deps-dev): bump ts-jest from 29.0.1 to 29.0.3 (#3484)
  • 515971a chore(deps-dev): bump css-loader from 6.7.1 to 6.7.2 (#3481)
  • f106109 chore(deps-dev): bump @​typescript-eslint/eslint-plugin
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=webpack-cli&package-manager=npm_and_yarn&previous-version=4.10.0&new-version=5.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- package-lock.json | 165 ++++++++++++++++++++++++---------------------- package.json | 2 +- 2 files changed, 86 insertions(+), 81 deletions(-) diff --git a/package-lock.json b/package-lock.json index d44c7cbadac..d971b884812 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "style-loader": "^3.3.1", "terser-webpack-plugin": "^5.3.6", "webpack": "^5.75.0", - "webpack-cli": "^4.10.0", + "webpack-cli": "^5.0.0", "webpack-dev-server": "^4.11.1" } }, @@ -493,34 +493,42 @@ } }, "node_modules/@webpack-cli/configtest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", - "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.0.0.tgz", + "integrity": "sha512-war4OU8NGjBqU3DP3bx6ciODXIh7dSXcpQq+P4K2Tqyd8L5OjZ7COx9QXx/QdCIwL2qoX09Wr4Cwf7uS4qdEng==", "dev": true, + "engines": { + "node": ">=14.15.0" + }, "peerDependencies": { - "webpack": "4.x.x || 5.x.x", - "webpack-cli": "4.x.x" + "webpack": "5.x.x", + "webpack-cli": "5.x.x" } }, "node_modules/@webpack-cli/info": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", - "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.0.tgz", + "integrity": "sha512-NNxDgbo4VOkNhOlTgY0Elhz3vKpOJq4/PKeKg7r8cmYM+GQA9vDofLYyup8jS6EpUvhNmR30cHTCEIyvXpskwA==", "dev": true, - "dependencies": { - "envinfo": "^7.7.3" + "engines": { + "node": ">=14.15.0" }, "peerDependencies": { - "webpack-cli": "4.x.x" + "webpack": "5.x.x", + "webpack-cli": "5.x.x" } }, "node_modules/@webpack-cli/serve": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", - "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.0.tgz", + "integrity": "sha512-Rumq5mHvGXamnOh3O8yLk1sjx8dB30qF1OeR6VC00DIR6SLJ4bwwUGKC4pE7qBFoQyyh0H9sAg3fikYgAqVR0w==", "dev": true, + "engines": { + "node": ">=14.15.0" + }, "peerDependencies": { - "webpack-cli": "4.x.x" + "webpack": "5.x.x", + "webpack-cli": "5.x.x" }, "peerDependenciesMeta": { "webpack-dev-server": { @@ -2310,12 +2318,12 @@ "dev": true }, "node_modules/interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "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": ">= 0.10" + "node": ">=10.13.0" } }, "node_modules/ipaddr.js": { @@ -2340,9 +2348,9 @@ } }, "node_modules/is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "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" @@ -3432,15 +3440,15 @@ } }, "node_modules/rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "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.9.0" + "resolve": "^1.20.0" }, "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, "node_modules/relateurl": { @@ -4347,44 +4355,42 @@ } }, "node_modules/webpack-cli": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", - "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.0.tgz", + "integrity": "sha512-AACDTo20yG+xn6HPW5xjbn2Be4KUzQPebWXsDMHwPPyKh9OnTOJgZN2Nc+g/FZKV3ObRTYsGvibAvc+5jAUrVA==", "dev": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.2.0", - "@webpack-cli/info": "^1.5.0", - "@webpack-cli/serve": "^1.7.0", + "@webpack-cli/configtest": "^2.0.0", + "@webpack-cli/info": "^2.0.0", + "@webpack-cli/serve": "^2.0.0", "colorette": "^2.0.14", - "commander": "^7.0.0", + "commander": "^9.4.1", "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", - "interpret": "^2.2.0", - "rechoir": "^0.7.0", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", "webpack-merge": "^5.7.3" }, "bin": { "webpack-cli": "bin/cli.js" }, "engines": { - "node": ">=10.13.0" + "node": ">=14.15.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "4.x.x || 5.x.x" + "webpack": "5.x.x" }, "peerDependenciesMeta": { "@webpack-cli/generators": { "optional": true }, - "@webpack-cli/migrate": { - "optional": true - }, "webpack-bundle-analyzer": { "optional": true }, @@ -4394,12 +4400,12 @@ } }, "node_modules/webpack-cli/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "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": ">= 10" + "node": "^12.20.0 || >=14" } }, "node_modules/webpack-dev-middleware": { @@ -5089,25 +5095,23 @@ } }, "@webpack-cli/configtest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", - "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.0.0.tgz", + "integrity": "sha512-war4OU8NGjBqU3DP3bx6ciODXIh7dSXcpQq+P4K2Tqyd8L5OjZ7COx9QXx/QdCIwL2qoX09Wr4Cwf7uS4qdEng==", "dev": true, "requires": {} }, "@webpack-cli/info": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", - "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.0.tgz", + "integrity": "sha512-NNxDgbo4VOkNhOlTgY0Elhz3vKpOJq4/PKeKg7r8cmYM+GQA9vDofLYyup8jS6EpUvhNmR30cHTCEIyvXpskwA==", "dev": true, - "requires": { - "envinfo": "^7.7.3" - } + "requires": {} }, "@webpack-cli/serve": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", - "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.0.tgz", + "integrity": "sha512-Rumq5mHvGXamnOh3O8yLk1sjx8dB30qF1OeR6VC00DIR6SLJ4bwwUGKC4pE7qBFoQyyh0H9sAg3fikYgAqVR0w==", "dev": true, "requires": {} }, @@ -6445,9 +6449,9 @@ "dev": true }, "interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "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": { @@ -6466,9 +6470,9 @@ } }, "is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "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" @@ -7252,12 +7256,12 @@ } }, "rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "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.9.0" + "resolve": "^1.20.0" } }, "relateurl": { @@ -7956,29 +7960,30 @@ } }, "webpack-cli": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", - "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.0.tgz", + "integrity": "sha512-AACDTo20yG+xn6HPW5xjbn2Be4KUzQPebWXsDMHwPPyKh9OnTOJgZN2Nc+g/FZKV3ObRTYsGvibAvc+5jAUrVA==", "dev": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.2.0", - "@webpack-cli/info": "^1.5.0", - "@webpack-cli/serve": "^1.7.0", + "@webpack-cli/configtest": "^2.0.0", + "@webpack-cli/info": "^2.0.0", + "@webpack-cli/serve": "^2.0.0", "colorette": "^2.0.14", - "commander": "^7.0.0", + "commander": "^9.4.1", "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", - "interpret": "^2.2.0", - "rechoir": "^0.7.0", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", "webpack-merge": "^5.7.3" }, "dependencies": { "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "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 } } diff --git a/package.json b/package.json index f212f119db6..96eec506a22 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "style-loader": "^3.3.1", "terser-webpack-plugin": "^5.3.6", "webpack": "^5.75.0", - "webpack-cli": "^4.10.0", + "webpack-cli": "^5.0.0", "webpack-dev-server": "^4.11.1" }, "dependencies": { From e0b08337979fdd31db63a4bc5a2c178f0554b98c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Nov 2022 10:57:37 +0000 Subject: [PATCH 17/24] Bump serde_json from 1.0.87 to 1.0.88 (#2445) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.87 to 1.0.88.
Release notes

Sourced from serde_json's releases.

v1.0.88

  • Optimize serde_json::Map's implementation of append and clone_from (#952, thanks @​Lucretiel)
Commits
  • 9eb66da Release 1.0.88
  • 057957c Merge pull request #952 from Lucretiel/map-traits
  • 3347248 Add clone_from to Map; Map::append now uses IndexMap::extend
  • ca41bdd Update ui test suite to nightly-2022-11-16
  • 4f194c9 Resolve needless_borrow pedantic clippy lint in test
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=serde_json&package-manager=cargo&previous-version=1.0.87&new-version=1.0.88)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- Cargo.lock | 4 ++-- boa_cli/Cargo.toml | 2 +- boa_engine/Cargo.toml | 2 +- boa_tester/Cargo.toml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 308292210b7..311d249ea30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1613,9 +1613,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.87" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +checksum = "8e8b3801309262e8184d9687fb697586833e939767aea0dda89f5a8e650e8bd7" dependencies = [ "itoa", "ryu", diff --git a/boa_cli/Cargo.toml b/boa_cli/Cargo.toml index 286809b4546..774dc865e9e 100644 --- a/boa_cli/Cargo.toml +++ b/boa_cli/Cargo.toml @@ -19,7 +19,7 @@ boa_parser.workspace = true rustyline = "10.0.0" rustyline-derive = "0.7.0" clap = { version = "4.0.26", features = ["derive"] } -serde_json = "1.0.87" +serde_json = "1.0.88" colored = "2.0.0" regex = "1.7.0" phf = { version = "0.11.1", features = ["macros"] } diff --git a/boa_engine/Cargo.toml b/boa_engine/Cargo.toml index 579e3e83946..2b3b9651911 100644 --- a/boa_engine/Cargo.toml +++ b/boa_engine/Cargo.toml @@ -38,7 +38,7 @@ boa_macros.workspace = true boa_ast.workspace = true boa_parser.workspace = true serde = { version = "1.0.147", features = ["derive", "rc"] } -serde_json = "1.0.87" +serde_json = "1.0.88" rand = "0.8.5" num-traits = "0.2.15" regress = "0.4.1" diff --git a/boa_tester/Cargo.toml b/boa_tester/Cargo.toml index d83bf00cf66..9670ae61027 100644 --- a/boa_tester/Cargo.toml +++ b/boa_tester/Cargo.toml @@ -19,7 +19,7 @@ boa_parser.workspace = true clap = { version = "4.0.26", features = ["derive"] } serde = { version = "1.0.147", features = ["derive"] } serde_yaml = "0.9.14" -serde_json = "1.0.87" +serde_json = "1.0.88" bitflags = "1.3.2" regex = "1.7.0" once_cell = "1.16.0" From f617de94dc9158f16af27065447155d890618c62 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Nov 2022 10:57:38 +0000 Subject: [PATCH 18/24] Bump indexmap from 1.9.1 to 1.9.2 (#2446) Bumps [indexmap](https://github.com/bluss/indexmap) from 1.9.1 to 1.9.2.
Changelog

Sourced from indexmap's changelog.

  • 1.9.2

    • IndexMap and IndexSet both implement arbitrary::Arbitrary<'_> and quickcheck::Arbitrary if those optional dependency features are enabled.
Commits
  • 4d52cf3 Merge pull request #247 from cuviper/arbitrary-1.x
  • 4d41050 Release 1.9.2
  • bc1a12f Add an Arbitrary release note
  • 2251812 impl Arbitrary for IndexMap and IndexSet
  • fe98ec2 Revert "Run CI on 1.56.1 until next hashbrown release"
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=indexmap&package-manager=cargo&previous-version=1.9.1&new-version=1.9.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- Cargo.lock | 4 ++-- boa_engine/Cargo.toml | 2 +- boa_interner/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 311d249ea30..e3db049e183 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -948,9 +948,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown", diff --git a/boa_engine/Cargo.toml b/boa_engine/Cargo.toml index 2b3b9651911..07cfd34877e 100644 --- a/boa_engine/Cargo.toml +++ b/boa_engine/Cargo.toml @@ -46,7 +46,7 @@ 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.1" +indexmap = "1.9.2" ryu-js = "0.2.2" chrono = "0.4.23" fast-float = "0.2.0" diff --git a/boa_interner/Cargo.toml b/boa_interner/Cargo.toml index 5f07b9289d3..269d712c37a 100644 --- a/boa_interner/Cargo.toml +++ b/boa_interner/Cargo.toml @@ -20,5 +20,5 @@ phf = { version = "0.11.1", features = ["macros"] } rustc-hash = "1.1.0" static_assertions = "1.1.0" once_cell = "1.16.0" -indexmap = "1.9.1" +indexmap = "1.9.2" arbitrary = { version = "1", optional = true, features = ["derive"] } From a5e8111fa21c2b821aab05571c5d0f3e8f73a6a8 Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Sat, 19 Nov 2022 00:49:49 +0000 Subject: [PATCH 19/24] Restructure lints in multiple crates (#2447) This Pull Request restructures the lint deny/warn/allow lists in almost all crates. `boa_engine` will be done in a follow up PR as the changes there are pretty extensive. --- Cargo.lock | 3 - boa_cli/Cargo.toml | 1 - boa_cli/src/main.rs | 136 +++++++++--------- boa_gc/Cargo.toml | 3 - boa_gc/src/cell.rs | 41 +++--- boa_gc/src/internals/ephemeron_box.rs | 29 +--- boa_gc/src/internals/gc_box.rs | 10 +- boa_gc/src/internals/mod.rs | 5 +- boa_gc/src/lib.rs | 105 ++++++++------ boa_gc/src/pointers/ephemeron.rs | 3 +- boa_gc/src/pointers/gc.rs | 28 ++-- boa_gc/src/test/cell.rs | 3 +- boa_gc/src/test/weak.rs | 3 +- boa_gc/src/trace.rs | 28 ++-- boa_interner/src/interned_str.rs | 2 +- boa_interner/src/lib.rs | 120 +++++++++------- boa_interner/src/raw.rs | 6 +- boa_interner/src/sym.rs | 1 + boa_macros/src/lib.rs | 68 ++++++++- boa_parser/src/error.rs | 67 +++++---- boa_parser/src/lexer/comment.rs | 3 +- boa_parser/src/lexer/cursor.rs | 24 ++-- boa_parser/src/lexer/error.rs | 1 + boa_parser/src/lexer/identifier.rs | 35 ++--- boa_parser/src/lexer/mod.rs | 13 +- boa_parser/src/lexer/number.rs | 10 +- boa_parser/src/lexer/operator.rs | 12 +- boa_parser/src/lexer/private_identifier.rs | 5 +- boa_parser/src/lexer/regex.rs | 18 ++- boa_parser/src/lexer/spread.rs | 7 +- boa_parser/src/lexer/string.rs | 5 +- boa_parser/src/lexer/template.rs | 7 +- boa_parser/src/lexer/tests.rs | 16 +-- boa_parser/src/lexer/token.rs | 30 ++-- boa_parser/src/lib.rs | 81 ++++++----- .../src/parser/cursor/buffered_lexer/mod.rs | 2 +- boa_parser/src/parser/cursor/mod.rs | 49 +++---- .../src/parser/expression/assignment/mod.rs | 3 +- .../expression/left_hand_side/optional/mod.rs | 14 +- boa_parser/src/parser/expression/mod.rs | 2 +- .../primary/array_initializer/mod.rs | 6 +- .../primary/async_function_expression/mod.rs | 7 +- .../primary/async_generator_expression/mod.rs | 7 +- .../primary/function_expression/mod.rs | 7 +- .../primary/generator_expression/mod.rs | 7 +- .../src/parser/expression/primary/mod.rs | 4 +- .../primary/object_initializer/mod.rs | 7 +- boa_parser/src/parser/expression/update.rs | 8 +- boa_parser/src/parser/function/mod.rs | 4 +- boa_parser/src/parser/mod.rs | 9 +- boa_parser/src/parser/statement/block/mod.rs | 6 +- .../src/parser/statement/break_stm/mod.rs | 9 +- .../declaration/hoistable/class_decl/mod.rs | 6 +- .../statement/declaration/hoistable/mod.rs | 7 +- boa_parser/src/parser/statement/if_stm/mod.rs | 7 +- .../statement/iteration/do_while_statement.rs | 10 +- .../src/parser/statement/labelled_stm/mod.rs | 4 +- .../src/parser/statement/return_stm/mod.rs | 9 +- boa_profiler/src/lib.rs | 81 ++++++++++- boa_tester/Cargo.toml | 1 - boa_tester/src/exec/js262.rs | 11 +- boa_tester/src/exec/mod.rs | 6 +- boa_tester/src/main.rs | 121 ++++++++-------- boa_tester/src/read.rs | 12 +- boa_unicode/src/lib.rs | 98 +++++++------ boa_wasm/src/lib.rs | 101 ++++++------- 66 files changed, 834 insertions(+), 720 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e3db049e183..d8127a1b1ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -107,7 +107,6 @@ version = "0.16.0" dependencies = [ "boa_ast", "boa_engine", - "boa_interner", "boa_parser", "clap 4.0.26", "colored", @@ -179,7 +178,6 @@ version = "0.16.0" dependencies = [ "boa_macros", "boa_profiler", - "measureme", ] [[package]] @@ -237,7 +235,6 @@ dependencies = [ "bitflags", "boa_engine", "boa_gc", - "boa_interner", "boa_parser", "clap 4.0.26", "color-eyre", diff --git a/boa_cli/Cargo.toml b/boa_cli/Cargo.toml index 774dc865e9e..38a0901dbc7 100644 --- a/boa_cli/Cargo.toml +++ b/boa_cli/Cargo.toml @@ -14,7 +14,6 @@ rust-version.workspace = true [dependencies] boa_engine = { workspace = true, features = ["deser", "console"] } boa_ast = { workspace = true, features = ["serde"]} -boa_interner.workspace = true boa_parser.workspace = true rustyline = "10.0.0" rustyline-derive = "0.7.0" diff --git a/boa_cli/src/main.rs b/boa_cli/src/main.rs index 82da7c5cf8c..928c4ca7529 100644 --- a/boa_cli/src/main.rs +++ b/boa_cli/src/main.rs @@ -1,63 +1,64 @@ +//! 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( - clippy::perf, - clippy::single_match_else, - clippy::dbg_macro, - clippy::doc_markdown, - clippy::wildcard_imports, - clippy::struct_excessive_bools, - clippy::doc_markdown, - clippy::semicolon_if_nothing_returned, - clippy::pedantic -)] +#![warn(missing_docs, clippy::dbg_macro)] #![deny( - clippy::all, - clippy::cast_lossless, - clippy::redundant_closure_for_method_calls, - clippy::use_self, - clippy::unnested_or_patterns, - clippy::trivially_copy_pass_by_ref, - clippy::needless_pass_by_value, - clippy::match_wildcard_for_single_variants, - clippy::map_unwrap_or, - unused_qualifications, - unused_import_braces, - unused_lifetimes, - unreachable_pub, - trivial_numeric_casts, - // rustdoc, - missing_debug_implementations, - missing_copy_implementations, - deprecated_in_future, - meta_variable_misuse, - non_ascii_idents, - rust_2018_compatibility, - rust_2018_idioms, + // 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::cast_possible_truncation, - clippy::cast_sign_loss, - clippy::cast_precision_loss, - clippy::cast_possible_wrap, - clippy::cast_ptr_alignment, - clippy::missing_panics_doc, - clippy::too_many_lines, - clippy::unreadable_literal, - clippy::missing_inline_in_public_items, - clippy::cognitive_complexity, - clippy::must_use_candidate, - clippy::missing_errors_doc, - clippy::as_conversions, - clippy::let_unit_value, - rustdoc::missing_doc_code_examples -)] +#![allow(clippy::option_if_let_else, clippy::redundant_pub_crate)] + +mod helper; use boa_ast::StatementList; use boa_engine::Context; @@ -65,7 +66,6 @@ use clap::{Parser, ValueEnum, ValueHint}; use colored::{Color, Colorize}; use rustyline::{config::Config, error::ReadlineError, EditMode, Editor}; use std::{fs::read, fs::OpenOptions, io, path::PathBuf}; -mod helper; #[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))] #[cfg_attr( @@ -82,7 +82,6 @@ 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 -#[allow(clippy::option_option)] #[derive(Debug, Parser)] #[command(author, version, about, name = "boa")] struct Opt { @@ -98,6 +97,7 @@ struct Opt { ignore_case = true, value_enum )] + #[allow(clippy::option_option)] dump_ast: Option>, /// Dump the AST to stdout with the given format. @@ -111,7 +111,7 @@ struct Opt { impl Opt { /// Returns whether a dump flag has been used. - fn has_dump_flag(&self) -> bool { + const fn has_dump_flag(&self) -> bool { self.dump_ast.is_some() } } @@ -162,29 +162,23 @@ where let ast = parse_tokens(src, context)?; match arg { - Some(format) => match format { - DumpFormat::Debug => println!("{ast:#?}"), - DumpFormat::Json => println!( - "{}", - serde_json::to_string(&ast).expect("could not convert AST to a JSON string") - ), - DumpFormat::JsonPretty => { - println!( - "{}", - serde_json::to_string_pretty(&ast) - .expect("could not convert AST to a pretty JSON string") - ); - } - }, - // Default ast dumping format. - None => println!("{ast:#?}"), + 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(()) } -pub fn main() -> Result<(), io::Error> { +fn main() -> Result<(), io::Error> { let args = Opt::parse(); let mut context = Context::default(); diff --git a/boa_gc/Cargo.toml b/boa_gc/Cargo.toml index ceaa195cdc6..39f1afa64c1 100644 --- a/boa_gc/Cargo.toml +++ b/boa_gc/Cargo.toml @@ -13,6 +13,3 @@ rust-version.workspace = true [dependencies] boa_profiler.workspace = true boa_macros.workspace = true - -# Optional Dependencies -measureme = { version = "10.1.0", optional = true } diff --git a/boa_gc/src/cell.rs b/boa_gc/src/cell.rs index 36607629347..953f0e4edf9 100644 --- a/boa_gc/src/cell.rs +++ b/boa_gc/src/cell.rs @@ -1,11 +1,13 @@ //! A garbage collected cell implementation -use std::cell::{Cell, UnsafeCell}; -use std::cmp::Ordering; -use std::fmt::{self, Debug, Display}; -use std::hash::Hash; -use std::ops::{Deref, DerefMut}; use crate::trace::{Finalize, Trace}; +use std::{ + cell::{Cell, UnsafeCell}, + cmp::Ordering, + fmt::{self, Debug, Display}, + hash::Hash, + ops::{Deref, DerefMut}, +}; /// `BorrowFlag` represent the internal state of a `GcCell` and /// keeps track of the amount of current borrows. @@ -34,7 +36,7 @@ pub(crate) const BORROWFLAG_INIT: BorrowFlag = BorrowFlag(ROOT); impl BorrowFlag { /// Check the current `BorrowState` of `BorrowFlag`. #[inline] - pub(crate) fn borrowed(self) -> BorrowState { + pub(crate) const fn borrowed(self) -> BorrowState { match self.0 & !ROOT { UNUSED => BorrowState::Unused, WRITING => BorrowState::Writing, @@ -44,20 +46,20 @@ impl BorrowFlag { /// Check whether the borrow bit is flagged. #[inline] - pub(crate) fn rooted(self) -> bool { + pub(crate) const fn rooted(self) -> bool { self.0 & ROOT > 0 } /// Set the `BorrowFlag`'s state to writing. #[inline] - pub(crate) fn set_writing(self) -> Self { + pub(crate) const fn set_writing(self) -> Self { // Set every bit other than the root bit, which is preserved Self(self.0 | WRITING) } /// Remove the root flag on `BorrowFlag` #[inline] - pub(crate) fn set_unused(self) -> Self { + pub(crate) const fn set_unused(self) -> Self { // Clear every bit other than the root bit, which is preserved Self(self.0 & ROOT) } @@ -130,7 +132,7 @@ pub struct GcCell { impl GcCell { /// Creates a new `GcCell` containing `value`. #[inline] - pub fn new(value: T) -> Self { + pub const fn new(value: T) -> Self { Self { flags: Cell::new(BORROWFLAG_INIT), cell: UnsafeCell::new(value), @@ -402,7 +404,7 @@ impl<'a, T: ?Sized> GcCellRef<'a, T> { } } -impl<'a, T: ?Sized> Deref for GcCellRef<'a, T> { +impl Deref for GcCellRef<'_, T> { type Target = T; #[inline] @@ -411,20 +413,20 @@ impl<'a, T: ?Sized> Deref for GcCellRef<'a, T> { } } -impl<'a, T: ?Sized> Drop for GcCellRef<'a, T> { +impl Drop for GcCellRef<'_, T> { fn drop(&mut self) { debug_assert!(self.flags.get().borrowed() == BorrowState::Reading); self.flags.set(self.flags.get().sub_reading()); } } -impl<'a, T: ?Sized + Debug> Debug for GcCellRef<'a, T> { +impl Debug for GcCellRef<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Debug::fmt(&**self, f) } } -impl<'a, T: ?Sized + Display> Display for GcCellRef<'a, T> { +impl Display for GcCellRef<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(&**self, f) } @@ -452,6 +454,7 @@ impl<'a, T: Trace + ?Sized, U: ?Sized> GcCellRefMut<'a, T, U> { F: FnOnce(&mut U) -> &mut V, { // SAFETY: This is safe as `GcCellRefMut` is already borrowed, so the value is rooted. + #[allow(trivial_casts)] let value = unsafe { &mut *(orig.value as *mut U) }; let ret = GcCellRefMut { @@ -467,7 +470,7 @@ impl<'a, T: Trace + ?Sized, U: ?Sized> GcCellRefMut<'a, T, U> { } } -impl<'a, T: Trace + ?Sized, U: ?Sized> Deref for GcCellRefMut<'a, T, U> { +impl Deref for GcCellRefMut<'_, T, U> { type Target = U; #[inline] @@ -476,14 +479,14 @@ impl<'a, T: Trace + ?Sized, U: ?Sized> Deref for GcCellRefMut<'a, T, U> { } } -impl<'a, T: Trace + ?Sized, U: ?Sized> DerefMut for GcCellRefMut<'a, T, U> { +impl DerefMut for GcCellRefMut<'_, T, U> { #[inline] fn deref_mut(&mut self) -> &mut U { self.value } } -impl<'a, T: Trace + ?Sized, U: ?Sized> Drop for GcCellRefMut<'a, T, U> { +impl Drop for GcCellRefMut<'_, T, U> { #[inline] fn drop(&mut self) { debug_assert!(self.gc_cell.flags.get().borrowed() == BorrowState::Writing); @@ -502,13 +505,13 @@ impl<'a, T: Trace + ?Sized, U: ?Sized> Drop for GcCellRefMut<'a, T, U> { } } -impl<'a, T: Trace + ?Sized, U: Debug + ?Sized> Debug for GcCellRefMut<'a, T, U> { +impl Debug for GcCellRefMut<'_, T, U> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Debug::fmt(&**self, f) } } -impl<'a, T: Trace + ?Sized, U: Display + ?Sized> Display for GcCellRefMut<'a, T, U> { +impl Display for GcCellRefMut<'_, T, U> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(&**self, f) } diff --git a/boa_gc/src/internals/ephemeron_box.rs b/boa_gc/src/internals/ephemeron_box.rs index 420b7fb36c6..0056736eef6 100644 --- a/boa_gc/src/internals/ephemeron_box.rs +++ b/boa_gc/src/internals/ephemeron_box.rs @@ -1,8 +1,5 @@ -use crate::trace::Trace; -use crate::{finalizer_safe, GcBox}; -use crate::{Finalize, Gc}; -use std::cell::Cell; -use std::ptr::NonNull; +use crate::{finalizer_safe, trace::Trace, Finalize, Gc, GcBox}; +use std::{cell::Cell, ptr::NonNull}; /// The inner allocation of an [`Ephemeron`][crate::Ephemeron] pointer. pub(crate) struct EphemeronBox { @@ -23,11 +20,7 @@ impl EphemeronBox { /// Checks if the key pointer is marked by Trace #[inline] pub(crate) fn is_marked(&self) -> bool { - if let Some(key) = self.inner_key() { - key.is_marked() - } else { - false - } + self.inner_key().map_or(false, GcBox::is_marked) } /// Returns some pointer to the `key`'s `GcBox` or None @@ -46,28 +39,18 @@ impl EphemeronBox { // fetch either a live `GcBox` or None. The value of `key` is set // to None in the case where `EphemeronBox` and `key`'s `GcBox` // entered into `Collector::sweep()` as unmarked. - unsafe { - if let Some(inner_key) = self.inner_key_ptr() { - Some(&*inner_key) - } else { - None - } - } + unsafe { self.inner_key_ptr().map(|inner_key| &*inner_key) } } /// Returns a reference to the value of `key`'s `GcBox` #[inline] pub(crate) fn key(&self) -> Option<&K> { - if let Some(key_box) = self.inner_key() { - Some(key_box.value()) - } else { - None - } + self.inner_key().map(GcBox::value) } /// Returns a reference to `value` #[inline] - pub(crate) fn value(&self) -> &V { + pub(crate) const fn value(&self) -> &V { &self.value } diff --git a/boa_gc/src/internals/gc_box.rs b/boa_gc/src/internals/gc_box.rs index eaca4b48f79..9c5ac998d9b 100644 --- a/boa_gc/src/internals/gc_box.rs +++ b/boa_gc/src/internals/gc_box.rs @@ -1,7 +1,9 @@ use crate::Trace; -use std::cell::Cell; -use std::fmt; -use std::ptr::{self, NonNull}; +use std::{ + cell::Cell, + fmt, + ptr::{self, NonNull}, +}; // Age and Weak Flags const MARK_MASK: usize = 1 << (usize::BITS - 2); @@ -183,7 +185,7 @@ impl GcBox { /// Returns a reference to the `GcBox`'s value. #[inline] - pub(crate) fn value(&self) -> &T { + pub(crate) const fn value(&self) -> &T { &self.value } diff --git a/boa_gc/src/internals/mod.rs b/boa_gc/src/internals/mod.rs index 005c000ade5..7539365723b 100644 --- a/boa_gc/src/internals/mod.rs +++ b/boa_gc/src/internals/mod.rs @@ -1,5 +1,4 @@ mod ephemeron_box; -pub(crate) use ephemeron_box::EphemeronBox; - mod gc_box; -pub(crate) use gc_box::GcBox; + +pub(crate) use self::{ephemeron_box::EphemeronBox, gc_box::GcBox}; diff --git a/boa_gc/src/lib.rs b/boa_gc/src/lib.rs index 4b26855ea85..6db1b8d614f 100644 --- a/boa_gc/src/lib.rs +++ b/boa_gc/src/lib.rs @@ -1,69 +1,84 @@ //! Garbage collector for the Boa JavaScript engine. -#![warn( - clippy::perf, - clippy::single_match_else, - clippy::dbg_macro, - clippy::doc_markdown, - clippy::wildcard_imports, - clippy::struct_excessive_bools, - clippy::doc_markdown, - clippy::semicolon_if_nothing_returned, - clippy::pedantic -)] +#![cfg_attr(not(test), forbid(clippy::unwrap_used))] +#![warn(missing_docs, clippy::dbg_macro)] #![deny( - clippy::all, - clippy::cast_lossless, - clippy::redundant_closure_for_method_calls, - clippy::use_self, - clippy::unnested_or_patterns, - clippy::trivially_copy_pass_by_ref, - clippy::needless_pass_by_value, - clippy::match_wildcard_for_single_variants, - clippy::map_unwrap_or, - clippy::undocumented_unsafe_blocks, - clippy::missing_safety_doc, + // 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_qualifications, + unused_crate_dependencies, unused_import_braces, unused_lifetimes, - unreachable_pub, - trivial_numeric_casts, + unused_qualifications, + unused_tuple_struct_fields, + variant_size_differences, + + // rustdoc lints https://doc.rust-lang.org/rustdoc/lints.html rustdoc::broken_intra_doc_links, - missing_debug_implementations, - missing_copy_implementations, - deprecated_in_future, - meta_variable_misuse, - non_ascii_idents, - rust_2018_compatibility, - rust_2018_idioms, - future_incompatible, - nonstandard_style, - missing_docs + 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::redundant_pub_crate, + clippy::let_unit_value )] -#![allow(clippy::let_unit_value, clippy::module_name_repetitions)] extern crate self as boa_gc; -use boa_profiler::Profiler; -use std::cell::{Cell, RefCell}; -use std::mem; -use std::ptr::NonNull; - +mod cell; +mod pointers; mod trace; pub(crate) mod internals; -mod cell; -mod pointers; +use boa_profiler::Profiler; +use internals::GcBox; +use std::{ + cell::{Cell, RefCell}, + mem, + ptr::NonNull, +}; pub use crate::trace::{Finalize, Trace}; pub use boa_macros::{Finalize, Trace}; pub use cell::{GcCell, GcCellRef, GcCellRefMut}; pub use pointers::{Ephemeron, Gc, WeakGc}; -use internals::GcBox; - type GcPointer = NonNull>; thread_local!(static EPHEMERON_QUEUE: Cell>> = Cell::new(None)); diff --git a/boa_gc/src/pointers/ephemeron.rs b/boa_gc/src/pointers/ephemeron.rs index 35702a7b119..db21d28c539 100644 --- a/boa_gc/src/pointers/ephemeron.rs +++ b/boa_gc/src/pointers/ephemeron.rs @@ -4,8 +4,7 @@ use crate::{ trace::{Finalize, Trace}, Allocator, Gc, GcBox, EPHEMERON_QUEUE, }; -use std::cell::Cell; -use std::ptr::NonNull; +use std::{cell::Cell, ptr::NonNull}; #[derive(Debug)] /// A key-value pair where the value becomes unaccesible when the key is garbage collected. diff --git a/boa_gc/src/pointers/gc.rs b/boa_gc/src/pointers/gc.rs index df44a8b327e..2dc9f275fae 100644 --- a/boa_gc/src/pointers/gc.rs +++ b/boa_gc/src/pointers/gc.rs @@ -1,15 +1,19 @@ -use std::cell::Cell; -use std::cmp::Ordering; -use std::fmt::{self, Debug, Display}; -use std::hash::{Hash, Hasher}; -use std::marker::PhantomData; -use std::ops::Deref; -use std::ptr::{self, addr_of_mut, NonNull}; -use std::rc::Rc; - -use crate::internals::GcBox; -use crate::trace::{Finalize, Trace}; -use crate::{finalizer_safe, Allocator}; +use crate::{ + finalizer_safe, + internals::GcBox, + trace::{Finalize, Trace}, + Allocator, +}; +use std::{ + cell::Cell, + cmp::Ordering, + fmt::{self, Debug, Display}, + hash::{Hash, Hasher}, + marker::PhantomData, + ops::Deref, + ptr::{self, addr_of_mut, NonNull}, + rc::Rc, +}; // Technically, this function is safe, since we're just modifying the address of a pointer without // dereferencing it. diff --git a/boa_gc/src/test/cell.rs b/boa_gc/src/test/cell.rs index 1e82acf1e7c..3c067fd3cb6 100644 --- a/boa_gc/src/test/cell.rs +++ b/boa_gc/src/test/cell.rs @@ -1,6 +1,5 @@ -use boa_gc::{Gc, GcCell}; - use super::run_test; +use crate::{Gc, GcCell}; #[test] fn boa_borrow_mut_test() { diff --git a/boa_gc/src/test/weak.rs b/boa_gc/src/test/weak.rs index 5f1e33ccc19..709c03edbb5 100644 --- a/boa_gc/src/test/weak.rs +++ b/boa_gc/src/test/weak.rs @@ -1,6 +1,5 @@ -use boa_gc::{force_collect, Ephemeron, Gc, WeakGc}; - use super::run_test; +use crate::{force_collect, Ephemeron, Gc, WeakGc}; #[test] fn eph_weak_gc_test() { diff --git a/boa_gc/src/trace.rs b/boa_gc/src/trace.rs index 354a0119d40..06512fcaa7c 100644 --- a/boa_gc/src/trace.rs +++ b/boa_gc/src/trace.rs @@ -1,16 +1,18 @@ -use std::borrow::{Cow, ToOwned}; -use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; -use std::hash::{BuildHasher, Hash}; -use std::marker::PhantomData; -use std::num::{ - NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, - NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, -}; -use std::path::{Path, PathBuf}; -use std::rc::Rc; -use std::sync::atomic::{ - AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, - AtomicU64, AtomicU8, AtomicUsize, +use std::{ + borrow::{Cow, ToOwned}, + collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}, + hash::{BuildHasher, Hash}, + marker::PhantomData, + num::{ + NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, + NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, + }, + path::{Path, PathBuf}, + rc::Rc, + sync::atomic::{ + AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, + AtomicU64, AtomicU8, AtomicUsize, + }, }; /// Substitute for the [`Drop`] trait for garbage collected types. diff --git a/boa_interner/src/interned_str.rs b/boa_interner/src/interned_str.rs index 7f5654a22cf..e534914ff56 100644 --- a/boa_interner/src/interned_str.rs +++ b/boa_interner/src/interned_str.rs @@ -27,7 +27,7 @@ impl InternedStr { /// Not maintaining the invariants specified on the struct definition /// could cause Undefined Behaviour. #[inline] - pub(super) unsafe fn new(ptr: NonNull<[Char]>) -> Self { + pub(super) const unsafe fn new(ptr: NonNull<[Char]>) -> Self { Self { ptr } } diff --git a/boa_interner/src/lib.rs b/boa_interner/src/lib.rs index 112b3cd7e71..ebcdd4d7d92 100644 --- a/boa_interner/src/lib.rs +++ b/boa_interner/src/lib.rs @@ -13,62 +13,60 @@ html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg" )] #![cfg_attr(not(test), forbid(clippy::unwrap_used))] -#![warn( - clippy::perf, - clippy::single_match_else, - clippy::dbg_macro, - clippy::doc_markdown, - clippy::wildcard_imports, - clippy::struct_excessive_bools, - clippy::doc_markdown, - clippy::semicolon_if_nothing_returned, - clippy::pedantic -)] +#![warn(missing_docs, clippy::dbg_macro)] #![deny( - clippy::all, - clippy::cast_lossless, - clippy::redundant_closure_for_method_calls, - clippy::use_self, - clippy::unnested_or_patterns, - clippy::trivially_copy_pass_by_ref, - clippy::needless_pass_by_value, - clippy::match_wildcard_for_single_variants, - clippy::map_unwrap_or, - unused_qualifications, - unused_import_braces, - unused_lifetimes, - unreachable_pub, - trivial_numeric_casts, - // rustdoc, - missing_debug_implementations, - missing_copy_implementations, - deprecated_in_future, - meta_variable_misuse, - non_ascii_idents, - rust_2018_compatibility, - rust_2018_idioms, + // rustc lint groups https://doc.rust-lang.org/rustc/lints/groups.html + warnings, future_incompatible, + let_underscore, nonstandard_style, - unsafe_op_in_unsafe_fn + 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, + 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::cast_possible_truncation, - clippy::cast_sign_loss, - clippy::cast_precision_loss, - clippy::cast_possible_wrap, - clippy::cast_ptr_alignment, - clippy::missing_panics_doc, - clippy::too_many_lines, - clippy::unreadable_literal, - clippy::missing_inline_in_public_items, - clippy::cognitive_complexity, - clippy::must_use_candidate, - clippy::missing_errors_doc, - clippy::as_conversions, - clippy::let_unit_value, + clippy::redundant_pub_crate, // TODO deny once false positive is fixed (https://github.com/rust-lang/rust-clippy/issues/9626). - clippy::trait_duplication_in_bounds, + clippy::trait_duplication_in_bounds )] extern crate static_assertions as sa; @@ -77,12 +75,13 @@ mod fixed_string; mod interned_str; mod raw; mod sym; + #[cfg(test)] mod tests; +use raw::RawInterner; use std::borrow::Cow; -use raw::RawInterner; pub use sym::*; /// An enumeration of all slice types [`Interner`] can internally store. @@ -91,7 +90,10 @@ pub use sym::*; /// encodings [`Interner`] can store. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum JStrRef<'a> { + /// A `UTF-8` string reference. Utf8(&'a str), + + /// A `UTF-16` string reference. Utf16(&'a [u16]), } @@ -128,12 +130,16 @@ pub struct JSInternedStrRef<'a, 'b> { impl<'a, 'b> JSInternedStrRef<'a, 'b> { /// Returns the inner reference to the interned string in `UTF-8` encoding. /// if the string is not representable in `UTF-8`, returns [`None`] - pub fn utf8(&self) -> Option<&'a str> { + #[inline] + #[must_use] + pub const fn utf8(&self) -> Option<&'a str> { self.utf8 } /// Returns the inner reference to the interned string in `UTF-16` encoding. - pub fn utf16(&self) -> &'b [u16] { + #[inline] + #[must_use] + pub const fn utf16(&self) -> &'b [u16] { self.utf16 } @@ -186,7 +192,7 @@ impl<'a, 'b> JSInternedStrRef<'a, 'b> { } } -impl<'a, 'b> std::fmt::Display for JSInternedStrRef<'a, 'b> { +impl std::fmt::Display for JSInternedStrRef<'_, '_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.join_with_context( std::fmt::Display::fmt, @@ -215,12 +221,14 @@ pub struct Interner { impl Interner { /// Creates a new [`Interner`]. #[inline] + #[must_use] pub fn new() -> Self { Self::default() } /// Creates a new [`Interner`] with the specified capacity. #[inline] + #[must_use] pub fn with_capacity(capacity: usize) -> Self { Self { utf8_interner: RawInterner::with_capacity(capacity), @@ -230,6 +238,7 @@ impl Interner { /// Returns the number of strings interned by the interner. #[inline] + #[must_use] pub fn len(&self) -> usize { // `utf16_interner.len()` == `utf8_interner.len()`, // so we can use any of them. @@ -238,6 +247,7 @@ impl Interner { /// Returns `true` if the [`Interner`] contains no interned strings. #[inline] + #[must_use] pub fn is_empty(&self) -> bool { COMMON_STRINGS_UTF8.is_empty() && self.utf16_interner.is_empty() } @@ -342,6 +352,7 @@ impl Interner { /// Returns the string for the given symbol if any. #[inline] + #[must_use] pub fn resolve(&self, symbol: Sym) -> Option> { let index = symbol.get() - 1; @@ -385,6 +396,7 @@ impl Interner { /// /// If the interner cannot resolve the given symbol. #[inline] + #[must_use] pub fn resolve_expect(&self, symbol: Sym) -> JSInternedStrRef<'_, '_> { self.resolve(symbol).expect("string disappeared") } diff --git a/boa_interner/src/raw.rs b/boa_interner/src/raw.rs index 9604f9bff77..2fed0be3999 100644 --- a/boa_interner/src/raw.rs +++ b/boa_interner/src/raw.rs @@ -1,8 +1,6 @@ -use std::hash::Hash; - -use rustc_hash::FxHashMap; - use crate::{fixed_string::FixedString, interned_str::InternedStr}; +use rustc_hash::FxHashMap; +use std::hash::Hash; /// Raw string interner, generic by a char type. #[derive(Debug)] diff --git a/boa_interner/src/sym.rs b/boa_interner/src/sym.rs index 8cf8385bef0..32114070f58 100644 --- a/boa_interner/src/sym.rs +++ b/boa_interner/src/sym.rs @@ -131,6 +131,7 @@ impl Sym { /// Returns the internal value of the [`Sym`] #[inline] + #[must_use] pub const fn get(self) -> usize { self.value.get() } diff --git a/boa_macros/src/lib.rs b/boa_macros/src/lib.rs index c1fc87fa6dd..6525e7665c5 100644 --- a/boa_macros/src/lib.rs +++ b/boa_macros/src/lib.rs @@ -1,3 +1,58 @@ +//! Macros for the Boa JavaScript engine. + +#![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, +)] + use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, LitStr}; @@ -15,7 +70,11 @@ pub fn utf16(input: TokenStream) -> TokenStream { .into() } -decl_derive!([Trace, attributes(unsafe_ignore_trace)] => derive_trace); +decl_derive! { + [Trace, attributes(unsafe_ignore_trace)] => + /// Derive the Trace trait. + derive_trace +} fn derive_trace(mut s: Structure<'_>) -> proc_macro2::TokenStream { s.filter(|bi| { @@ -109,8 +168,13 @@ fn derive_trace(mut s: Structure<'_>) -> proc_macro2::TokenStream { } } -decl_derive!([Finalize] => derive_finalize); +decl_derive! { + [Finalize] => + /// Derive the Finalize trait. + derive_finalize +} +#[allow(clippy::needless_pass_by_value)] fn derive_finalize(s: Structure<'_>) -> proc_macro2::TokenStream { s.unbound_impl(quote!(::boa_gc::Finalize), quote!()) } diff --git a/boa_parser/src/error.rs b/boa_parser/src/error.rs index 324ffc03625..336bff5ca9d 100644 --- a/boa_parser/src/error.rs +++ b/boa_parser/src/error.rs @@ -1,7 +1,6 @@ //! Error and result implementation for the parser. use crate::lexer::Error as LexError; - use boa_ast::{Position, Span}; use std::fmt; @@ -26,34 +25,51 @@ impl From for Error { } } -/// An enum which represents errors encounted during parsing an expression +/// An enum which represents errors encountered during parsing an expression #[derive(Debug)] pub enum Error { /// When it expected a certain kind of token, but got another as part of something Expected { + /// The token(s) that were expected. expected: Box<[String]>, + + /// The token that was not expected. found: Box, - span: Span, + + /// The parsing context in which the error occurred. context: &'static str, + + /// Position of the source code where the error occurred. + span: Span, }, + /// When a token is unexpected Unexpected { + /// The error message. + message: Option<&'static str>, + + /// The token that was not expected. found: Box, + + /// Position of the source code where the error occurred. span: Span, - message: Option<&'static str>, }, + /// When there is an abrupt end to the parsing AbruptEnd, + /// A lexing error. - Lex { err: LexError }, + Lex { + /// The error that occurred during lexing. + err: LexError, + }, + /// Catch all General Error General { + /// The error message. message: &'static str, - position: Position, - }, - /// Unimplemented syntax error - Unimplemented { - message: &'static str, + + /// Position of the source code where the error occurred. position: Position, }, } @@ -73,6 +89,7 @@ impl Error { } /// Creates an `Expected` parsing error. + #[inline] pub(crate) fn expected(expected: E, found: F, span: Span, context: &'static str) -> Self where E: Into>, @@ -87,6 +104,7 @@ impl Error { } /// Creates an `Expected` parsing error. + #[inline] pub(crate) fn unexpected(found: F, span: Span, message: C) -> Self where F: Into>, @@ -100,12 +118,14 @@ impl Error { } /// Creates a "general" parsing error. - pub(crate) fn general(message: &'static str, position: Position) -> Self { + #[inline] + pub(crate) const fn general(message: &'static str, position: Position) -> Self { Self::General { message, position } } /// Creates a "general" parsing error with the specific error message for a wrong function declaration in non-strict mode. - pub(crate) fn wrong_function_declaration_non_strict(position: Position) -> Self { + #[inline] + pub(crate) const fn wrong_function_declaration_non_strict(position: Position) -> Self { Self::General { message: "In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement.", position @@ -114,7 +134,7 @@ impl Error { /// Creates a "general" parsing error with the specific error message for a wrong function declaration with label. #[inline] - pub(crate) fn wrong_labelled_function_declaration(position: Position) -> Self { + pub(crate) const fn wrong_labelled_function_declaration(position: Position) -> Self { Self::General { message: "Labelled functions can only be declared at top level or inside a block", position, @@ -122,15 +142,10 @@ impl Error { } /// Creates a parsing error from a lexing error. - pub(crate) fn lex(e: LexError) -> Self { + #[inline] + pub(crate) const fn lex(e: LexError) -> Self { Self::Lex { err: e } } - - /// Creates a new `Unimplemented` parsing error. - #[allow(dead_code)] - pub(crate) fn unimplemented(message: &'static str, position: Position) -> Self { - Self::Unimplemented { message, position } - } } impl fmt::Display for Error { @@ -180,11 +195,7 @@ impl fmt::Display for Error { } => write!( f, "unexpected token '{found}'{} at line {}, col {}", - if let Some(m) = message { - format!(", {m}") - } else { - String::new() - }, + message.map_or_else(String::new, |m| format!(", {m}")), span.start().line_number(), span.start().column_number() ), @@ -196,12 +207,6 @@ impl fmt::Display for Error { position.column_number() ), Self::Lex { err } => fmt::Display::fmt(err, f), - Self::Unimplemented { message, position } => write!( - f, - "{message} not yet implemented at line {}, col {}", - position.line_number(), - position.column_number() - ), } } } diff --git a/boa_parser/src/lexer/comment.rs b/boa_parser/src/lexer/comment.rs index 127c6932ef3..b5db8a5b32b 100644 --- a/boa_parser/src/lexer/comment.rs +++ b/boa_parser/src/lexer/comment.rs @@ -1,7 +1,6 @@ //! This module implements lexing for comments used in the JavaScript programing language. -use super::{Cursor, Error, Tokenizer}; -use crate::lexer::{Token, TokenKind}; +use crate::lexer::{Cursor, Error, Token, TokenKind, Tokenizer}; use boa_ast::{Position, Span}; use boa_interner::Interner; use boa_profiler::Profiler; diff --git a/boa_parser/src/lexer/cursor.rs b/boa_parser/src/lexer/cursor.rs index 3359e621153..5dd890f8388 100644 --- a/boa_parser/src/lexer/cursor.rs +++ b/boa_parser/src/lexer/cursor.rs @@ -14,9 +14,10 @@ pub(super) struct Cursor { impl Cursor { /// Gets the current position of the cursor in the source code. #[inline] - pub(super) fn pos(&self) -> Position { + pub(super) const fn pos(&self) -> Position { self.pos } + /// Advances the position to the next column. #[inline] pub(super) fn next_column(&mut self) { @@ -34,7 +35,7 @@ impl Cursor { #[inline] /// Returns if strict mode is currently active. - pub(super) fn strict_mode(&self) -> bool { + pub(super) const fn strict_mode(&self) -> bool { self.strict_mode } @@ -120,11 +121,8 @@ where let _timer = Profiler::global().start_event("cursor::next_is_ascii_pred()", "Lexing"); Ok(match self.peek()? { - Some(byte) => match byte { - 0..=0x7F => pred(char::from(byte)), - _ => false, - }, - None => false, + Some(byte) if (0..=0x7F).contains(&byte) => pred(char::from(byte)), + Some(_) | None => false, }) } @@ -141,11 +139,7 @@ where { let _timer = Profiler::global().start_event("cursor::next_is_char_pred()", "Lexing"); - Ok(if let Some(peek) = self.peek_char()? { - pred(peek) - } else { - false - }) + Ok(self.peek_char()?.map_or(false, pred)) } /// Fills the buffer with all bytes until the stop byte is found. @@ -303,7 +297,7 @@ struct InnerIter { impl InnerIter { /// Creates a new inner iterator. #[inline] - fn new(iter: Bytes) -> Self { + const fn new(iter: Bytes) -> Self { Self { iter, num_peeked_bytes: 0, @@ -503,7 +497,7 @@ fn utf8_acc_cont_byte(ch: u32, byte: u8) -> u32 { /// Checks whether the byte is a UTF-8 first byte (i.e., ascii byte or starts with the /// bits `11`). #[inline] -fn utf8_is_first_byte(byte: u8) -> bool { +const fn utf8_is_first_byte(byte: u8) -> bool { byte <= 0x7F || (byte >> 6) == 0x11 } @@ -513,7 +507,7 @@ fn unwrap_or_0(opt: Option) -> u8 { } #[inline] -fn utf8_len(ch: u32) -> u32 { +const fn utf8_len(ch: u32) -> u32 { if ch <= 0x7F { 1 } else if ch <= 0x7FF { diff --git a/boa_parser/src/lexer/error.rs b/boa_parser/src/lexer/error.rs index d24044aa9a2..7221a43314f 100644 --- a/boa_parser/src/lexer/error.rs +++ b/boa_parser/src/lexer/error.rs @@ -8,6 +8,7 @@ use boa_ast::Position; use std::{error::Error as StdError, fmt, io}; +/// An error that occurred during the lexing. #[derive(Debug)] pub enum Error { /// An IO error is raised to indicate an issue when the lexer is reading data that isn't diff --git a/boa_parser/src/lexer/identifier.rs b/boa_parser/src/lexer/identifier.rs index 6a63cc78e35..d66038d3061 100644 --- a/boa_parser/src/lexer/identifier.rs +++ b/boa_parser/src/lexer/identifier.rs @@ -1,7 +1,6 @@ //! This module implements lexing for identifiers (foo, myvar, etc.) used in the JavaScript programing language. -use super::{Cursor, Error, Tokenizer}; -use crate::lexer::{StringLiteral, Token, TokenKind}; +use crate::lexer::{Cursor, Error, StringLiteral, Token, TokenKind, Tokenizer}; use boa_ast::{Keyword, Position, Span}; use boa_interner::Interner; use boa_profiler::Profiler; @@ -23,7 +22,8 @@ pub(super) struct Identifier { impl Identifier { /// Creates a new identifier/keyword lexer. - pub(super) fn new(init: char) -> Self { + #[inline] + pub(super) const fn new(init: char) -> Self { Self { init } } @@ -33,13 +33,10 @@ impl Identifier { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-names-and-keywords + #[inline] pub(super) fn is_identifier_start(ch: u32) -> bool { matches!(ch, 0x0024 /* $ */ | 0x005F /* _ */) - || if let Ok(ch) = char::try_from(ch) { - ch.is_id_start() - } else { - false - } + || char::try_from(ch).map_or(false, char::is_id_start) } /// Checks if a character is `IdentifierPart` as per ECMAScript standards. @@ -48,15 +45,12 @@ impl Identifier { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-names-and-keywords + #[inline] fn is_identifier_part(ch: u32) -> bool { matches!( ch, 0x0024 /* $ */ | 0x005F /* _ */ | 0x200C /* */ | 0x200D /* */ - ) || if let Ok(ch) = char::try_from(ch) { - ch.is_id_continue() - } else { - false - } + ) || char::try_from(ch).map_or(false, char::is_id_continue) } } @@ -75,15 +69,12 @@ impl Tokenizer for Identifier { let (identifier_name, contains_escaped_chars) = Self::take_identifier_name(cursor, start_pos, self.init)?; - let token_kind = if let Ok(keyword) = identifier_name.parse() { - match keyword { - Keyword::True => TokenKind::BooleanLiteral(true), - Keyword::False => TokenKind::BooleanLiteral(false), - Keyword::Null => TokenKind::NullLiteral, - _ => TokenKind::Keyword((keyword, contains_escaped_chars)), - } - } else { - TokenKind::identifier(interner.get_or_intern(identifier_name.as_str())) + let token_kind = match identifier_name.parse() { + Ok(Keyword::True) => TokenKind::BooleanLiteral(true), + Ok(Keyword::False) => TokenKind::BooleanLiteral(false), + Ok(Keyword::Null) => TokenKind::NullLiteral, + Ok(keyword) => TokenKind::Keyword((keyword, contains_escaped_chars)), + _ => TokenKind::identifier(interner.get_or_intern(identifier_name.as_str())), }; Ok(Token::new(token_kind, Span::new(start_pos, cursor.pos()))) diff --git a/boa_parser/src/lexer/mod.rs b/boa_parser/src/lexer/mod.rs index eb7aaa2908c..7c2c17adae0 100644 --- a/boa_parser/src/lexer/mod.rs +++ b/boa_parser/src/lexer/mod.rs @@ -14,18 +14,19 @@ //! //! [spec]: https://tc39.es/ecma262/#sec-ecmascript-language-lexical-grammar +pub mod error; +pub mod regex; +pub mod token; + mod comment; mod cursor; -pub mod error; mod identifier; mod number; mod operator; mod private_identifier; -pub mod regex; mod spread; mod string; mod template; -pub mod token; #[cfg(test)] mod tests; @@ -80,7 +81,7 @@ impl Lexer { /// * ECMAScript standard uses `\{Space_Separator}` + `\u{0009}`, `\u{000B}`, `\u{000C}`, `\u{FEFF}` /// /// [More information](https://tc39.es/ecma262/#table-32) - fn is_whitespace(ch: u32) -> bool { + const fn is_whitespace(ch: u32) -> bool { matches!( ch, 0x0020 | 0x0009 | 0x000B | 0x000C | 0x00A0 | 0xFEFF | @@ -97,13 +98,13 @@ impl Lexer { /// Gets the goal symbol the lexer is currently using. #[inline] - pub(crate) fn get_goal(&self) -> InputElement { + pub(crate) const fn get_goal(&self) -> InputElement { self.goal_symbol } #[inline] /// Returns if strict mode is currently active. - pub(super) fn strict_mode(&self) -> bool { + pub(super) const fn strict_mode(&self) -> bool { self.cursor.strict_mode() } diff --git a/boa_parser/src/lexer/number.rs b/boa_parser/src/lexer/number.rs index fddaf9cc1e8..82127aeb022 100644 --- a/boa_parser/src/lexer/number.rs +++ b/boa_parser/src/lexer/number.rs @@ -25,7 +25,7 @@ pub(super) struct NumberLiteral { impl NumberLiteral { /// Creates a new string literal lexer. - pub(super) fn new(init: u8) -> Self { + pub(super) const fn new(init: u8) -> Self { Self { init } } } @@ -42,7 +42,7 @@ enum NumericKind { impl NumericKind { /// Get the base of the number kind. - fn base(self) -> u32 { + const fn base(self) -> u32 { match self { Self::Rational => 10, Self::Integer(base) | Self::BigInt(base) => base, @@ -402,12 +402,10 @@ impl Tokenizer for NumberLiteral { } }, NumericKind::Integer(base) => { - if let Ok(num) = i32::from_str_radix(num_str, base) { - Numeric::Integer(num) - } else { + i32::from_str_radix(num_str, base).map_or_else(|_| { let num = BigInt::parse_bytes(num_str.as_bytes(), base).expect("Failed to parse integer after checks"); Numeric::Rational(num.to_f64().unwrap_or(f64::INFINITY)) - } + }, Numeric::Integer) } }; diff --git a/boa_parser/src/lexer/operator.rs b/boa_parser/src/lexer/operator.rs index b36d2dcc809..acfb686c4b5 100644 --- a/boa_parser/src/lexer/operator.rs +++ b/boa_parser/src/lexer/operator.rs @@ -37,16 +37,6 @@ macro_rules! vop { _ => $op, } }); - ($cursor:ident, $op:expr, {$($case:pat => $block:expr),+}) => { - match $cursor.peek().ok_or_else(|| Error::syntax("could not preview next value", $cursor.pos()))? { - $($case => { - $cursor.next_byte()?; - $cursor.next_column(); - $block - })+, - _ => $op - } - } } /// The `op` macro handles binary operations or assignment operations and converts them into tokens. @@ -83,7 +73,7 @@ pub(super) struct Operator { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators impl Operator { /// Creates a new operator lexer. - pub(super) fn new(init: u8) -> Self { + pub(super) const fn new(init: u8) -> Self { Self { init } } } diff --git a/boa_parser/src/lexer/private_identifier.rs b/boa_parser/src/lexer/private_identifier.rs index f33a1a19fe0..0d1bd852489 100644 --- a/boa_parser/src/lexer/private_identifier.rs +++ b/boa_parser/src/lexer/private_identifier.rs @@ -1,7 +1,6 @@ //! This module implements lexing for private identifiers (#foo, #myvar, etc.) used in the JavaScript programing language. -use super::{identifier::Identifier, Cursor, Error, Tokenizer}; -use crate::lexer::{Token, TokenKind}; +use crate::lexer::{identifier::Identifier, Cursor, Error, Token, TokenKind, Tokenizer}; use boa_ast::{Position, Span}; use boa_interner::Interner; use boa_profiler::Profiler; @@ -18,7 +17,7 @@ pub(super) struct PrivateIdentifier; impl PrivateIdentifier { /// Creates a new private identifier lexer. - pub(super) fn new() -> Self { + pub(super) const fn new() -> Self { Self } } diff --git a/boa_parser/src/lexer/regex.rs b/boa_parser/src/lexer/regex.rs index 187a932bc00..6b2a9f39ccb 100644 --- a/boa_parser/src/lexer/regex.rs +++ b/boa_parser/src/lexer/regex.rs @@ -1,7 +1,6 @@ //! This module implements lexing for regex literals used in the JavaScript programing language. -use super::{Cursor, Error, Span, Tokenizer}; -use crate::lexer::{Token, TokenKind}; +use crate::lexer::{Cursor, Error, Span, Token, TokenKind, Tokenizer}; use bitflags::bitflags; use boa_ast::Position; use boa_interner::{Interner, Sym}; @@ -141,12 +140,27 @@ bitflags! { /// Flags of a regular expression. #[derive(Default)] pub struct RegExpFlags: u8 { + /// Whether to test the regular expression against all possible matches in a string, + /// or only against the first. const GLOBAL = 0b0000_0001; + + /// Whether to ignore case while attempting a match in a string. const IGNORE_CASE = 0b0000_0010; + + /// Whether or not to search in strings across multiple lines. const MULTILINE = 0b0000_0100; + + /// Whether `.` matches newlines or not. const DOT_ALL = 0b0000_1000; + + /// Whether or not Unicode features are enabled. const UNICODE = 0b0001_0000; + + /// Whether or not the search is sticky. const STICKY = 0b0010_0000; + + /// Whether the regular expression result exposes the start and end indices of + /// captured substrings. const HAS_INDICES = 0b0100_0000; } } diff --git a/boa_parser/src/lexer/spread.rs b/boa_parser/src/lexer/spread.rs index e6d7d9396b7..b26b8f5d47b 100644 --- a/boa_parser/src/lexer/spread.rs +++ b/boa_parser/src/lexer/spread.rs @@ -1,7 +1,6 @@ //! This module implements lexing for spread (...) literals used in the JavaScript programing language. -use super::{Cursor, Error, Tokenizer}; -use crate::lexer::Token; +use crate::lexer::{Cursor, Error, Token, Tokenizer}; use boa_ast::{Position, Punctuator, Span}; use boa_interner::Interner; use boa_profiler::Profiler; @@ -22,8 +21,8 @@ pub(super) struct SpreadLiteral; impl SpreadLiteral { /// Creates a new string literal lexer. - pub(super) fn new() -> Self { - Self {} + pub(super) const fn new() -> Self { + Self } } diff --git a/boa_parser/src/lexer/string.rs b/boa_parser/src/lexer/string.rs index 93005920196..38f44ef2d8c 100644 --- a/boa_parser/src/lexer/string.rs +++ b/boa_parser/src/lexer/string.rs @@ -1,7 +1,6 @@ //! This module implements lexing for string literals used in the JavaScript programing language. -use super::{Cursor, Error, Tokenizer}; -use crate::lexer::{Token, TokenKind}; +use crate::lexer::{Cursor, Error, Token, TokenKind, Tokenizer}; use boa_ast::{Position, Span}; use boa_interner::Interner; use boa_profiler::Profiler; @@ -109,7 +108,7 @@ impl StringLiteral { /// /// [spec]: https://tc39.es/ecma262/#prod-LineTerminator #[inline] - pub(super) fn is_line_terminator(ch: u32) -> bool { + pub(super) const fn is_line_terminator(ch: u32) -> bool { matches!( ch, 0x000A /* */ | 0x000D /* */ | 0x2028 /* */ | 0x2029 /* */ diff --git a/boa_parser/src/lexer/template.rs b/boa_parser/src/lexer/template.rs index 00d88675397..b1ab0e12cb5 100644 --- a/boa_parser/src/lexer/template.rs +++ b/boa_parser/src/lexer/template.rs @@ -1,9 +1,8 @@ //! This module implements lexing for template literals used in the JavaScript programing language. -use super::{Cursor, Error, Tokenizer}; use crate::lexer::{ string::{StringLiteral, UTF16CodeUnitsBuffer}, - Token, TokenKind, + Cursor, Error, Token, TokenKind, Tokenizer, }; use boa_ast::{Position, Span}; use boa_interner::{Interner, Sym}; @@ -22,7 +21,7 @@ pub struct TemplateString { impl TemplateString { /// Creates a new `TemplateString` with the given raw template ans start position. - pub fn new(raw: Sym, start_pos: Position) -> Self { + pub const fn new(raw: Sym, start_pos: Position) -> Self { Self { raw, start_pos } } @@ -32,7 +31,7 @@ impl TemplateString { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-templatestrings - pub fn as_raw(self) -> Sym { + pub const fn as_raw(self) -> Sym { self.raw } diff --git a/boa_parser/src/lexer/tests.rs b/boa_parser/src/lexer/tests.rs index 5e810ae607f..01616e1f4da 100644 --- a/boa_parser/src/lexer/tests.rs +++ b/boa_parser/src/lexer/tests.rs @@ -5,11 +5,9 @@ use crate::lexer::{ template::TemplateString, token::Numeric, Cursor, Error, Interner, Lexer, Position, Punctuator, Read, Span, TokenKind, }; - use boa_ast::Keyword; use boa_interner::Sym; use boa_macros::utf16; - use std::str; fn span(start: (u32, u32), end: (u32, u32)) -> Span { @@ -610,11 +608,9 @@ fn single_number_without_semicolon() { let mut lexer = Lexer::new(&b"1"[..]); let interner = &mut Interner::default(); - if let Some(x) = lexer.next(interner).unwrap() { - assert_eq!(x.kind(), &TokenKind::numeric_literal(Numeric::Integer(1))); - } else { - panic!("Failed to lex 1 without semicolon"); - } + let expected = [TokenKind::numeric_literal(Numeric::Integer(1))]; + + expect_tokens(&mut lexer, &expected, interner); } #[test] @@ -864,11 +860,7 @@ fn take_while_char_pred_utf8_char() { let mut buf: Vec = Vec::new(); cur.take_while_char_pred(&mut buf, &|c| { - if let Ok(c) = char::try_from(c) { - c == 'a' || c == 'b' || c == 'c' || c == '😀' - } else { - false - } + char::try_from(c).map_or(false, |c| c == 'a' || c == 'b' || c == 'c' || c == '😀') }) .unwrap(); diff --git a/boa_parser/src/lexer/token.rs b/boa_parser/src/lexer/token.rs index db783fd1e9a..df752d6893f 100644 --- a/boa_parser/src/lexer/token.rs +++ b/boa_parser/src/lexer/token.rs @@ -29,21 +29,21 @@ impl Token { /// Create a new detailed token from the token data, line number and column number #[inline] #[must_use] - pub fn new(kind: TokenKind, span: Span) -> Self { + pub const fn new(kind: TokenKind, span: Span) -> Self { Self { kind, span } } /// Gets the kind of the token. #[inline] #[must_use] - pub fn kind(&self) -> &TokenKind { + pub const fn kind(&self) -> &TokenKind { &self.kind } /// Gets the token span in the original source code. #[inline] #[must_use] - pub fn span(&self) -> Span { + pub const fn span(&self) -> Span { self.span } @@ -63,7 +63,7 @@ pub enum Numeric { /// An integer Integer(i32), - // A BigInt + /// A BigInt BigInt(Box), } @@ -162,19 +162,19 @@ impl From for TokenKind { impl TokenKind { /// Creates a `BooleanLiteral` token kind. #[must_use] - pub fn boolean_literal(lit: bool) -> Self { + pub const fn boolean_literal(lit: bool) -> Self { Self::BooleanLiteral(lit) } /// Creates an `EOF` token kind. #[must_use] - pub fn eof() -> Self { + pub const fn eof() -> Self { Self::EOF } /// Creates an `Identifier` token type. #[must_use] - pub fn identifier(ident: Sym) -> Self { + pub const fn identifier(ident: Sym) -> Self { Self::Identifier(ident) } @@ -188,41 +188,43 @@ impl TokenKind { /// Creates a `Punctuator` token type. #[must_use] - pub fn punctuator(punc: Punctuator) -> Self { + pub const fn punctuator(punc: Punctuator) -> Self { Self::Punctuator(punc) } /// Creates a `StringLiteral` token type. #[must_use] - pub fn string_literal(lit: Sym) -> Self { + pub const fn string_literal(lit: Sym) -> Self { Self::StringLiteral(lit) } + /// Creates a `TemplateMiddle` token type. #[must_use] - pub fn template_middle(template_string: TemplateString) -> Self { + pub const fn template_middle(template_string: TemplateString) -> Self { Self::TemplateMiddle(template_string) } + /// Creates a `TemplateNoSubstitution` token type. #[must_use] - pub fn template_no_substitution(template_string: TemplateString) -> Self { + pub const fn template_no_substitution(template_string: TemplateString) -> Self { Self::TemplateNoSubstitution(template_string) } /// Creates a `RegularExpressionLiteral` token kind. #[must_use] - pub fn regular_expression_literal(body: Sym, flags: Sym) -> Self { + pub const fn regular_expression_literal(body: Sym, flags: Sym) -> Self { Self::RegularExpressionLiteral(body, flags) } /// Creates a `LineTerminator` token kind. #[must_use] - pub fn line_terminator() -> Self { + pub const fn line_terminator() -> Self { Self::LineTerminator } /// Creates a 'Comment' token kind. #[must_use] - pub fn comment() -> Self { + pub const fn comment() -> Self { Self::Comment } diff --git a/boa_parser/src/lib.rs b/boa_parser/src/lib.rs index 53ba00cc0fd..372efae3a15 100644 --- a/boa_parser/src/lib.rs +++ b/boa_parser/src/lib.rs @@ -9,46 +9,63 @@ //! [grammar]: https://tc39.es/ecma262/#sec-ecmascript-language-expressions #![cfg_attr(not(test), forbid(clippy::unwrap_used))] -#![warn( - clippy::perf, - clippy::single_match_else, - clippy::dbg_macro, - clippy::doc_markdown, - clippy::wildcard_imports, - clippy::struct_excessive_bools, - clippy::doc_markdown, - clippy::semicolon_if_nothing_returned, - clippy::pedantic -)] +#![warn(missing_docs, clippy::dbg_macro)] #![deny( - clippy::all, - clippy::cast_lossless, - clippy::redundant_closure_for_method_calls, - clippy::unnested_or_patterns, - clippy::trivially_copy_pass_by_ref, - clippy::needless_pass_by_value, - clippy::match_wildcard_for_single_variants, - clippy::map_unwrap_or, - unused_qualifications, + // 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, - unreachable_pub, - trivial_numeric_casts, + unused_qualifications, + unused_tuple_struct_fields, + variant_size_differences, + + // rustdoc lints https://doc.rust-lang.org/rustdoc/lints.html rustdoc::broken_intra_doc_links, - missing_debug_implementations, - missing_copy_implementations, - deprecated_in_future, - meta_variable_misuse, - non_ascii_idents, - rust_2018_compatibility, - rust_2018_idioms, - future_incompatible, - nonstandard_style + 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::let_unit_value + clippy::cognitive_complexity, + clippy::let_unit_value, + clippy::redundant_pub_crate )] pub mod error; diff --git a/boa_parser/src/parser/cursor/buffered_lexer/mod.rs b/boa_parser/src/parser/cursor/buffered_lexer/mod.rs index 44828cef695..f8c3edf098c 100644 --- a/boa_parser/src/parser/cursor/buffered_lexer/mod.rs +++ b/boa_parser/src/parser/cursor/buffered_lexer/mod.rs @@ -105,7 +105,7 @@ where } #[inline] - pub(super) fn strict_mode(&self) -> bool { + pub(super) const fn strict_mode(&self) -> bool { self.lexer.strict_mode() } diff --git a/boa_parser/src/parser/cursor/mod.rs b/boa_parser/src/parser/cursor/mod.rs index 8f260c84b84..39d97ac2fbf 100644 --- a/boa_parser/src/parser/cursor/mod.rs +++ b/boa_parser/src/parser/cursor/mod.rs @@ -87,10 +87,8 @@ where /// This function will panic if there is no further token in the cursor. #[inline] #[track_caller] - #[allow(clippy::let_underscore_drop)] pub(super) fn advance(&mut self, interner: &mut Interner) { - let _ = self - .next(interner) + self.next(interner) .expect("tried to advance cursor, but the buffer was empty"); } @@ -108,7 +106,7 @@ where /// Gets the current strict mode for the cursor. #[inline] - pub(super) fn strict_mode(&self) -> bool { + pub(super) const fn strict_mode(&self) -> bool { self.buffered_lexer.strict_mode() } @@ -120,7 +118,7 @@ where /// Returns if the cursor is currently in an arrow function declaration. #[inline] - pub(super) fn arrow(&self) -> bool { + pub(super) const fn arrow(&self) -> bool { self.arrow } @@ -132,7 +130,7 @@ where /// Returns if the cursor is currently used in `JSON.parse`. #[inline] - pub(super) fn json_parse(&self) -> bool { + pub(super) const fn json_parse(&self) -> bool { self.json_parse } @@ -156,15 +154,18 @@ where identifier: Sym, position: Position, ) -> ParseResult<()> { - if let Some(env) = self.private_environments_stack.last_mut() { - env.entry(identifier).or_insert(position); - Ok(()) - } else { - Err(Error::general( - "private identifier declared outside of class", - position, - )) - } + self.private_environments_stack.last_mut().map_or_else( + || { + Err(Error::general( + "private identifier declared outside of class", + position, + )) + }, + |env| { + env.entry(identifier).or_insert(position); + Ok(()) + }, + ) } /// Pop the last private environment. @@ -231,14 +232,14 @@ where &mut self, interner: &mut Interner, ) -> ParseResult> { - match self.buffered_lexer.peek(0, false, interner)? { - Some(tk) => match tk.kind() { + self.buffered_lexer.peek(0, false, interner)?.map_or( + Ok(SemicolonResult::Found(None)), + |tk| match tk.kind() { TokenKind::Punctuator(Punctuator::Semicolon | Punctuator::CloseBlock) | TokenKind::LineTerminator => Ok(SemicolonResult::Found(Some(tk))), _ => Ok(SemicolonResult::NotFound(tk)), }, - None => Ok(SemicolonResult::Found(None)), - } + ) } /// Consumes the next token if it is a semicolon, or returns a `Errpr` if it's not. @@ -306,11 +307,11 @@ where skip_n: usize, interner: &mut Interner, ) -> ParseResult> { - if let Some(t) = self.buffered_lexer.peek(skip_n, false, interner)? { - Ok(Some(t.kind() == &TokenKind::LineTerminator)) - } else { - Ok(None) - } + self.buffered_lexer + .peek(skip_n, false, interner)? + .map_or(Ok(None), |t| { + Ok(Some(t.kind() == &TokenKind::LineTerminator)) + }) } /// Advance the cursor to the next token and retrieve it, only if it's of `kind` type. diff --git a/boa_parser/src/parser/expression/assignment/mod.rs b/boa_parser/src/parser/expression/assignment/mod.rs index 5b163d63573..afe6ece672f 100644 --- a/boa_parser/src/parser/expression/assignment/mod.rs +++ b/boa_parser/src/parser/expression/assignment/mod.rs @@ -13,6 +13,7 @@ mod conditional; mod exponentiation; mod r#yield; +use super::check_strict_arguments_or_eval; use crate::{ lexer::{Error as LexError, InputElement, TokenKind}, parser::{ @@ -41,8 +42,6 @@ use std::io::Read; pub(super) use exponentiation::ExponentiationExpression; -use super::check_strict_arguments_or_eval; - /// Assignment expression parsing. /// /// This can be one of the following: diff --git a/boa_parser/src/parser/expression/left_hand_side/optional/mod.rs b/boa_parser/src/parser/expression/left_hand_side/optional/mod.rs index d89c3b5ff5b..1653a7c821c 100644 --- a/boa_parser/src/parser/expression/left_hand_side/optional/mod.rs +++ b/boa_parser/src/parser/expression/left_hand_side/optional/mod.rs @@ -1,26 +1,22 @@ #[cfg(test)] mod tests; -use std::io::Read; - -use boa_interner::{Interner, Sym}; -use boa_profiler::Profiler; - use crate::{ lexer::{Token, TokenKind}, parser::{ - cursor::Cursor, expression::Expression, AllowAwait, AllowYield, OrAbrupt, ParseResult, - TokenParser, + cursor::Cursor, expression::left_hand_side::arguments::Arguments, expression::Expression, + AllowAwait, AllowYield, OrAbrupt, ParseResult, TokenParser, }, Error, }; - -use super::arguments::Arguments; use boa_ast::{ self as ast, expression::{access::PropertyAccessField, Optional, OptionalOperation, OptionalOperationKind}, Punctuator, }; +use boa_interner::{Interner, Sym}; +use boa_profiler::Profiler; +use std::io::Read; /// Parses an optional expression. /// diff --git a/boa_parser/src/parser/expression/mod.rs b/boa_parser/src/parser/expression/mod.rs index 8740f4fbdfa..fa47b764218 100644 --- a/boa_parser/src/parser/expression/mod.rs +++ b/boa_parser/src/parser/expression/mod.rs @@ -736,7 +736,7 @@ expression!( ); /// Returns an error if `arguments` or `eval` are used as identifier in strict mode. -fn check_strict_arguments_or_eval(ident: Identifier, position: Position) -> ParseResult<()> { +const fn check_strict_arguments_or_eval(ident: Identifier, position: Position) -> ParseResult<()> { match ident.sym() { Sym::ARGUMENTS => Err(Error::general( "unexpected identifier 'arguments' in strict mode", diff --git a/boa_parser/src/parser/expression/primary/array_initializer/mod.rs b/boa_parser/src/parser/expression/primary/array_initializer/mod.rs index 547bfa9d148..658441467ea 100644 --- a/boa_parser/src/parser/expression/primary/array_initializer/mod.rs +++ b/boa_parser/src/parser/expression/primary/array_initializer/mod.rs @@ -124,10 +124,8 @@ where } } - if last_spread { - if let Some(None) = elements.last() { - has_trailing_comma_spread = true; - } + if last_spread && elements.last() == Some(&None) { + has_trailing_comma_spread = true; } Ok(literal::ArrayLiteral::new( diff --git a/boa_parser/src/parser/expression/primary/async_function_expression/mod.rs b/boa_parser/src/parser/expression/primary/async_function_expression/mod.rs index 2da894c56b0..12d8d7f40d2 100644 --- a/boa_parser/src/parser/expression/primary/async_function_expression/mod.rs +++ b/boa_parser/src/parser/expression/primary/async_function_expression/mod.rs @@ -77,10 +77,9 @@ where if cursor.strict_mode() && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) { return Err(Error::lex(LexError::Syntax( "Unexpected eval or arguments in strict mode".into(), - match cursor.peek(0, interner)? { - Some(token) => token.span().end(), - None => Position::new(1, 1), - }, + cursor + .peek(0, interner)? + .map_or_else(|| Position::new(1, 1), |token| token.span().end()), ))); } } diff --git a/boa_parser/src/parser/expression/primary/async_generator_expression/mod.rs b/boa_parser/src/parser/expression/primary/async_generator_expression/mod.rs index 6e3ef65476c..660452da602 100644 --- a/boa_parser/src/parser/expression/primary/async_generator_expression/mod.rs +++ b/boa_parser/src/parser/expression/primary/async_generator_expression/mod.rs @@ -86,10 +86,9 @@ where if cursor.strict_mode() && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) { return Err(Error::lex(LexError::Syntax( "Unexpected eval or arguments in strict mode".into(), - match cursor.peek(0, interner)? { - Some(token) => token.span().end(), - None => Position::new(1, 1), - }, + cursor + .peek(0, interner)? + .map_or_else(|| Position::new(1, 1), |token| token.span().end()), ))); } } diff --git a/boa_parser/src/parser/expression/primary/function_expression/mod.rs b/boa_parser/src/parser/expression/primary/function_expression/mod.rs index fda58036fec..4903e50c0bc 100644 --- a/boa_parser/src/parser/expression/primary/function_expression/mod.rs +++ b/boa_parser/src/parser/expression/primary/function_expression/mod.rs @@ -79,10 +79,9 @@ where if cursor.strict_mode() && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) { return Err(Error::lex(LexError::Syntax( "Unexpected eval or arguments in strict mode".into(), - match cursor.peek(0, interner)? { - Some(token) => token.span().end(), - None => Position::new(1, 1), - }, + cursor + .peek(0, interner)? + .map_or_else(|| Position::new(1, 1), |token| token.span().end()), ))); } } diff --git a/boa_parser/src/parser/expression/primary/generator_expression/mod.rs b/boa_parser/src/parser/expression/primary/generator_expression/mod.rs index 5a12db96823..8f9c920afb8 100644 --- a/boa_parser/src/parser/expression/primary/generator_expression/mod.rs +++ b/boa_parser/src/parser/expression/primary/generator_expression/mod.rs @@ -82,10 +82,9 @@ where if cursor.strict_mode() && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) { return Err(Error::lex(LexError::Syntax( "Unexpected eval or arguments in strict mode".into(), - match cursor.peek(0, interner)? { - Some(token) => token.span().end(), - None => Position::new(1, 1), - }, + cursor + .peek(0, interner)? + .map_or_else(|| Position::new(1, 1), |token| token.span().end()), ))); } } diff --git a/boa_parser/src/parser/expression/primary/mod.rs b/boa_parser/src/parser/expression/primary/mod.rs index 3a1cd8553ad..33a142dc75d 100644 --- a/boa_parser/src/parser/expression/primary/mod.rs +++ b/boa_parser/src/parser/expression/primary/mod.rs @@ -450,8 +450,8 @@ where } }; - let is_arrow = if let Some(TokenKind::Punctuator(Punctuator::Arrow)) = - cursor.peek(0, interner)?.map(Token::kind) + let is_arrow = if cursor.peek(0, interner)?.map(Token::kind) + == Some(&TokenKind::Punctuator(Punctuator::Arrow)) { !cursor.peek_is_line_terminator(0, interner).or_abrupt()? } else { diff --git a/boa_parser/src/parser/expression/primary/object_initializer/mod.rs b/boa_parser/src/parser/expression/primary/object_initializer/mod.rs index cf975a74c8a..006c91be999 100644 --- a/boa_parser/src/parser/expression/primary/object_initializer/mod.rs +++ b/boa_parser/src/parser/expression/primary/object_initializer/mod.rs @@ -121,10 +121,9 @@ where if let Some(position) = duplicate_proto_position { if !cursor.json_parse() - && match cursor.peek(0, interner)? { - Some(token) => token.kind() != &TokenKind::Punctuator(Punctuator::Assign), - None => true, - } + && cursor.peek(0, interner)?.map_or(true, |token| { + token.kind() != &TokenKind::Punctuator(Punctuator::Assign) + }) { return Err(Error::general( "Duplicate __proto__ fields are not allowed in object literals.", diff --git a/boa_parser/src/parser/expression/update.rs b/boa_parser/src/parser/expression/update.rs index 421c8400580..52fe12ba960 100644 --- a/boa_parser/src/parser/expression/update.rs +++ b/boa_parser/src/parser/expression/update.rs @@ -5,12 +5,14 @@ //! //! [spec]: https://tc39.es/ecma262/#sec-update-expressions -use super::{check_strict_arguments_or_eval, left_hand_side::LeftHandSideExpression}; use crate::{ lexer::{Error as LexError, TokenKind}, parser::{ - expression::unary::UnaryExpression, AllowAwait, AllowYield, Cursor, OrAbrupt, ParseResult, - TokenParser, + expression::{ + check_strict_arguments_or_eval, left_hand_side::LeftHandSideExpression, + unary::UnaryExpression, + }, + AllowAwait, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser, }, Error, }; diff --git a/boa_parser/src/parser/function/mod.rs b/boa_parser/src/parser/function/mod.rs index 7803d452e7b..fd58d9bf475 100644 --- a/boa_parser/src/parser/function/mod.rs +++ b/boa_parser/src/parser/function/mod.rs @@ -15,7 +15,7 @@ use crate::{ parser::{ expression::{BindingIdentifier, Initializer}, statement::{ArrayBindingPattern, ObjectBindingPattern, StatementList}, - AllowAwait, AllowYield, Cursor, TokenParser, + AllowAwait, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser, }, Error, }; @@ -30,8 +30,6 @@ use boa_macros::utf16; use boa_profiler::Profiler; use std::io::Read; -use super::{OrAbrupt, ParseResult}; - /// Formal parameters parsing. /// /// More information: diff --git a/boa_parser/src/parser/mod.rs b/boa_parser/src/parser/mod.rs index 9a884380a79..e3f835db61a 100644 --- a/boa_parser/src/parser/mod.rs +++ b/boa_parser/src/parser/mod.rs @@ -9,9 +9,6 @@ pub(crate) mod function; #[cfg(test)] mod tests; -use rustc_hash::FxHashSet; -use std::io::Read; - use crate::{ error::ParseResult, lexer::TokenKind, @@ -31,6 +28,8 @@ use boa_ast::{ }; use boa_interner::Interner; use boa_macros::utf16; +use rustc_hash::FxHashSet; +use std::io::Read; /// Trait implemented by parsers. /// @@ -240,7 +239,7 @@ pub struct Script { impl Script { /// Create a new `Script` parser. #[inline] - fn new(direct_eval: bool) -> Self { + const fn new(direct_eval: bool) -> Self { Self { direct_eval } } } @@ -315,7 +314,7 @@ pub struct ScriptBody { impl ScriptBody { /// Create a new `ScriptBody` parser. #[inline] - fn new(direct_eval: bool) -> Self { + const fn new(direct_eval: bool) -> Self { Self { direct_eval } } } diff --git a/boa_parser/src/parser/statement/block/mod.rs b/boa_parser/src/parser/statement/block/mod.rs index d13b1554458..f0cf432ae4a 100644 --- a/boa_parser/src/parser/statement/block/mod.rs +++ b/boa_parser/src/parser/statement/block/mod.rs @@ -10,10 +10,12 @@ #[cfg(test)] mod tests; -use super::StatementList; use crate::{ lexer::TokenKind, - parser::{AllowAwait, AllowReturn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser}, + parser::{ + statement::StatementList, AllowAwait, AllowReturn, AllowYield, Cursor, OrAbrupt, + ParseResult, TokenParser, + }, Error, }; use boa_ast::{ diff --git a/boa_parser/src/parser/statement/break_stm/mod.rs b/boa_parser/src/parser/statement/break_stm/mod.rs index 3271b01c1ad..8efb2f982ca 100644 --- a/boa_parser/src/parser/statement/break_stm/mod.rs +++ b/boa_parser/src/parser/statement/break_stm/mod.rs @@ -11,7 +11,7 @@ mod tests; use crate::{ - lexer::TokenKind, + lexer::{Token, TokenKind}, parser::{ cursor::{Cursor, SemicolonResult}, expression::LabelIdentifier, @@ -62,11 +62,8 @@ where cursor.expect((Keyword::Break, false), "break statement", interner)?; let label = if let SemicolonResult::Found(tok) = cursor.peek_semicolon(interner)? { - match tok { - Some(tok) if tok.kind() == &TokenKind::Punctuator(Punctuator::Semicolon) => { - cursor.advance(interner); - } - _ => {} + if tok.map(Token::kind) == Some(&TokenKind::Punctuator(Punctuator::Semicolon)) { + cursor.advance(interner); } None diff --git a/boa_parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs b/boa_parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs index f71f65f811b..412703cae9c 100644 --- a/boa_parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs +++ b/boa_parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs @@ -660,7 +660,7 @@ where match class_element_name { ClassElementName::PropertyName(property_name) if r#static => { - if let Some(Sym::PROTOTYPE) = property_name.prop_name() { + if property_name.prop_name() == Some(Sym::PROTOTYPE) { return Err(Error::general( "class may not have static method definitions named 'prototype'", name_position, @@ -717,7 +717,7 @@ where cursor.set_strict_mode(strict); match class_element_name { ClassElementName::PropertyName(property_name) if r#static => { - if let Some(Sym::PROTOTYPE) = property_name.prop_name() { + if property_name.prop_name() == Some(Sym::PROTOTYPE) { return Err(Error::general( "class may not have static method definitions named 'prototype'", name_position, @@ -762,7 +762,7 @@ where match class_element_name { ClassElementName::PropertyName(property_name) if r#static => { - if let Some(Sym::PROTOTYPE) = property_name.prop_name() { + if property_name.prop_name() == Some(Sym::PROTOTYPE) { return Err(Error::general( "class may not have static method definitions named 'prototype'", name_position, diff --git a/boa_parser/src/parser/statement/declaration/hoistable/mod.rs b/boa_parser/src/parser/statement/declaration/hoistable/mod.rs index 55f406cc6d1..5a82e10571e 100644 --- a/boa_parser/src/parser/statement/declaration/hoistable/mod.rs +++ b/boa_parser/src/parser/statement/declaration/hoistable/mod.rs @@ -165,10 +165,9 @@ fn parse_callable_declaration( if cursor.strict_mode() && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) { return Err(Error::lex(LexError::Syntax( "Unexpected eval or arguments in strict mode".into(), - match cursor.peek(0, interner)? { - Some(token) => token.span().end(), - None => Position::new(1, 1), - }, + cursor + .peek(0, interner)? + .map_or_else(|| Position::new(1, 1), |token| token.span().end()), ))); } diff --git a/boa_parser/src/parser/statement/if_stm/mod.rs b/boa_parser/src/parser/statement/if_stm/mod.rs index 53603b38251..5c660953a00 100644 --- a/boa_parser/src/parser/statement/if_stm/mod.rs +++ b/boa_parser/src/parser/statement/if_stm/mod.rs @@ -4,8 +4,9 @@ mod tests; use crate::{ lexer::TokenKind, parser::{ - expression::Expression, statement::declaration::FunctionDeclaration, AllowAwait, - AllowReturn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser, + expression::Expression, + statement::{declaration::FunctionDeclaration, Statement}, + AllowAwait, AllowReturn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser, }, Error, }; @@ -17,8 +18,6 @@ use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; -use super::Statement; - /// If statement parsing. /// /// An `if` statement will have a condition, a block statement, and an optional `else` statement. diff --git a/boa_parser/src/parser/statement/iteration/do_while_statement.rs b/boa_parser/src/parser/statement/iteration/do_while_statement.rs index 6f4d1cde72b..57f252cf5e1 100644 --- a/boa_parser/src/parser/statement/iteration/do_while_statement.rs +++ b/boa_parser/src/parser/statement/iteration/do_while_statement.rs @@ -8,7 +8,7 @@ //! [spec]: https://tc39.es/ecma262/#sec-do-while-statement use crate::{ - lexer::TokenKind, + lexer::{Token, TokenKind}, parser::{ expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser, @@ -107,10 +107,10 @@ where // Here, we only care to read the next token if it's a semicolon. If it's not, we // automatically "enter" or assume a semicolon, since we have just read the `)` token: // https://tc39.es/ecma262/#sec-automatic-semicolon-insertion - if let Some(tok) = cursor.peek(0, interner)? { - if let TokenKind::Punctuator(Punctuator::Semicolon) = *tok.kind() { - cursor.advance(interner); - } + if cursor.peek(0, interner)?.map(Token::kind) + == Some(&TokenKind::Punctuator(Punctuator::Semicolon)) + { + cursor.advance(interner); } Ok(DoWhileLoop::new(body, cond)) diff --git a/boa_parser/src/parser/statement/labelled_stm/mod.rs b/boa_parser/src/parser/statement/labelled_stm/mod.rs index cda3d665f36..0b93edb487b 100644 --- a/boa_parser/src/parser/statement/labelled_stm/mod.rs +++ b/boa_parser/src/parser/statement/labelled_stm/mod.rs @@ -3,7 +3,7 @@ use crate::{ parser::{ cursor::Cursor, expression::LabelIdentifier, - statement::{AllowAwait, AllowReturn, Statement}, + statement::{declaration::FunctionDeclaration, AllowAwait, AllowReturn, Statement}, AllowYield, OrAbrupt, ParseResult, TokenParser, }, Error, @@ -13,8 +13,6 @@ use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; -use super::declaration::FunctionDeclaration; - /// Labelled Statement Parsing /// /// More information diff --git a/boa_parser/src/parser/statement/return_stm/mod.rs b/boa_parser/src/parser/statement/return_stm/mod.rs index 56947b633de..6d8afc070a5 100644 --- a/boa_parser/src/parser/statement/return_stm/mod.rs +++ b/boa_parser/src/parser/statement/return_stm/mod.rs @@ -1,5 +1,5 @@ use crate::{ - lexer::TokenKind, + lexer::{Token, TokenKind}, parser::{ cursor::{Cursor, SemicolonResult}, expression::Expression, @@ -50,11 +50,8 @@ where cursor.expect((Keyword::Return, false), "return statement", interner)?; if let SemicolonResult::Found(tok) = cursor.peek_semicolon(interner)? { - match tok { - Some(tok) if tok.kind() == &TokenKind::Punctuator(Punctuator::Semicolon) => { - cursor.advance(interner); - } - _ => {} + if tok.map(Token::kind) == Some(&TokenKind::Punctuator(Punctuator::Semicolon)) { + cursor.advance(interner); } return Ok(Return::new(None)); diff --git a/boa_profiler/src/lib.rs b/boa_profiler/src/lib.rs index c1a543aa763..c57cce497b2 100644 --- a/boa_profiler/src/lib.rs +++ b/boa_profiler/src/lib.rs @@ -1,16 +1,71 @@ -#![allow(missing_copy_implementations, missing_debug_implementations)] +//! Profiler for the Boa JavaScript engine. + +#![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, +)] + +use std::fmt::{self, Debug}; #[cfg(feature = "profiler")] use measureme::{EventId, Profiler as MeasuremeProfiler, TimingGuard}; #[cfg(feature = "profiler")] use once_cell::sync::OnceCell; -use std::fmt::{self, Debug}; #[cfg(feature = "profiler")] use std::{ path::Path, thread::{current, ThreadId}, }; +/// Profiler for the Boa JavaScript engine. #[cfg(feature = "profiler")] pub struct Profiler { profiler: MeasuremeProfiler, @@ -24,6 +79,7 @@ static mut INSTANCE: OnceCell = OnceCell::new(); #[cfg(feature = "profiler")] impl Profiler { + /// Start a new profiled event. pub fn start_event(&self, label: &str, category: &str) -> TimingGuard<'_> { let kind = self.profiler.alloc_string(category); let id = EventId::from_label(self.profiler.alloc_string(label)); @@ -32,16 +88,19 @@ impl Profiler { .start_recording_interval_event(kind, id, thread_id) } - pub fn default() -> Self { + fn default() -> Self { let profiler = MeasuremeProfiler::new(Path::new("./my_trace")).expect("must be able to create file"); Self { profiler } } + /// Return the global instance of the profiler. + #[must_use] pub fn global() -> &'static Self { unsafe { INSTANCE.get_or_init(Self::default) } } + /// Drop the global instance of the profiler. pub fn drop(&self) { // In order to drop the INSTANCE we need to get ownership of it, which isn't possible on a static unless you make it a mutable static // mutating statics is unsafe, so we need to wrap it as so. @@ -58,6 +117,7 @@ impl Profiler { // Once `as_64()` is in stable we can do this: // https://github.com/rust-lang/rust/pull/68531/commits/ea42b1c5b85f649728e3a3b334489bac6dce890a // Until then our options are: use rust-nightly or use unsafe {} + #[allow(clippy::cast_possible_truncation)] fn thread_id_to_u32(tid: ThreadId) -> u32 { unsafe { std::mem::transmute::(tid) as u32 } } @@ -69,17 +129,24 @@ impl Debug for Profiler { } } +/// An empty profiler that does nothing. #[cfg(not(feature = "profiler"))] +#[derive(Copy, Clone)] pub struct Profiler; -#[allow(clippy::unused_unit, clippy::unused_self)] +//#[allow(clippy::unused_unit, clippy::unused_self)] #[cfg(not(feature = "profiler"))] impl Profiler { - pub fn start_event(&self, _label: &str, _category: &str) -> () {} + /// Does nothing. + #[allow(clippy::unused_unit)] + pub const fn start_event(&self, _label: &str, _category: &str) -> () {} - pub fn drop(&self) {} + /// Does nothing. + pub const fn drop(&self) {} - pub fn global() -> Self { + /// Does nothing. + #[must_use] + pub const fn global() -> Self { Self } } diff --git a/boa_tester/Cargo.toml b/boa_tester/Cargo.toml index 9670ae61027..522d546551c 100644 --- a/boa_tester/Cargo.toml +++ b/boa_tester/Cargo.toml @@ -13,7 +13,6 @@ rust-version.workspace = true [dependencies] boa_engine = { workspace = true, features = ["intl"] } -boa_interner.workspace = true boa_gc.workspace = true boa_parser.workspace = true clap = { version = "4.0.26", features = ["derive"] } diff --git a/boa_tester/src/exec/js262.rs b/boa_tester/src/exec/js262.rs index 8cdb1118cbb..89d5c4602e8 100644 --- a/boa_tester/src/exec/js262.rs +++ b/boa_tester/src/exec/js262.rs @@ -78,8 +78,9 @@ fn detach_array_buffer(_this: &JsValue, args: &[JsValue], _: &mut Context) -> Js /// /// Accepts a string value as its first argument and executes it as an ECMAScript script. fn eval_script(_this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - if let Some(source_text) = args.get(0).and_then(JsValue::as_string) { - match context.parse(source_text.to_std_string_escaped()) { + args.get(0).and_then(JsValue::as_string).map_or_else( + || Ok(JsValue::undefined()), + |source_text| match context.parse(source_text.to_std_string_escaped()) { // TODO: check strict Err(e) => Err(JsNativeError::typ() .with_message(format!("Uncaught Syntax Error: {e}")) @@ -87,10 +88,8 @@ fn eval_script(_this: &JsValue, args: &[JsValue], context: &mut Context) -> JsRe // Calling eval here parses the code a second time. // TODO: We can fix this after we have have defined the public api for the vm executer. Ok(_) => context.eval(source_text.to_std_string_escaped()), - } - } else { - Ok(JsValue::undefined()) - } + }, + ) } /// The `$262.gc()` function. diff --git a/boa_tester/src/exec/mod.rs b/boa_tester/src/exec/mod.rs index 254bebc0fa1..89eba98563c 100644 --- a/boa_tester/src/exec/mod.rs +++ b/boa_tester/src/exec/mod.rs @@ -2,13 +2,10 @@ mod js262; -use std::borrow::Cow; - -use crate::read::ErrorType; - use super::{ Harness, Outcome, Phase, SuiteResult, Test, TestFlags, TestOutcomeResult, TestResult, TestSuite, }; +use crate::read::ErrorType; use boa_engine::{ builtins::JsArgs, object::FunctionBuilder, property::Attribute, Context, JsNativeErrorKind, JsResult, JsValue, @@ -17,6 +14,7 @@ use boa_gc::{Finalize, Gc, GcCell, Trace}; use boa_parser::Parser; use colored::Colorize; use rayon::prelude::*; +use std::borrow::Cow; impl TestSuite { /// Runs the test suite. diff --git a/boa_tester/src/main.rs b/boa_tester/src/main.rs index 41b7b0f5846..3cdb2e34d0c 100644 --- a/boa_tester/src/main.rs +++ b/boa_tester/src/main.rs @@ -2,64 +2,69 @@ //! //! This crate will run the full ECMAScript test suite (Test262) and report compliance of the //! `boa` wrap_err. + #![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( - clippy::perf, - clippy::single_match_else, - clippy::dbg_macro, - clippy::doc_markdown, - clippy::wildcard_imports, - clippy::struct_excessive_bools, - clippy::doc_markdown, - clippy::semicolon_if_nothing_returned, - clippy::pedantic -)] +#![warn(missing_docs, clippy::dbg_macro)] #![deny( - clippy::all, - clippy::cast_lossless, - clippy::redundant_closure_for_method_calls, - clippy::unnested_or_patterns, - clippy::trivially_copy_pass_by_ref, - clippy::needless_pass_by_value, - clippy::match_wildcard_for_single_variants, - clippy::map_unwrap_or, - unused_qualifications, - unused_import_braces, - unused_lifetimes, - unreachable_pub, - trivial_numeric_casts, - // rustdoc, - missing_debug_implementations, - missing_copy_implementations, - deprecated_in_future, - meta_variable_misuse, - non_ascii_idents, - rust_2018_compatibility, - rust_2018_idioms, + // 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::use_self, // TODO: deny once false positives are fixed - clippy::module_name_repetitions, - clippy::cast_possible_truncation, - clippy::cast_sign_loss, - clippy::cast_precision_loss, - clippy::cast_possible_wrap, - clippy::cast_ptr_alignment, - clippy::missing_panics_doc, + clippy::use_self, clippy::too_many_lines, - clippy::unreadable_literal, - clippy::missing_inline_in_public_items, - clippy::cognitive_complexity, - clippy::must_use_candidate, - clippy::missing_errors_doc, - clippy::as_conversions, - clippy::let_unit_value, + clippy::redundant_pub_crate, + clippy::cast_precision_loss, + clippy::cast_possible_wrap )] mod exec; @@ -123,7 +128,7 @@ impl Ignored { .unwrap_or_default() } - pub(crate) fn contains_any_flag(&self, flags: TestFlags) -> bool { + pub(crate) const fn contains_any_flag(&self, flags: TestFlags) -> bool { flags.intersects(self.flags) } } @@ -427,15 +432,15 @@ impl From> for Outcome { bitflags! { struct TestFlags: u16 { - const STRICT = 0b000000001; - const NO_STRICT = 0b000000010; - const MODULE = 0b000000100; - const RAW = 0b000001000; - const ASYNC = 0b000010000; - const GENERATED = 0b000100000; - const CAN_BLOCK_IS_FALSE = 0b001000000; - const CAN_BLOCK_IS_TRUE = 0b010000000; - const NON_DETERMINISTIC = 0b100000000; + const STRICT = 0b0_0000_0001; + const NO_STRICT = 0b0_0000_0010; + const MODULE = 0b0_0000_0100; + const RAW = 0b0_0000_1000; + const ASYNC = 0b0_0001_0000; + const GENERATED = 0b0_0010_0000; + const CAN_BLOCK_IS_FALSE = 0b0_0100_0000; + const CAN_BLOCK_IS_TRUE = 0b0_1000_0000; + const NON_DETERMINISTIC = 0b1_0000_0000; } } @@ -512,7 +517,7 @@ impl<'de> Deserialize<'de> for TestFlags { struct RawFlagsVisitor; - impl<'de> Visitor<'de> for RawFlagsVisitor { + impl Visitor<'_> for RawFlagsVisitor { type Value = TestFlags; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/boa_tester/src/read.rs b/boa_tester/src/read.rs index 29dc714d63f..0d187c416eb 100644 --- a/boa_tester/src/read.rs +++ b/boa_tester/src/read.rs @@ -53,13 +53,13 @@ pub(super) enum ErrorType { } impl ErrorType { - pub(super) fn as_str(self) -> &'static str { + pub(super) const fn as_str(self) -> &'static str { match self { - ErrorType::Test262Error => "Test262Error", - ErrorType::SyntaxError => "SyntaxError", - ErrorType::ReferenceError => "ReferenceError", - ErrorType::RangeError => "RangeError", - ErrorType::TypeError => "TypeError", + Self::Test262Error => "Test262Error", + Self::SyntaxError => "SyntaxError", + Self::ReferenceError => "ReferenceError", + Self::RangeError => "RangeError", + Self::TypeError => "TypeError", } } } diff --git a/boa_unicode/src/lib.rs b/boa_unicode/src/lib.rs index d896e788e04..fd4ce3a45c6 100644 --- a/boa_unicode/src/lib.rs +++ b/boa_unicode/src/lib.rs @@ -11,60 +11,58 @@ html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg" )] #![cfg_attr(not(test), forbid(clippy::unwrap_used))] -#![warn( - clippy::perf, - clippy::single_match_else, - clippy::dbg_macro, - clippy::doc_markdown, - clippy::wildcard_imports, - clippy::struct_excessive_bools, - clippy::doc_markdown, - clippy::semicolon_if_nothing_returned, - clippy::pedantic -)] +#![warn(missing_docs, clippy::dbg_macro)] #![deny( - clippy::all, - clippy::cast_lossless, - clippy::redundant_closure_for_method_calls, - clippy::use_self, - clippy::unnested_or_patterns, - clippy::trivially_copy_pass_by_ref, - clippy::needless_pass_by_value, - clippy::match_wildcard_for_single_variants, - clippy::map_unwrap_or, - unused_qualifications, - unused_import_braces, - unused_lifetimes, - unreachable_pub, - trivial_numeric_casts, - // rustdoc, - missing_debug_implementations, - missing_copy_implementations, - deprecated_in_future, - meta_variable_misuse, - non_ascii_idents, - rust_2018_compatibility, - rust_2018_idioms, + // 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::cast_possible_truncation, - clippy::cast_sign_loss, - clippy::cast_precision_loss, - clippy::cast_possible_wrap, - clippy::cast_ptr_alignment, - clippy::missing_panics_doc, - clippy::too_many_lines, - clippy::unreadable_literal, - clippy::missing_inline_in_public_items, - clippy::cognitive_complexity, - clippy::must_use_candidate, - clippy::missing_errors_doc, - clippy::as_conversions, - clippy::let_unit_value -)] +#![allow(clippy::redundant_pub_crate)] mod tables; #[cfg(test)] diff --git a/boa_wasm/src/lib.rs b/boa_wasm/src/lib.rs index 803ad9ecea0..85fb45be99e 100644 --- a/boa_wasm/src/lib.rs +++ b/boa_wasm/src/lib.rs @@ -1,66 +1,67 @@ +//! A ECMAScript WASM 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), forbid(clippy::unwrap_used))] -#![warn( - clippy::perf, - clippy::single_match_else, - clippy::dbg_macro, - clippy::doc_markdown, - clippy::wildcard_imports, - clippy::struct_excessive_bools, - clippy::doc_markdown, - clippy::semicolon_if_nothing_returned, - clippy::pedantic -)] +#![warn(missing_docs, clippy::dbg_macro)] #![deny( - clippy::all, - clippy::cast_lossless, - clippy::redundant_closure_for_method_calls, - clippy::use_self, - clippy::unnested_or_patterns, - clippy::trivially_copy_pass_by_ref, - clippy::needless_pass_by_value, - clippy::match_wildcard_for_single_variants, - clippy::map_unwrap_or, - unused_qualifications, - unused_import_braces, - unused_lifetimes, - unreachable_pub, - trivial_numeric_casts, - // rustdoc, - missing_debug_implementations, - missing_copy_implementations, - deprecated_in_future, - meta_variable_misuse, - non_ascii_idents, - rust_2018_compatibility, - rust_2018_idioms, + // rustc lint groups https://doc.rust-lang.org/rustc/lints/groups.html + warnings, future_incompatible, + let_underscore, nonstandard_style, -)] -#![allow( - clippy::module_name_repetitions, - clippy::cast_possible_truncation, - clippy::cast_sign_loss, - clippy::cast_precision_loss, - clippy::cast_possible_wrap, - clippy::cast_ptr_alignment, - clippy::missing_panics_doc, - clippy::too_many_lines, - clippy::unreadable_literal, - clippy::missing_inline_in_public_items, - clippy::cognitive_complexity, - clippy::must_use_candidate, - clippy::missing_errors_doc, - clippy::as_conversions, - clippy::let_unit_value + 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, )] use boa_engine::Context; +use getrandom as _; use wasm_bindgen::prelude::*; +/// Evaluate the given ECMAScript code. #[wasm_bindgen] pub fn evaluate(src: &str) -> Result { // Setup executor From 385eaa0081c577d7a953618f920ddf171832a202 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 10:59:42 +0000 Subject: [PATCH 20/24] Bump rayon from 1.5.3 to 1.6.0 (#2451) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [rayon](https://github.com/rayon-rs/rayon) from 1.5.3 to 1.6.0.
Changelog

Sourced from rayon's changelog.

Release rayon 1.6.0 / rayon-core 1.10.0 (2022-11-18)

  • The minimum supported rustc is now 1.56.
  • The new IndexedParallelIterator::fold_chunks and fold_chunks_with methods work like ParallelIterator::fold and fold_with with fixed-size chunks of items. This may be useful for predictable batching performance, without the allocation overhead of IndexedParallelIterator::chunks.
  • New "broadcast" methods run a given function on all threads in the pool. These run at a sort of reduced priority after each thread has exhausted their local work queue, but before they attempt work-stealing from other threads.
    • The global broadcast function and ThreadPool::broadcast method will block until completion, returning a Vec of all return values.
    • The global spawn_broadcast function and methods on ThreadPool, Scope, and ScopeFifo will run detached, without blocking the current thread.
  • Panicking methods now use #[track_caller] to report the caller's location.
  • Fixed a truncated length in vec::Drain when given an empty range.

Contributors

Thanks to all of the contributors for this release!

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=rayon&package-manager=cargo&previous-version=1.5.3&new-version=1.6.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- Cargo.lock | 9 ++++----- boa_tester/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d8127a1b1ea..ff08ba6d95c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1427,11 +1427,10 @@ dependencies = [ [[package]] name = "rayon" -version = "1.5.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b" dependencies = [ - "autocfg", "crossbeam-deque", "either", "rayon-core", @@ -1439,9 +1438,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.3" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" dependencies = [ "crossbeam-channel", "crossbeam-deque", diff --git a/boa_tester/Cargo.toml b/boa_tester/Cargo.toml index 522d546551c..32057053d76 100644 --- a/boa_tester/Cargo.toml +++ b/boa_tester/Cargo.toml @@ -24,6 +24,6 @@ regex = "1.7.0" once_cell = "1.16.0" colored = "2.0.0" fxhash = "0.2.1" -rayon = "1.5.3" +rayon = "1.6.0" toml = "0.5.9" color-eyre = "0.6.2" From 7c4fbd28b08e1059ce6eb754d6ace366ba6789dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Nov 2022 02:12:35 +0000 Subject: [PATCH 21/24] Bump test262 from `1d5dc6b` to `83a46bf` (#2452) Bumps [test262](https://github.com/tc39/test262) from `1d5dc6b` to `83a46bf`.
Commits
  • 83a46bf test/built-ins/TypedArray/prototype/map/callbackfn-resize.js test result is i...
  • ec752eb Temporal: Expand PlainYearMonth and PlainMonthDay strings tests
  • 7d0dde3 Temporal: Remove duplicate number-as-PlainMonthDay test case
  • d1b16d7 Temporal: Move more collections of valid/invalid strings into TemporalHelpers
  • 10ac2ad Fix esids
  • 60e41ed CI: Disable GraalJS.
  • dfb3593 Add feature flag for Array.fromAsync
  • See full diff in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- test262 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test262 b/test262 index 1d5dc6b577a..83a46bfe0e7 160000 --- a/test262 +++ b/test262 @@ -1 +1 @@ -Subproject commit 1d5dc6b577ae6728c087f7d6143c3a5ccf88bcff +Subproject commit 83a46bfe0e79aed8274a1b9f4beb0a2efa0b3533 From 5125da00082d73b28526b9b4822e7d69f356c90f Mon Sep 17 00:00:00 2001 From: Iban Eguia Moraza Date: Tue, 22 Nov 2022 02:21:53 +0000 Subject: [PATCH 22/24] Fixed typo in the docs (#2450) This PR fixes a typo introduced in #2440 --- boa_tester/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boa_tester/src/main.rs b/boa_tester/src/main.rs index 3cdb2e34d0c..af02e8b2994 100644 --- a/boa_tester/src/main.rs +++ b/boa_tester/src/main.rs @@ -1,7 +1,7 @@ //! Test262 test runner //! //! This crate will run the full ECMAScript test suite (Test262) and report compliance of the -//! `boa` wrap_err. +//! `boa` engine. #![doc( html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg", From a5bf5a2212f0ca57652ce27e4779ac7639800115 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Nov 2022 11:29:41 +0000 Subject: [PATCH 23/24] Bump bootstrap from 5.2.2 to 5.2.3 (#2456) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [bootstrap](https://github.com/twbs/bootstrap) from 5.2.2 to 5.2.3.
Release notes

Sourced from bootstrap's releases.

v5.2.3

Fixes

🎨 CSS

  • #37377: Import root in bootstrap-utilities
  • #37425: Fix deprecation warning with sass 1.56.0
  • Carousel: Fix RTL translate() direction

☕️ JavaScript

  • #37235: fix tooltip/popper disposal inconsistencies
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=bootstrap&package-manager=npm_and_yarn&previous-version=5.2.2&new-version=5.2.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index d971b884812..525d8fca0a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ }, "devDependencies": { "@wasm-tool/wasm-pack-plugin": "^1.6.0", - "bootstrap": "^5.2.2", + "bootstrap": "^5.2.3", "clean-webpack-plugin": "^4.0.0", "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.7.2", @@ -782,9 +782,9 @@ "dev": true }, "node_modules/bootstrap": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.2.tgz", - "integrity": "sha512-dEtzMTV71n6Fhmbg4fYJzQsw1N29hJKO1js5ackCgIpDcGid2ETMGC6zwSYw09v05Y+oRdQ9loC54zB1La3hHQ==", + "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": [ { @@ -5303,9 +5303,9 @@ "dev": true }, "bootstrap": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.2.tgz", - "integrity": "sha512-dEtzMTV71n6Fhmbg4fYJzQsw1N29hJKO1js5ackCgIpDcGid2ETMGC6zwSYw09v05Y+oRdQ9loC54zB1La3hHQ==", + "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": {} }, diff --git a/package.json b/package.json index 96eec506a22..7e50d48e585 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ }, "devDependencies": { "@wasm-tool/wasm-pack-plugin": "^1.6.0", - "bootstrap": "^5.2.2", + "bootstrap": "^5.2.3", "clean-webpack-plugin": "^4.0.0", "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.7.2", From 20db887025d7426696c618057b4a1da7a02e94db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Nov 2022 11:29:44 +0000 Subject: [PATCH 24/24] Bump serde_json from 1.0.88 to 1.0.89 (#2457) Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.88 to 1.0.89.
Release notes

Sourced from serde_json's releases.

v1.0.89

  • Fix invalid JSON incorrectly accepted when a large number has no digits after decimal point (#953)
Commits
  • d2f9368 Release 1.0.89
  • 0b89836 Merge pull request #956 from dtolnay/decimal
  • 9d94e92 Require at least one digit after decimal point
  • c27b023 Add regression test for issue 953
  • 586fefb Resolve semicolon_if_nothing_returned pedantic clippy lint
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=serde_json&package-manager=cargo&previous-version=1.0.88&new-version=1.0.89)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- Cargo.lock | 4 ++-- boa_cli/Cargo.toml | 2 +- boa_engine/Cargo.toml | 2 +- boa_tester/Cargo.toml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff08ba6d95c..599fe559ebb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1609,9 +1609,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.88" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8b3801309262e8184d9687fb697586833e939767aea0dda89f5a8e650e8bd7" +checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" dependencies = [ "itoa", "ryu", diff --git a/boa_cli/Cargo.toml b/boa_cli/Cargo.toml index 38a0901dbc7..e9350998842 100644 --- a/boa_cli/Cargo.toml +++ b/boa_cli/Cargo.toml @@ -18,7 +18,7 @@ boa_parser.workspace = true rustyline = "10.0.0" rustyline-derive = "0.7.0" clap = { version = "4.0.26", features = ["derive"] } -serde_json = "1.0.88" +serde_json = "1.0.89" colored = "2.0.0" regex = "1.7.0" phf = { version = "0.11.1", features = ["macros"] } diff --git a/boa_engine/Cargo.toml b/boa_engine/Cargo.toml index 07cfd34877e..81f9957e1aa 100644 --- a/boa_engine/Cargo.toml +++ b/boa_engine/Cargo.toml @@ -38,7 +38,7 @@ boa_macros.workspace = true boa_ast.workspace = true boa_parser.workspace = true serde = { version = "1.0.147", features = ["derive", "rc"] } -serde_json = "1.0.88" +serde_json = "1.0.89" rand = "0.8.5" num-traits = "0.2.15" regress = "0.4.1" diff --git a/boa_tester/Cargo.toml b/boa_tester/Cargo.toml index 32057053d76..10f8cf5159e 100644 --- a/boa_tester/Cargo.toml +++ b/boa_tester/Cargo.toml @@ -18,7 +18,7 @@ boa_parser.workspace = true clap = { version = "4.0.26", features = ["derive"] } serde = { version = "1.0.147", features = ["derive"] } serde_yaml = "0.9.14" -serde_json = "1.0.88" +serde_json = "1.0.89" bitflags = "1.3.2" regex = "1.7.0" once_cell = "1.16.0"