Browse Source

Restructure lint lists in `boa_engine` (#2455)

This Pull Request restructures the lint deny/warn/allow lists in `boa_engine`. It adds a lot of documentation to pup functions. There are still a few clippy lints that are not fixed, mainly regarding casting of number types. Fixing those lints effectiveley would in some cases probably require bigger refactors.

This should probably wait for #2449 to be merged, because that PR already fixes that lints regarding the `Date` built-in.
pull/2462/head
raskad 2 years ago
parent
commit
5435ae0ab9
  1. 1
      Cargo.lock
  2. 1
      boa_engine/Cargo.toml
  3. 74
      boa_engine/src/bigint.rs
  4. 7
      boa_engine/src/builtins/array/array_iterator.rs
  5. 30
      boa_engine/src/builtins/array/mod.rs
  6. 25
      boa_engine/src/builtins/array_buffer/mod.rs
  7. 1
      boa_engine/src/builtins/async_function/mod.rs
  8. 2
      boa_engine/src/builtins/async_generator/mod.rs
  9. 2
      boa_engine/src/builtins/async_generator_function/mod.rs
  10. 4
      boa_engine/src/builtins/console/mod.rs
  11. 10
      boa_engine/src/builtins/dataview/mod.rs
  12. 13
      boa_engine/src/builtins/date/mod.rs
  13. 8
      boa_engine/src/builtins/date/tests.rs
  14. 55
      boa_engine/src/builtins/error/mod.rs
  15. 98
      boa_engine/src/builtins/function/mod.rs
  16. 18
      boa_engine/src/builtins/generator/mod.rs
  17. 2
      boa_engine/src/builtins/generator_function/mod.rs
  18. 27
      boa_engine/src/builtins/intl/mod.rs
  19. 47
      boa_engine/src/builtins/iterable/mod.rs
  20. 2
      boa_engine/src/builtins/json/mod.rs
  21. 11
      boa_engine/src/builtins/map/map_iterator.rs
  22. 8
      boa_engine/src/builtins/map/mod.rs
  23. 14
      boa_engine/src/builtins/map/ordered_map.rs
  24. 2
      boa_engine/src/builtins/math/tests.rs
  25. 1
      boa_engine/src/builtins/mod.rs
  26. 10
      boa_engine/src/builtins/number/conversions.rs
  27. 17
      boa_engine/src/builtins/number/mod.rs
  28. 7
      boa_engine/src/builtins/object/for_in_iterator.rs
  29. 95
      boa_engine/src/builtins/object/mod.rs
  30. 74
      boa_engine/src/builtins/promise/mod.rs
  31. 117
      boa_engine/src/builtins/regexp/mod.rs
  32. 13
      boa_engine/src/builtins/regexp/regexp_string_iterator.rs
  33. 12
      boa_engine/src/builtins/set/mod.rs
  34. 10
      boa_engine/src/builtins/set/ordered_set.rs
  35. 11
      boa_engine/src/builtins/set/set_iterator.rs
  36. 4
      boa_engine/src/builtins/string/mod.rs
  37. 27
      boa_engine/src/builtins/string/string_iterator.rs
  38. 15
      boa_engine/src/builtins/symbol/mod.rs
  39. 12
      boa_engine/src/builtins/typed_array/integer_indexed_object.rs
  40. 11
      boa_engine/src/builtins/typed_array/mod.rs
  41. 2
      boa_engine/src/builtins/weak/mod.rs
  42. 12
      boa_engine/src/bytecompiler/function.rs
  43. 68
      boa_engine/src/bytecompiler/mod.rs
  44. 2
      boa_engine/src/context/icu.rs
  45. 353
      boa_engine/src/context/intrinsics.rs
  46. 24
      boa_engine/src/context/mod.rs
  47. 28
      boa_engine/src/environments/compile.rs
  48. 38
      boa_engine/src/environments/runtime.rs
  49. 64
      boa_engine/src/error.rs
  50. 6
      boa_engine/src/job.rs
  51. 104
      boa_engine/src/lib.rs
  52. 18
      boa_engine/src/object/builtins/jsarray.rs
  53. 2
      boa_engine/src/object/builtins/jsdataview.rs
  54. 1
      boa_engine/src/object/builtins/jsproxy.rs
  55. 19
      boa_engine/src/object/builtins/jstypedarray.rs
  56. 2
      boa_engine/src/object/internal_methods/arguments.rs
  57. 4
      boa_engine/src/object/internal_methods/mod.rs
  58. 2
      boa_engine/src/object/internal_methods/proxy.rs
  59. 12
      boa_engine/src/object/jsobject.rs
  60. 337
      boa_engine/src/object/mod.rs
  61. 23
      boa_engine/src/object/operations.rs
  62. 58
      boa_engine/src/object/property_map.rs
  63. 9
      boa_engine/src/property/attribute/mod.rs
  64. 249
      boa_engine/src/property/mod.rs
  65. 3
      boa_engine/src/realm.rs
  66. 27
      boa_engine/src/string/mod.rs
  67. 17
      boa_engine/src/symbol.rs
  68. 96
      boa_engine/src/tests.rs
  69. 35
      boa_engine/src/value/conversions.rs
  70. 2
      boa_engine/src/value/display.rs
  71. 28
      boa_engine/src/value/equality.rs
  72. 15
      boa_engine/src/value/integer.rs
  73. 115
      boa_engine/src/value/mod.rs
  74. 44
      boa_engine/src/value/operations.rs
  75. 6
      boa_engine/src/value/serde_json.rs
  76. 19
      boa_engine/src/value/tests.rs
  77. 17
      boa_engine/src/value/type.rs
  78. 1
      boa_engine/src/vm/call_frame.rs
  79. 40
      boa_engine/src/vm/code_block.rs
  80. 14
      boa_engine/src/vm/flowgraph/color.rs
  81. 2
      boa_engine/src/vm/flowgraph/edge.rs
  82. 10
      boa_engine/src/vm/flowgraph/graph.rs
  83. 15
      boa_engine/src/vm/flowgraph/mod.rs
  84. 2
      boa_engine/src/vm/flowgraph/node.rs
  85. 14
      boa_engine/src/vm/mod.rs
  86. 15
      boa_engine/src/vm/opcode/mod.rs

1
Cargo.lock generated

@ -129,7 +129,6 @@ dependencies = [
"boa_macros",
"boa_parser",
"boa_profiler",
"boa_unicode",
"chrono",
"criterion",
"dyn-clone",

1
boa_engine/Cargo.toml

@ -33,7 +33,6 @@ flowgraph = []
console = []
[dependencies]
boa_unicode.workspace = true
boa_interner.workspace = true
boa_gc.workspace = true
boa_profiler.workspace = true

74
boa_engine/src/bigint.rs

@ -25,12 +25,14 @@ pub struct JsBigInt {
impl JsBigInt {
/// Create a new [`JsBigInt`].
#[inline]
#[must_use]
pub fn new<T: Into<Self>>(value: T) -> Self {
value.into()
}
/// Create a [`JsBigInt`] with value `0`.
#[inline]
#[must_use]
pub fn zero() -> Self {
Self {
inner: Rc::new(RawBigInt::zero()),
@ -39,12 +41,14 @@ impl JsBigInt {
/// Check if is zero.
#[inline]
#[must_use]
pub fn is_zero(&self) -> bool {
self.inner.is_zero()
}
/// Create a [`JsBigInt`] with value `1`.
#[inline]
#[must_use]
pub fn one() -> Self {
Self {
inner: Rc::new(RawBigInt::one()),
@ -53,12 +57,14 @@ impl JsBigInt {
/// Check if is one.
#[inline]
#[must_use]
pub fn is_one(&self) -> bool {
self.inner.is_one()
}
/// Convert bigint to string with radix.
#[inline]
#[must_use]
pub fn to_string_radix(&self, radix: u32) -> String {
self.inner.to_str_radix(radix)
}
@ -67,12 +73,14 @@ impl JsBigInt {
///
/// Returns `f64::INFINITY` if the `BigInt` is too big.
#[inline]
#[must_use]
pub fn to_f64(&self) -> f64 {
self.inner.to_f64().unwrap_or(f64::INFINITY)
}
/// Converts a string to a `BigInt` with the specified radix.
#[inline]
#[must_use]
pub fn from_string_radix(buf: &str, radix: u32) -> Option<Self> {
Some(Self {
inner: Rc::new(RawBigInt::parse_bytes(buf.as_bytes(), radix)?),
@ -86,6 +94,7 @@ impl JsBigInt {
///
/// [spec]: https://tc39.es/ecma262/#sec-stringtobigint
#[inline]
#[must_use]
pub fn from_string(mut string: &str) -> Option<Self> {
string = string.trim();
@ -115,6 +124,7 @@ impl JsBigInt {
///
/// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-equal
#[inline]
#[must_use]
pub fn same_value_zero(x: &Self, y: &Self) -> bool {
// Return BigInt::equal(x, y)
Self::equal(x, y)
@ -128,6 +138,7 @@ impl JsBigInt {
///
/// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-sameValue
#[inline]
#[must_use]
pub fn same_value(x: &Self, y: &Self) -> bool {
// Return BigInt::equal(x, y)
Self::equal(x, y)
@ -143,10 +154,12 @@ impl JsBigInt {
///
/// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-sameValueZero
#[inline]
#[must_use]
pub fn equal(x: &Self, y: &Self) -> bool {
x == y
}
/// Returns `x` to the power `y`.
#[inline]
pub fn pow(x: &Self, y: &Self) -> JsResult<Self> {
let y = y
@ -168,37 +181,27 @@ impl JsBigInt {
Ok(Self::new(x.inner.as_ref().clone().pow(y)))
}
/// Performs the `>>` operation.
#[inline]
pub fn shift_right(x: &Self, y: &Self) -> JsResult<Self> {
if let Some(n) = y.inner.to_i32() {
let inner = if n > 0 {
x.inner.as_ref().clone().shr(n as usize)
} else {
x.inner.as_ref().clone().shl(n.unsigned_abs())
};
Ok(Self::new(inner))
} else {
Err(JsNativeError::range()
match y.inner.to_i32() {
Some(n) if n > 0 => Ok(Self::new(x.inner.as_ref().clone().shr(n as usize))),
Some(n) => Ok(Self::new(x.inner.as_ref().clone().shl(n.unsigned_abs()))),
None => Err(JsNativeError::range()
.with_message("Maximum BigInt size exceeded")
.into())
.into()),
}
}
/// Performs the `<<` operation.
#[inline]
pub fn shift_left(x: &Self, y: &Self) -> JsResult<Self> {
if let Some(n) = y.inner.to_i32() {
let inner = if n > 0 {
x.inner.as_ref().clone().shl(n as usize)
} else {
x.inner.as_ref().clone().shr(n.unsigned_abs())
};
Ok(Self::new(inner))
} else {
Err(JsNativeError::range()
match y.inner.to_i32() {
Some(n) if n > 0 => Ok(Self::new(x.inner.as_ref().clone().shl(n as usize))),
Some(n) => Ok(Self::new(x.inner.as_ref().clone().shr(n.unsigned_abs()))),
None => Err(JsNativeError::range()
.with_message("Maximum BigInt size exceeded")
.into())
.into()),
}
}
@ -211,56 +214,77 @@ impl JsBigInt {
/// assert_eq!((8).mod_floor(&-3), -1);
/// ```
#[inline]
#[must_use]
pub fn mod_floor(x: &Self, y: &Self) -> Self {
Self::new(x.inner.mod_floor(&y.inner))
}
/// Performs the `+` operation.
#[inline]
#[must_use]
pub fn add(x: &Self, y: &Self) -> Self {
Self::new(x.inner.as_ref().clone().add(y.inner.as_ref()))
}
/// Performs the `-` operation.
#[inline]
#[must_use]
pub fn sub(x: &Self, y: &Self) -> Self {
Self::new(x.inner.as_ref().clone().sub(y.inner.as_ref()))
}
/// Performs the `*` operation.
#[inline]
#[must_use]
pub fn mul(x: &Self, y: &Self) -> Self {
Self::new(x.inner.as_ref().clone().mul(y.inner.as_ref()))
}
/// Performs the `/` operation.
#[inline]
#[must_use]
pub fn div(x: &Self, y: &Self) -> Self {
Self::new(x.inner.as_ref().clone().div(y.inner.as_ref()))
}
/// Performs the `%` operation.
#[inline]
#[must_use]
pub fn rem(x: &Self, y: &Self) -> Self {
Self::new(x.inner.as_ref().clone().rem(y.inner.as_ref()))
}
/// Performs the `&` operation.
#[inline]
#[must_use]
pub fn bitand(x: &Self, y: &Self) -> Self {
Self::new(x.inner.as_ref().clone().bitand(y.inner.as_ref()))
}
/// Performs the `|` operation.
#[inline]
#[must_use]
pub fn bitor(x: &Self, y: &Self) -> Self {
Self::new(x.inner.as_ref().clone().bitor(y.inner.as_ref()))
}
/// Performs the `^` operation.
#[inline]
#[must_use]
pub fn bitxor(x: &Self, y: &Self) -> Self {
Self::new(x.inner.as_ref().clone().bitxor(y.inner.as_ref()))
}
/// Performs the unary `-` operation.
#[inline]
#[must_use]
pub fn neg(x: &Self) -> Self {
Self::new(x.as_inner().neg())
}
/// Performs the unary `!` operation.
#[inline]
#[must_use]
pub fn not(x: &Self) -> Self {
Self::new(!x.as_inner())
}
@ -386,6 +410,7 @@ impl From<usize> for JsBigInt {
}
}
/// The error indicates that the conversion from [`f64`] to [`JsBigInt`] failed.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct TryFromF64Error;
@ -407,10 +432,7 @@ impl TryFrom<f64> for JsBigInt {
if !Number::equal(n.trunc(), n) {
return Err(TryFromF64Error);
}
match RawBigInt::from_f64(n) {
Some(bigint) => Ok(Self::new(bigint)),
None => Err(TryFromF64Error),
}
RawBigInt::from_f64(n).map_or(Err(TryFromF64Error), |bigint| Ok(Self::new(bigint)))
}
}

7
boa_engine/src/builtins/array/array_iterator.rs

@ -1,3 +1,10 @@
//! This module implements the `ArrayIterator` object.
//!
//! More information:
//! - [ECMAScript reference][spec]
//!
//! [spec]: https://tc39.es/ecma262/#sec-array-iterator-objects
use crate::{
builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Array, JsValue},
error::JsNativeError,

30
boa_engine/src/builtins/array/mod.rs

@ -236,10 +236,8 @@ impl Array {
// 3. Let A be ! MakeBasicObject(« [[Prototype]], [[Extensible]] »).
// 4. Set A.[[Prototype]] to proto.
// 5. Set A.[[DefineOwnProperty]] as specified in 10.4.2.1.
let prototype = match prototype {
Some(prototype) => prototype,
None => context.intrinsics().constructors().array().prototype(),
};
let prototype =
prototype.unwrap_or_else(|| context.intrinsics().constructors().array().prototype());
let array = JsObject::from_proto_and_data(prototype, ObjectData::array());
// 6. Perform ! OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor { [[Value]]: 𝔽(length), [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }).
@ -380,15 +378,15 @@ impl Array {
return Self::array_create(length, None, context);
}
// 7. If IsConstructor(C) is false, throw a TypeError exception.
if let Some(c) = c.as_constructor() {
// 8. Return ? Construct(C, « 𝔽(length) »).
c.construct(&[JsValue::new(length)], Some(c), context)
} else {
Err(JsNativeError::typ()
.with_message("Symbol.species must be a constructor")
.into())
return c.construct(&[JsValue::new(length)], Some(c), context);
}
// 7. If IsConstructor(C) is false, throw a TypeError exception.
Err(JsNativeError::typ()
.with_message("Symbol.species must be a constructor")
.into())
}
/// `Array.from(arrayLike)`
@ -670,7 +668,7 @@ impl Array {
let mut n = 0;
// 4. Prepend O to items.
// 5. For each element E of items, do
for item in [JsValue::new(obj)].iter().chain(args.iter()) {
for item in std::iter::once(&JsValue::new(obj)).chain(args.iter()) {
// a. Let spreadable be ? IsConcatSpreadable(E).
let spreadable = Self::is_concat_spreadable(item, context)?;
// b. If spreadable is true, then
@ -1776,13 +1774,13 @@ impl Array {
}
// iii. Let shouldFlatten be false
let mut should_flatten = false;
// iv. If depth > 0, then
if depth > 0 {
let should_flatten = if depth > 0 {
// 1. Set shouldFlatten to ? IsArray(element).
should_flatten = element.is_array()?;
}
element.is_array()?
} else {
false
};
// v. If shouldFlatten is true
if should_flatten {

25
boa_engine/src/builtins/array_buffer/mod.rs

@ -1,3 +1,12 @@
//! This module implements the global `ArrayBuffer` object.
//!
//! More information:
//! - [ECMAScript reference][spec]
//! - [MDN documentation][mdn]
//!
//! [spec]: https://tc39.es/ecma262/#sec-arraybuffer-objects
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
#[cfg(test)]
mod tests;
@ -19,15 +28,21 @@ use boa_profiler::Profiler;
use num_traits::{Signed, ToPrimitive};
use tap::{Conv, Pipe};
/// The internal representation of an `ArrayBuffer` object.
#[derive(Debug, Clone, Trace, Finalize)]
pub struct ArrayBuffer {
/// The `[[ArrayBufferData]]` internal slot.
pub array_buffer_data: Option<Vec<u8>>,
/// The `[[ArrayBufferByteLength]]` internal slot.
pub array_buffer_byte_length: u64,
/// The `[[ArrayBufferDetachKey]]` internal slot.
pub array_buffer_detach_key: JsValue,
}
impl ArrayBuffer {
pub(crate) fn array_buffer_byte_length(&self) -> u64 {
pub(crate) const fn array_buffer_byte_length(&self) -> u64 {
self.array_buffer_byte_length
}
}
@ -349,7 +364,7 @@ impl ArrayBuffer {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-isdetachedbuffer
pub(crate) fn is_detached_buffer(&self) -> bool {
pub(crate) const fn is_detached_buffer(&self) -> bool {
// 1. If arrayBuffer.[[ArrayBufferData]] is null, return true.
// 2. Return false.
self.array_buffer_data.is_none()
@ -407,7 +422,7 @@ impl ArrayBuffer {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-isunclampedintegerelementtype
fn is_unclamped_integer_element_type(t: TypedArrayKind) -> bool {
const fn is_unclamped_integer_element_type(t: TypedArrayKind) -> bool {
// 1. If type is Int8, Uint8, Int16, Uint16, Int32, or Uint32, return true.
// 2. Return false.
matches!(
@ -427,7 +442,7 @@ impl ArrayBuffer {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-isbigintelementtype
fn is_big_int_element_type(t: TypedArrayKind) -> bool {
const fn is_big_int_element_type(t: TypedArrayKind) -> bool {
// 1. If type is BigUint64 or BigInt64, return true.
// 2. Return false.
matches!(t, TypedArrayKind::BigUint64 | TypedArrayKind::BigInt64)
@ -441,7 +456,7 @@ impl ArrayBuffer {
/// [spec]: https://tc39.es/ecma262/#sec-isnotearconfiguration
// TODO: Allow unused function until shared array buffers are implemented.
#[allow(dead_code)]
fn is_no_tear_configuration(t: TypedArrayKind, order: SharedMemoryOrder) -> bool {
const fn is_no_tear_configuration(t: TypedArrayKind, order: SharedMemoryOrder) -> bool {
// 1. If ! IsUnclampedIntegerElementType(type) is true, return true.
if Self::is_unclamped_integer_element_type(t) {
return true;

1
boa_engine/src/builtins/async_function/mod.rs

@ -19,6 +19,7 @@ use crate::{
};
use boa_profiler::Profiler;
/// The internal representation of an `AsyncFunction` object.
#[derive(Debug, Clone, Copy)]
pub struct AsyncFunction;

2
boa_engine/src/builtins/async_generator/mod.rs

@ -48,7 +48,7 @@ pub(crate) struct AsyncGeneratorRequest {
capability: PromiseCapability,
}
/// The internal representation on an `AsyncGenerator` object.
/// The internal representation of an `AsyncGenerator` object.
#[derive(Debug, Clone, Finalize, Trace)]
pub struct AsyncGenerator {
/// The `[[AsyncGeneratorState]]` internal slot.

2
boa_engine/src/builtins/async_generator_function/mod.rs

@ -18,7 +18,7 @@ use crate::{
};
use boa_profiler::Profiler;
/// The internal representation on a `AsyncGeneratorFunction` object.
/// The internal representation of an `AsyncGeneratorFunction` object.
#[derive(Debug, Clone, Copy)]
pub struct AsyncGeneratorFunction;

4
boa_engine/src/builtins/console/mod.rs

@ -29,7 +29,7 @@ use tap::{Conv, Pipe};
/// This represents the different types of log messages.
#[derive(Debug)]
pub enum LogMessage {
enum LogMessage {
Log(String),
Info(String),
Warn(String),
@ -37,7 +37,7 @@ pub enum LogMessage {
}
/// Helper function for logging messages.
pub(crate) fn logger(msg: LogMessage, console_state: &Console) {
fn logger(msg: LogMessage, console_state: &Console) {
let indent = 2 * console_state.groups.len();
match msg {

10
boa_engine/src/builtins/dataview/mod.rs

@ -1,3 +1,12 @@
//! This module implements the global `DataView` object.
//!
//! More information:
//! - [ECMAScript reference][spec]
//! - [MDN documentation][mdn]
//!
//! [spec]: https://tc39.es/ecma262/#sec-dataview-objects
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView
use crate::{
builtins::{array_buffer::SharedMemoryOrder, typed_array::TypedArrayKind, BuiltIn, JsArgs},
context::intrinsics::StandardConstructors,
@ -14,6 +23,7 @@ use crate::{
use boa_gc::{Finalize, Trace};
use tap::{Conv, Pipe};
/// The internal representation of a `DataView` object.
#[derive(Debug, Clone, Trace, Finalize)]
pub struct DataView {
pub(crate) viewed_array_buffer: JsObject,

13
boa_engine/src/builtins/date/mod.rs

@ -1,5 +1,15 @@
//! This module implements the global `Date` object.
//!
//! More information:
//! - [ECMAScript reference][spec]
//! - [MDN documentation][mdn]
//!
//! [spec]: https://tc39.es/ecma262/#sec-date-objects
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
mod utils;
use utils::{make_date, make_day, make_time, replace_params, time_clip, DateParameters};
#[cfg(test)]
mod tests;
@ -58,13 +68,14 @@ pub(super) fn this_time_value(value: &JsValue) -> JsResult<Option<NaiveDateTime>
.0)
}
/// The internal representation of a `Date` object.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Date(Option<NaiveDateTime>);
impl Date {
/// Creates a new `Date`.
#[inline]
pub(crate) fn new(dt: Option<NaiveDateTime>) -> Self {
pub(crate) const fn new(dt: Option<NaiveDateTime>) -> Self {
Self(dt)
}

8
boa_engine/src/builtins/date/tests.rs

@ -194,7 +194,7 @@ fn date_ctor_parse_call() {
let date_time = forward_val(&mut context, "Date.parse('2020-06-08T09:16:15.779-07:30')");
assert_eq!(JsValue::new(1591634775779i64), date_time.unwrap());
assert_eq!(JsValue::new(1_591_634_775_779_i64), date_time.unwrap());
}
#[test]
@ -203,7 +203,7 @@ fn date_ctor_utc_call() {
let date_time = forward_val(&mut context, "Date.UTC(2020, 6, 8, 9, 16, 15, 779)");
assert_eq!(JsValue::new(1594199775779i64), date_time.unwrap());
assert_eq!(JsValue::new(1_594_199_775_779_i64), date_time.unwrap());
}
#[test]
@ -1379,7 +1379,7 @@ fn date_proto_value_of() {
"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).valueOf()",
)
.unwrap();
assert_eq!(JsValue::new(1594199775779i64), actual);
assert_eq!(JsValue::new(1_594_199_775_779_i64), actual);
}
#[test]
@ -1391,7 +1391,7 @@ fn date_neg() {
"-new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779))",
)
.unwrap();
assert_eq!(JsValue::new(-1594199775779i64), actual);
assert_eq!(JsValue::new(-1_594_199_775_779_i64), actual);
}
#[test]

55
boa_engine/src/builtins/error/mod.rs

@ -60,13 +60,68 @@ use super::JsArgs;
/// [spec]: https://tc39.es/ecma262/#sec-error-objects
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ErrorKind {
/// The `AggregateError` object type.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-aggregate-error-objects
Aggregate,
/// The `Error` object type.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-error-objects
Error,
/// The `EvalError` type.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-evalerror
Eval,
/// The `TypeError` type.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-typeerror
Type,
/// The `RangeError` type.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-rangeerror
Range,
/// The `ReferenceError` type.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-referenceerror
Reference,
/// The `SyntaxError` type.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-syntaxerror
Syntax,
/// The `URIError` type.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-urierror
Uri,
}

98
boa_engine/src/builtins/function/mod.rs

@ -73,7 +73,10 @@ mod sealed {
pub trait Sealed {}
impl<T: Copy> Sealed for T {}
}
/// This trait is implemented by any type that implements [`core::marker::Copy`].
pub trait DynCopy: sealed::Sealed {}
impl<T: Copy> DynCopy for T {}
/// Trait representing a native built-in closure.
@ -94,26 +97,41 @@ impl<T> ClosureFunctionSignature for T where
// Allows cloning Box<dyn ClosureFunctionSignature>
dyn_clone::clone_trait_object!(ClosureFunctionSignature);
/// Represents the `[[ThisMode]]` internal slot of function objects.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ecmascript-function-objects
#[derive(Debug, Trace, Finalize, PartialEq, Eq, Clone)]
pub enum ThisMode {
/// The `this` value refers to the `this` value of a lexically enclosing function.
Lexical,
/// The `this` value is used exactly as provided by an invocation of the function.
Strict,
/// The `this` value of `undefined` or `null` is interpreted as a reference to the global object,
/// and any other `this` value is first passed to `ToObject`.
Global,
}
impl ThisMode {
/// Returns `true` if the this mode is `Lexical`.
pub fn is_lexical(&self) -> bool {
#[must_use]
pub const fn is_lexical(&self) -> bool {
matches!(self, Self::Lexical)
}
/// Returns `true` if the this mode is `Strict`.
pub fn is_strict(&self) -> bool {
#[must_use]
pub const fn is_strict(&self) -> bool {
matches!(self, Self::Strict)
}
/// Returns `true` if the this mode is `Global`.
pub fn is_global(&self) -> bool {
#[must_use]
pub const fn is_global(&self) -> bool {
matches!(self, Self::Global)
}
}
@ -126,18 +144,23 @@ impl ThisMode {
/// [spec]: https://tc39.es/ecma262/#sec-ecmascript-function-objects
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ConstructorKind {
/// The class constructor is not derived.
Base,
/// The class constructor is a derived class constructor.
Derived,
}
impl ConstructorKind {
/// Returns `true` if the constructor kind is `Base`.
pub fn is_base(&self) -> bool {
#[must_use]
pub const fn is_base(&self) -> bool {
matches!(self, Self::Base)
}
/// Returns `true` if the constructor kind is `Derived`.
pub fn is_derived(&self) -> bool {
#[must_use]
pub const fn is_derived(&self) -> bool {
matches!(self, Self::Derived)
}
}
@ -150,7 +173,10 @@ impl ConstructorKind {
/// [spec]: https://tc39.es/ecma262/#sec-classfielddefinition-record-specification-type
#[derive(Clone, Debug, Finalize)]
pub enum ClassFieldDefinition {
/// A class field definition with a `string` or `symbol` as a name.
Public(PropertyKey, JsFunction),
/// A class field definition with a private name.
Private(Sym, JsFunction),
}
@ -216,17 +242,33 @@ impl Captures {
/// <https://tc39.es/ecma262/#sec-ecmascript-function-objects>
#[derive(Finalize)]
pub enum Function {
/// A rust function.
Native {
/// The rust function.
function: NativeFunctionSignature,
/// The kind of the function constructor if it is a constructor.
constructor: Option<ConstructorKind>,
},
/// A rust function that may contain captured values.
Closure {
/// The rust function.
function: Box<dyn ClosureFunctionSignature>,
/// The kind of the function constructor if it is a constructor.
constructor: Option<ConstructorKind>,
/// The captured values.
captures: Captures,
},
/// A bytecode function.
Ordinary {
/// The code block containing the compiled function.
code: Gc<crate::vm::CodeBlock>,
/// The `[[Environment]]` internal slot.
environments: DeclarativeEnvironmentStack,
/// The `[[ConstructorKind]]` internal slot.
@ -241,22 +283,42 @@ pub enum Function {
/// The `[[PrivateMethods]]` internal slot.
private_methods: Vec<(Sym, PrivateElement)>,
},
/// A bytecode async function.
Async {
/// The code block containing the compiled function.
code: Gc<crate::vm::CodeBlock>,
/// The `[[Environment]]` internal slot.
environments: DeclarativeEnvironmentStack,
/// The `[[HomeObject]]` internal slot.
home_object: Option<JsObject>,
/// The promise capability record of the async function.
promise_capability: PromiseCapability,
},
/// A bytecode generator function.
Generator {
/// The code block containing the compiled function.
code: Gc<crate::vm::CodeBlock>,
/// The `[[Environment]]` internal slot.
environments: DeclarativeEnvironmentStack,
/// The `[[HomeObject]]` internal slot.
home_object: Option<JsObject>,
},
/// A bytecode async generator function.
AsyncGenerator {
/// The code block containing the compiled function.
code: Gc<crate::vm::CodeBlock>,
/// The `[[Environment]]` internal slot.
environments: DeclarativeEnvironmentStack,
/// The `[[HomeObject]]` internal slot.
home_object: Option<JsObject>,
},
@ -311,7 +373,7 @@ impl Function {
}
/// Returns true if the function object is a derived constructor.
pub(crate) fn is_derived_constructor(&self) -> bool {
pub(crate) const fn is_derived_constructor(&self) -> bool {
if let Self::Ordinary {
constructor_kind, ..
} = self
@ -323,7 +385,7 @@ impl Function {
}
/// Returns a reference to the function `[[HomeObject]]` slot if present.
pub(crate) fn get_home_object(&self) -> Option<&JsObject> {
pub(crate) const fn get_home_object(&self) -> Option<&JsObject> {
match self {
Self::Ordinary { home_object, .. }
| Self::Async { home_object, .. }
@ -390,7 +452,7 @@ impl Function {
}
/// Returns the promise capability if the function is an async function.
pub(crate) fn get_promise_capability(&self) -> Option<&PromiseCapability> {
pub(crate) const fn get_promise_capability(&self) -> Option<&PromiseCapability> {
if let Self::Async {
promise_capability, ..
} = self
@ -461,11 +523,12 @@ pub(crate) fn make_builtin_fn<N>(
);
}
/// The internal representation of a `Function` object.
#[derive(Debug, Clone, Copy)]
pub struct BuiltInFunctionObject;
impl BuiltInFunctionObject {
pub const LENGTH: usize = 1;
const LENGTH: usize = 1;
/// `Function ( p1, p2, … , pn, body )`
///
@ -946,13 +1009,12 @@ fn set_function_name(
let mut name = match name {
PropertyKey::Symbol(sym) => {
// a. Let description be name's [[Description]] value.
if let Some(desc) = sym.description() {
// c. Else, set name to the string-concatenation of "[", description, and "]".
js_string!(utf16!("["), &desc, utf16!("]"))
} else {
// b. If description is undefined, set name to the empty String.
js_string!()
}
// b. If description is undefined, set name to the empty String.
// c. Else, set name to the string-concatenation of "[", description, and "]".
sym.description().map_or_else(
|| js_string!(),
|desc| js_string!(utf16!("["), &desc, utf16!("]")),
)
}
PropertyKey::String(string) => string.clone(),
PropertyKey::Index(index) => js_string!(format!("{}", index)),
@ -1038,12 +1100,12 @@ impl BoundFunction {
}
/// Get a reference to the bound function's this.
pub fn this(&self) -> &JsValue {
pub const fn this(&self) -> &JsValue {
&self.this
}
/// Get a reference to the bound function's target function.
pub fn target_function(&self) -> &JsObject {
pub const fn target_function(&self) -> &JsObject {
&self.target_function
}

18
boa_engine/src/builtins/generator/mod.rs

@ -44,7 +44,7 @@ pub(crate) struct GeneratorContext {
pub(crate) stack: Vec<JsValue>,
}
/// The internal representation on a `Generator` object.
/// The internal representation of a `Generator` object.
#[derive(Debug, Clone, Finalize, Trace)]
pub struct Generator {
/// The `[[GeneratorState]]` internal slot.
@ -148,14 +148,14 @@ impl Generator {
context: &mut Context,
) -> JsResult<JsValue> {
// 1. Return ? GeneratorResume(this value, value, empty).
match this.as_object() {
Some(obj) if obj.is_generator() => {
Self::generator_resume(obj, args.get_or_undefined(0), context)
}
_ => Err(JsNativeError::typ()
.with_message("Generator.prototype.next called on non generator")
.into()),
}
this.as_object().map_or_else(
|| {
Err(JsNativeError::typ()
.with_message("Generator.prototype.next called on non generator")
.into())
},
|obj| Self::generator_resume(obj, args.get_or_undefined(0), context),
)
}
/// `Generator.prototype.return ( value )`

2
boa_engine/src/builtins/generator_function/mod.rs

@ -23,7 +23,7 @@ use crate::{
};
use boa_profiler::Profiler;
/// The internal representation on a `Generator` object.
/// The internal representation of a `Generator` object.
#[derive(Debug, Clone, Copy)]
pub struct GeneratorFunction;

27
boa_engine/src/builtins/intl/mod.rs

@ -104,6 +104,7 @@ struct MatcherRecord {
///
/// [spec]: https://tc39.es/ecma402/#sec-defaultlocale
fn default_locale(canonicalizer: &LocaleCanonicalizer) -> Locale {
#[allow(clippy::string_lit_as_bytes)]
sys_locale::get_locale()
.and_then(|loc| loc.parse::<Locale>().ok())
.tap_some_mut(|loc| canonicalize_unicode_locale_id(loc, canonicalizer))
@ -281,10 +282,7 @@ fn unicode_extension_components(extension: &str) -> UniExtRecord {
let e = extension[k..].find('-');
// b. If e = -1, let len be size - k; else let len be e - k.
let len = match e {
Some(pos) => pos,
None => size - k,
};
let len = e.unwrap_or(size - k);
// c. Let subtag be the String value equal to the substring of extension consisting of the
// code units at indices k (inclusive) through k + len (exclusive).
@ -587,24 +585,21 @@ fn resolve_locale(
for &key in relevant_extension_keys {
// a. Let foundLocaleData be localeData.[[<foundLocale>]].
// TODO b. Assert: Type(foundLocaleData) is Record.
let found_locale_data = match locale_data.get(&found_locale) {
Some(locale_value) => locale_value.clone(),
None => FxHashMap::default(),
};
let found_locale_data = locale_data
.get(&found_locale)
.map_or_else(FxHashMap::default, Clone::clone);
// c. Let keyLocaleData be foundLocaleData.[[<key>]].
// TODO d. Assert: Type(keyLocaleData) is List.
let key_locale_data = match found_locale_data.get(key) {
Some(locale_vec) => locale_vec.clone(),
None => Vec::new(),
};
let key_locale_data = found_locale_data
.get(key)
.map_or_else(Vec::new, Clone::clone);
// e. Let value be keyLocaleData[0].
// TODO f. Assert: Type(value) is either String or Null.
let mut value = match key_locale_data.get(0) {
Some(first_elt) => first_elt.clone().into(),
None => JsValue::null(),
};
let mut value = key_locale_data
.get(0)
.map_or_else(JsValue::null, |first_elt| first_elt.clone().into());
// g. Let supportedExtensionAddition be "".
let mut supported_extension_addition = String::new();

47
boa_engine/src/builtins/iterable/mod.rs

@ -1,3 +1,5 @@
//! This module implements the global iterator prototype objects.
mod async_from_sync_iterator;
use crate::{
@ -17,25 +19,34 @@ use boa_profiler::Profiler;
pub(crate) use async_from_sync_iterator::AsyncFromSyncIterator;
/// The built-in iterator prototypes.
#[derive(Debug, Default)]
pub struct IteratorPrototypes {
/// %IteratorPrototype%
/// The `IteratorPrototype` object.
iterator_prototype: JsObject,
/// %AsyncIteratorPrototype%
/// The `AsyncIteratorPrototype` object.
async_iterator_prototype: JsObject,
/// %AsyncFromSyncIteratorPrototype%
/// The `AsyncFromSyncIteratorPrototype` prototype object.
async_from_sync_iterator_prototype: JsObject,
/// %MapIteratorPrototype%
/// The `ArrayIteratorPrototype` prototype object.
array_iterator: JsObject,
/// %SetIteratorPrototype%
/// The `SetIteratorPrototype` prototype object.
set_iterator: JsObject,
/// %StringIteratorPrototype%
/// The `StringIteratorPrototype` prototype object.
string_iterator: JsObject,
/// %RegExpStringIteratorPrototype%
/// The `RegExpStringIteratorPrototype` prototype object.
regexp_string_iterator: JsObject,
/// %MapIteratorPrototype%
/// The `MapIteratorPrototype` prototype object.
map_iterator: JsObject,
/// %ForInIteratorPrototype%
/// The `ForInIteratorPrototype` prototype object.
for_in_iterator: JsObject,
}
@ -62,46 +73,55 @@ impl IteratorPrototypes {
}
}
/// Returns the `ArrayIteratorPrototype` object.
#[inline]
pub fn array_iterator(&self) -> JsObject {
self.array_iterator.clone()
}
/// Returns the `IteratorPrototype` object.
#[inline]
pub fn iterator_prototype(&self) -> JsObject {
self.iterator_prototype.clone()
}
/// Returns the `AsyncIteratorPrototype` object.
#[inline]
pub fn async_iterator_prototype(&self) -> JsObject {
self.async_iterator_prototype.clone()
}
/// Returns the `AsyncFromSyncIteratorPrototype` object.
#[inline]
pub fn async_from_sync_iterator_prototype(&self) -> JsObject {
self.async_from_sync_iterator_prototype.clone()
}
/// Returns the `SetIteratorPrototype` object.
#[inline]
pub fn set_iterator(&self) -> JsObject {
self.set_iterator.clone()
}
/// Returns the `StringIteratorPrototype` object.
#[inline]
pub fn string_iterator(&self) -> JsObject {
self.string_iterator.clone()
}
/// Returns the `RegExpStringIteratorPrototype` object.
#[inline]
pub fn regexp_string_iterator(&self) -> JsObject {
self.regexp_string_iterator.clone()
}
/// Returns the `MapIteratorPrototype` object.
#[inline]
pub fn map_iterator(&self) -> JsObject {
self.map_iterator.clone()
}
/// Returns the `ForInIteratorPrototype` object.
#[inline]
pub fn for_in_iterator(&self) -> JsObject {
self.for_in_iterator.clone()
@ -132,7 +152,10 @@ pub fn create_iter_result_object(value: JsValue, done: bool, context: &mut Conte
/// Iterator hint for `GetIterator`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IteratorHint {
/// Hints that the iterator should be sync.
Sync,
/// Hints that the iterator should be async.
Async,
}
@ -311,19 +334,19 @@ impl IteratorRecord {
/// Get the `[[Iterator]]` field of the `IteratorRecord`.
#[inline]
pub(crate) fn iterator(&self) -> &JsObject {
pub(crate) const fn iterator(&self) -> &JsObject {
&self.iterator
}
/// Get the `[[NextMethod]]` field of the `IteratorRecord`.
#[inline]
pub(crate) fn next_method(&self) -> &JsValue {
pub(crate) const fn next_method(&self) -> &JsValue {
&self.next_method
}
/// Get the `[[Done]]` field of the `IteratorRecord`.
#[inline]
pub(crate) fn done(&self) -> bool {
pub(crate) const fn done(&self) -> bool {
self.done
}

2
boa_engine/src/builtins/json/mod.rs

@ -83,7 +83,7 @@ where
fn size_hint(&self) -> (usize, Option<usize>) {
type SizeHint = (usize, Option<usize>);
fn add(a: SizeHint, b: SizeHint) -> SizeHint {
const fn add(a: SizeHint, b: SizeHint) -> SizeHint {
let min = a.0.saturating_add(b.0);
let max = match (a.1, b.1) {
(Some(x), Some(y)) => x.checked_add(y),

11
boa_engine/src/builtins/map/map_iterator.rs

@ -1,3 +1,10 @@
//! This module implements the `MapIterator` object.
//!
//! More information:
//! - [ECMAScript reference][spec]
//!
//! [spec]: https://tc39.es/ecma262/#sec-map-iterator-objects
use super::ordered_map::MapLock;
use crate::{
builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Array, JsValue},
@ -15,7 +22,7 @@ use boa_profiler::Profiler;
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-array-iterator-objects
/// [spec]: https://tc39.es/ecma262/#sec-map-iterator-objects
#[derive(Debug, Clone, Finalize, Trace)]
pub struct MapIterator {
iterated_map: Option<JsObject>,
@ -86,7 +93,7 @@ impl MapIterator {
if let Some(obj) = map_iterator.iterated_map.take() {
let e = {
let map = obj.borrow();
let entries = map.as_map_ref().expect("iterator should only iterate maps");
let entries = map.as_map().expect("iterator should only iterate maps");
let len = entries.full_len();
loop {
let element = entries

8
boa_engine/src/builtins/map/mod.rs

@ -34,7 +34,7 @@ pub mod ordered_map;
mod tests;
#[derive(Debug, Clone)]
pub(crate) struct Map(OrderedMap<JsValue>);
pub(crate) struct Map;
impl BuiltIn for Map {
const NAME: &'static str = "Map";
@ -334,7 +334,7 @@ impl Map {
if let JsValue::Object(ref object) = this {
// 2. Perform ? RequireInternalSlot(M, [[MapData]]).
// 3. Let entries be the List that is M.[[MapData]].
if let Some(map) = object.borrow().as_map_ref() {
if let Some(map) = object.borrow().as_map() {
// 4. For each Record { [[Key]], [[Value]] } p of entries, do
// a. If p.[[Key]] is not empty and SameValueZero(p.[[Key]], key) is true, return p.[[Value]].
// 5. Return undefined.
@ -406,7 +406,7 @@ impl Map {
if let JsValue::Object(ref object) = this {
// 2. Perform ? RequireInternalSlot(M, [[MapData]]).
// 3. Let entries be the List that is M.[[MapData]].
if let Some(map) = object.borrow().as_map_ref() {
if let Some(map) = object.borrow().as_map() {
// 4. For each Record { [[Key]], [[Value]] } p of entries, do
// a. If p.[[Key]] is not empty and SameValueZero(p.[[Key]], key) is true, return true.
// 5. Return false.
@ -470,7 +470,7 @@ impl Map {
loop {
let arguments = {
let map = map.borrow();
let map = map.as_map_ref().expect("checked that `this` was a map");
let map = map.as_map().expect("checked that `this` was a map");
if index < map.full_len() {
map.get_index(index)
.map(|(k, v)| [v.clone(), k.clone(), this.clone()])

14
boa_engine/src/builtins/map/ordered_map.rs

@ -1,3 +1,5 @@
//! Implements a map type that preserves insertion order.
use crate::{object::JsObject, JsValue};
use boa_gc::{custom_trace, Finalize, Trace};
use indexmap::{Equivalent, IndexMap};
@ -18,8 +20,8 @@ enum MapKey {
impl Hash for MapKey {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
MapKey::Key(v) => v.hash(state),
MapKey::Empty(e) => e.hash(state),
Self::Key(v) => v.hash(state),
Self::Empty(e) => e.hash(state),
}
}
}
@ -66,6 +68,8 @@ impl<V> Default for OrderedMap<V> {
}
impl<V> OrderedMap<V> {
/// Creates a new empty `OrderedMap`.
#[must_use]
pub fn new() -> Self {
Self {
map: IndexMap::new(),
@ -74,6 +78,8 @@ impl<V> OrderedMap<V> {
}
}
/// Creates a new empty `OrderedMap` with the specified capacity.
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
map: IndexMap::with_capacity(capacity),
@ -85,6 +91,7 @@ impl<V> OrderedMap<V> {
/// Return the number of key-value pairs in the map, including empty values.
///
/// Computes in **O(1)** time.
#[must_use]
pub fn full_len(&self) -> usize {
self.map.len()
}
@ -92,6 +99,7 @@ impl<V> OrderedMap<V> {
/// Gets the number of key-value pairs in the map, not including empty values.
///
/// Computes in **O(1)** time.
#[must_use]
pub fn len(&self) -> usize {
self.map.len() - self.empty_count
}
@ -99,6 +107,7 @@ impl<V> OrderedMap<V> {
/// Returns true if the map contains no elements.
///
/// Computes in **O(1)** time.
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
@ -160,6 +169,7 @@ impl<V> OrderedMap<V> {
/// Valid indices are `0 <= index < self.full_len()`.
///
/// Computes in O(1) time.
#[must_use]
pub fn get_index(&self, index: usize) -> Option<(&JsValue, &V)> {
if let (MapKey::Key(key), Some(value)) = self.map.get_index(index)? {
Some((key, value))

2
boa_engine/src/builtins/math/tests.rs

@ -394,7 +394,7 @@ fn hypot() {
assert_eq!(b.to_number(&mut context).unwrap(), 5f64);
assert_eq!(c.to_number(&mut context).unwrap(), 13f64);
assert_eq!(d.to_number(&mut context).unwrap(), 7.071_067_811_865_475_5);
assert_eq!(e.to_number(&mut context).unwrap(), 8.774964387392123);
assert_eq!(e.to_number(&mut context).unwrap(), 8.774_964_387_392_123);
assert!(f.to_number(&mut context).unwrap().is_infinite());
assert_eq!(g.to_number(&mut context).unwrap(), 12f64);
}

1
boa_engine/src/builtins/mod.rs

@ -205,6 +205,7 @@ pub fn init(context: &mut Context) {
init_builtin::<console::Console>(context);
}
/// A utility trait to make working with function arguments easier.
pub trait JsArgs {
/// Utility function to `get` a parameter from a `[JsValue]` or default to `JsValue::Undefined`
/// if `get` returns `None`.

10
boa_engine/src/builtins/number/conversions.rs

@ -4,10 +4,10 @@
#[inline]
#[allow(clippy::float_cmp)]
pub(crate) fn f64_to_int32(number: f64) -> i32 {
const SIGN_MASK: u64 = 0x8000000000000000;
const EXPONENT_MASK: u64 = 0x7FF0000000000000;
const SIGNIFICAND_MASK: u64 = 0x000FFFFFFFFFFFFF;
const HIDDEN_BIT: u64 = 0x0010000000000000;
const SIGN_MASK: u64 = 0x8000_0000_0000_0000;
const EXPONENT_MASK: u64 = 0x7FF0_0000_0000_0000;
const SIGNIFICAND_MASK: u64 = 0x000F_FFFF_FFFF_FFFF;
const HIDDEN_BIT: u64 = 0x0010_0000_0000_0000;
const PHYSICAL_SIGNIFICAND_SIZE: i32 = 52; // Excludes the hidden bit.
const SIGNIFICAND_SIZE: i32 = 53;
@ -71,7 +71,7 @@ pub(crate) fn f64_to_int32(number: f64) -> i32 {
return 0;
}
(significand(number) << exponent) & 0xFFFFFFFF
(significand(number) << exponent) & 0xFFFF_FFFF
};
(sign(number) * (bits as i64)) as i32

17
boa_engine/src/builtins/number/mod.rs

@ -992,15 +992,14 @@ impl Number {
args: &[JsValue],
_ctx: &mut Context,
) -> JsResult<JsValue> {
Ok(JsValue::new(if let Some(val) = args.get(0) {
match val {
JsValue::Integer(_) => true,
JsValue::Rational(number) => number.is_finite(),
_ => false,
}
} else {
false
}))
// 1. If number is not a Number, return false.
// 2. If number is not finite, return false.
// 3. Otherwise, return true.
Ok(JsValue::new(args.get(0).map_or(false, |val| match val {
JsValue::Integer(_) => true,
JsValue::Rational(number) => number.is_finite(),
_ => false,
})))
}
/// `Number.isInteger( number )`

7
boa_engine/src/builtins/object/for_in_iterator.rs

@ -1,3 +1,10 @@
//! This module implements the `ForInIterator` object.
//!
//! More information:
//! - [ECMAScript reference][spec]
//!
//! [spec]: https://tc39.es/ecma262/#sec-for-in-iterator-objects
use crate::{
builtins::{function::make_builtin_fn, iterable::create_iter_result_object},
error::JsNativeError,

95
boa_engine/src/builtins/object/mod.rs

@ -503,60 +503,61 @@ impl Object {
desc: Option<PropertyDescriptor>,
context: &mut Context,
) -> JsValue {
match desc {
// 1. If Desc is undefined, return undefined.
None => JsValue::undefined(),
Some(desc) => {
// 2. Let obj be ! OrdinaryObjectCreate(%Object.prototype%).
// 3. Assert: obj is an extensible ordinary object with no own properties.
let obj = context.construct_object();
// 4. If Desc has a [[Value]] field, then
if let Some(value) = desc.value() {
// a. Perform ! CreateDataPropertyOrThrow(obj, "value", Desc.[[Value]]).
obj.create_data_property_or_throw("value", value, context)
.expect("CreateDataPropertyOrThrow cannot fail here");
}
// 1. If Desc is undefined, return undefined.
let desc = if let Some(desc) = desc {
desc
} else {
return JsValue::undefined();
};
// 5. If Desc has a [[Writable]] field, then
if let Some(writable) = desc.writable() {
// a. Perform ! CreateDataPropertyOrThrow(obj, "writable", Desc.[[Writable]]).
obj.create_data_property_or_throw("writable", writable, context)
.expect("CreateDataPropertyOrThrow cannot fail here");
}
// 2. Let obj be ! OrdinaryObjectCreate(%Object.prototype%).
// 3. Assert: obj is an extensible ordinary object with no own properties.
let obj = context.construct_object();
// 6. If Desc has a [[Get]] field, then
if let Some(get) = desc.get() {
// a. Perform ! CreateDataPropertyOrThrow(obj, "get", Desc.[[Get]]).
obj.create_data_property_or_throw("get", get, context)
.expect("CreateDataPropertyOrThrow cannot fail here");
}
// 4. If Desc has a [[Value]] field, then
if let Some(value) = desc.value() {
// a. Perform ! CreateDataPropertyOrThrow(obj, "value", Desc.[[Value]]).
obj.create_data_property_or_throw("value", value, context)
.expect("CreateDataPropertyOrThrow cannot fail here");
}
// 7. If Desc has a [[Set]] field, then
if let Some(set) = desc.set() {
// a. Perform ! CreateDataPropertyOrThrow(obj, "set", Desc.[[Set]]).
obj.create_data_property_or_throw("set", set, context)
.expect("CreateDataPropertyOrThrow cannot fail here");
}
// 5. If Desc has a [[Writable]] field, then
if let Some(writable) = desc.writable() {
// a. Perform ! CreateDataPropertyOrThrow(obj, "writable", Desc.[[Writable]]).
obj.create_data_property_or_throw("writable", writable, context)
.expect("CreateDataPropertyOrThrow cannot fail here");
}
// 8. If Desc has an [[Enumerable]] field, then
if let Some(enumerable) = desc.enumerable() {
// a. Perform ! CreateDataPropertyOrThrow(obj, "enumerable", Desc.[[Enumerable]]).
obj.create_data_property_or_throw("enumerable", enumerable, context)
.expect("CreateDataPropertyOrThrow cannot fail here");
}
// 6. If Desc has a [[Get]] field, then
if let Some(get) = desc.get() {
// a. Perform ! CreateDataPropertyOrThrow(obj, "get", Desc.[[Get]]).
obj.create_data_property_or_throw("get", get, context)
.expect("CreateDataPropertyOrThrow cannot fail here");
}
// 9. If Desc has a [[Configurable]] field, then
if let Some(configurable) = desc.configurable() {
// a. Perform ! CreateDataPropertyOrThrow(obj, "configurable", Desc.[[Configurable]]).
obj.create_data_property_or_throw("configurable", configurable, context)
.expect("CreateDataPropertyOrThrow cannot fail here");
}
// 7. If Desc has a [[Set]] field, then
if let Some(set) = desc.set() {
// a. Perform ! CreateDataPropertyOrThrow(obj, "set", Desc.[[Set]]).
obj.create_data_property_or_throw("set", set, context)
.expect("CreateDataPropertyOrThrow cannot fail here");
}
// 10. Return obj.
obj.into()
}
// 8. If Desc has an [[Enumerable]] field, then
if let Some(enumerable) = desc.enumerable() {
// a. Perform ! CreateDataPropertyOrThrow(obj, "enumerable", Desc.[[Enumerable]]).
obj.create_data_property_or_throw("enumerable", enumerable, context)
.expect("CreateDataPropertyOrThrow cannot fail here");
}
// 9. If Desc has a [[Configurable]] field, then
if let Some(configurable) = desc.configurable() {
// a. Perform ! CreateDataPropertyOrThrow(obj, "configurable", Desc.[[Configurable]]).
obj.create_data_property_or_throw("configurable", configurable, context)
.expect("CreateDataPropertyOrThrow cannot fail here");
}
// 10. Return obj.
obj.into()
}
/// Uses the `SameValue` algorithm to check equality of objects

74
boa_engine/src/builtins/promise/mod.rs

@ -65,6 +65,7 @@ pub(crate) enum PromiseState {
Rejected(JsValue),
}
/// The internal representation of a `Promise` object.
#[derive(Debug, Clone, Trace, Finalize)]
pub struct Promise {
promise_state: PromiseState,
@ -73,24 +74,52 @@ pub struct Promise {
promise_is_handled: bool,
}
/// The internal `PromiseReaction` data type.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-promisereaction-records
#[derive(Debug, Clone, Trace, Finalize)]
pub struct ReactionRecord {
pub(crate) struct ReactionRecord {
/// The `[[Capability]]` field.
promise_capability: Option<PromiseCapability>,
/// The `[[Type]]` field.
#[unsafe_ignore_trace]
reaction_type: ReactionType,
/// The `[[Handler]]` field.
handler: Option<JobCallback>,
}
/// The `[[Type]]` field values of a `PromiseReaction` record.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-promisereaction-records
#[derive(Debug, Clone, Copy)]
enum ReactionType {
Fulfill,
Reject,
}
/// The internal `PromiseCapability` data type.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-promisecapability-records
#[derive(Debug, Clone, Trace, Finalize)]
pub struct PromiseCapability {
/// The `[[Promise]]` field.
promise: JsObject,
/// The `[[Resolve]]` field.
resolve: JsFunction,
/// The `[[Reject]]` field.
reject: JsFunction,
}
@ -202,17 +231,17 @@ impl PromiseCapability {
}
/// Returns the promise object.
pub(crate) fn promise(&self) -> &JsObject {
pub(crate) const fn promise(&self) -> &JsObject {
&self.promise
}
/// Returns the resolve function.
pub(crate) fn resolve(&self) -> &JsFunction {
pub(crate) const fn resolve(&self) -> &JsFunction {
&self.resolve
}
/// Returns the reject function.
pub(crate) fn reject(&self) -> &JsFunction {
pub(crate) const fn reject(&self) -> &JsFunction {
&self.reject
}
}
@ -276,7 +305,7 @@ impl Promise {
const LENGTH: usize = 1;
/// Gets the current state of the promise.
pub(crate) fn state(&self) -> &PromiseState {
pub(crate) const fn state(&self) -> &PromiseState {
&self.promise_state
}
@ -1277,7 +1306,7 @@ impl Promise {
.borrow_mut()
.as_promise_mut()
.expect("Expected promise to be a Promise")
.fulfill_promise(resolution, context)?;
.fulfill_promise(resolution, context);
// b. Return undefined.
return Ok(JsValue::Undefined);
@ -1310,7 +1339,7 @@ impl Promise {
.borrow_mut()
.as_promise_mut()
.expect("Expected promise to be a Promise")
.fulfill_promise(resolution, context)?;
.fulfill_promise(resolution, context);
// b. Return undefined.
return Ok(JsValue::Undefined);
@ -1406,7 +1435,11 @@ impl Promise {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-fulfillpromise
pub fn fulfill_promise(&mut self, value: &JsValue, context: &mut Context) -> JsResult<()> {
///
/// # Panics
///
/// Panics if `Promise` is not pending.
fn fulfill_promise(&mut self, value: &JsValue, context: &mut Context) {
// 1. Assert: The value of promise.[[PromiseState]] is pending.
assert!(
matches!(self.promise_state, PromiseState::Pending),
@ -1431,7 +1464,6 @@ impl Promise {
self.promise_state = PromiseState::Fulfilled(value.clone());
// 8. Return unused.
Ok(())
}
/// `RejectPromise ( promise, reason )`
@ -1443,6 +1475,10 @@ impl Promise {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-rejectpromise
///
/// # Panics
///
/// Panics if `Promise` is not pending.
pub fn reject_promise(&mut self, reason: &JsValue, context: &mut Context) {
// 1. Assert: The value of promise.[[PromiseState]] is pending.
assert!(
@ -1488,7 +1524,7 @@ impl Promise {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-triggerpromisereactions
pub fn trigger_promise_reactions(
fn trigger_promise_reactions(
reactions: &[ReactionRecord],
argument: &JsValue,
context: &mut Context,
@ -1745,7 +1781,7 @@ impl Promise {
let c = promise_obj.species_constructor(StandardConstructors::promise, context)?;
// 4. Assert: IsConstructor(C) is true.
assert!(c.is_constructor());
debug_assert!(c.is_constructor());
let on_finally = args.get_or_undefined(0);
@ -2065,13 +2101,13 @@ impl Promise {
let promise_resolve = promise_constructor.get("resolve", context)?;
// 2. If IsCallable(promiseResolve) is false, throw a TypeError exception.
if let Some(promise_resolve) = promise_resolve.as_callable() {
// 3. Return promiseResolve.
Ok(promise_resolve.clone())
} else {
Err(JsNativeError::typ()
.with_message("retrieving a non-callable promise resolver")
.into())
}
promise_resolve.as_callable().map_or_else(
|| {
Err(JsNativeError::typ()
.with_message("retrieving a non-callable promise resolver")
.into())
},
|promise_resolve| Ok(promise_resolve.clone()),
)
}
}

117
boa_engine/src/builtins/regexp/mod.rs

@ -37,7 +37,7 @@ use tap::{Conv, Pipe};
#[cfg(test)]
mod tests;
/// The internal representation on a `RegExp` object.
/// The internal representation of a `RegExp` object.
#[derive(Debug, Clone)]
pub struct RegExp {
/// Regex matcher.
@ -615,42 +615,42 @@ impl RegExp {
) -> JsResult<JsValue> {
// 1. Let R be the this value.
// 2. If Type(R) is not Object, throw a TypeError exception.
if let Some(object) = this.as_object() {
let object = object.borrow();
let object = if let Some(object) = this.as_object() {
object
} else {
return Err(JsNativeError::typ()
.with_message("RegExp.prototype.source method called on incompatible value")
.into());
};
match object.as_regexp() {
// 3. If R does not have an [[OriginalSource]] internal slot, then
None => {
// a. If SameValue(R, %RegExp.prototype%) is true, return "(?:)".
// b. Otherwise, throw a TypeError exception.
if JsValue::same_value(
this,
&JsValue::new(context.intrinsics().constructors().regexp().prototype()),
) {
Ok(JsValue::new("(?:)"))
} else {
Err(JsNativeError::typ()
.with_message(
"RegExp.prototype.source method called on incompatible value",
)
.into())
}
}
// 4. Assert: R has an [[OriginalFlags]] internal slot.
Some(re) => {
// 5. Let src be R.[[OriginalSource]].
// 6. Let flags be R.[[OriginalFlags]].
// 7. Return EscapeRegExpPattern(src, flags).
Ok(Self::escape_pattern(
&re.original_source,
&re.original_flags,
))
let object = object.borrow();
match object.as_regexp() {
// 3. If R does not have an [[OriginalSource]] internal slot, then
None => {
// a. If SameValue(R, %RegExp.prototype%) is true, return "(?:)".
// b. Otherwise, throw a TypeError exception.
if JsValue::same_value(
this,
&JsValue::new(context.intrinsics().constructors().regexp().prototype()),
) {
Ok(JsValue::new("(?:)"))
} else {
Err(JsNativeError::typ()
.with_message("RegExp.prototype.source method called on incompatible value")
.into())
}
}
} else {
Err(JsNativeError::typ()
.with_message("RegExp.prototype.source method called on incompatible value")
.into())
// 4. Assert: R has an [[OriginalFlags]] internal slot.
Some(re) => {
// 5. Let src be R.[[OriginalSource]].
// 6. Let flags be R.[[OriginalFlags]].
// 7. Return EscapeRegExpPattern(src, flags).
Ok(Self::escape_pattern(
&re.original_source,
&re.original_flags,
))
}
}
}
@ -752,11 +752,8 @@ impl RegExp {
let arg_str = args.get_or_undefined(0).to_string(context)?;
// 4. Return ? RegExpBuiltinExec(R, S).
if let Some(v) = Self::abstract_builtin_exec(obj, &arg_str, context)? {
Ok(v.into())
} else {
Ok(JsValue::null())
}
(Self::abstract_builtin_exec(obj, &arg_str, context)?)
.map_or_else(|| Ok(JsValue::null()), |v| Ok(v.into()))
}
/// `22.2.5.2.1 RegExpExec ( R, S )`
@ -1010,14 +1007,13 @@ impl RegExp {
// a. Let captureI be ith element of r's captures List.
let capture = match_value.group(i as usize);
let captured_value = match capture {
// b. If captureI is undefined, let capturedValue be undefined.
None => JsValue::undefined(),
// c. Else if fullUnicode is true, then
// d. Else,
// TODO: Full UTF-16 regex support
Some(range) => js_string!(&lossy_input[range]).into(),
};
// b. If captureI is undefined, let capturedValue be undefined.
// c. Else if fullUnicode is true, then
// d. Else,
// TODO: Full UTF-16 regex support
let captured_value = capture.map_or_else(JsValue::undefined, |range| {
js_string!(&lossy_input[range]).into()
});
// e. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(i)), capturedValue).
a.create_data_property_or_throw(i, captured_value, context)
@ -1060,11 +1056,8 @@ impl RegExp {
#[allow(clippy::if_not_else)]
if !global {
// a. Return ? RegExpExec(rx, S).
if let Some(v) = Self::abstract_exec(rx, arg_str, context)? {
Ok(v.into())
} else {
Ok(JsValue::null())
}
(Self::abstract_exec(rx, arg_str, context)?)
.map_or_else(|| Ok(JsValue::null()), |v| Ok(v.into()))
// 6. Else,
} else {
// a. Assert: global is true.
@ -1365,8 +1358,7 @@ impl RegExp {
// k. If functionalReplace is true, then
// l. Else,
let replacement: JsString;
if functional_replace {
let replacement = if functional_replace {
// i. Let replacerArgs be « matched ».
let mut replacer_args = vec![JsValue::new(matched)];
@ -1388,7 +1380,7 @@ impl RegExp {
context.call(&replace_value, &JsValue::undefined(), &replacer_args)?;
// vi. Let replacement be ? ToString(replValue).
replacement = repl_value.to_string(context)?;
repl_value.to_string(context)?
} else {
// i. If namedCaptures is not undefined, then
if !named_captures.is_undefined() {
@ -1397,7 +1389,7 @@ impl RegExp {
}
// ii. Let replacement be ? GetSubstitution(matched, S, position, captures, namedCaptures, replaceValue).
replacement = string::get_substitution(
string::get_substitution(
&matched,
&arg_str,
position,
@ -1405,8 +1397,8 @@ impl RegExp {
&named_captures,
&replace_value.to_string(context)?,
context,
)?;
}
)?
};
// m. If position ≥ nextSourcePosition, then
if position >= next_source_position {
@ -1481,11 +1473,10 @@ impl RegExp {
// 9. If result is null, return -1𝔽.
// 10. Return ? Get(result, "index").
if let Some(result) = result {
result.get("index", context)
} else {
Ok(JsValue::new(-1))
}
result.map_or_else(
|| Ok(JsValue::new(-1)),
|result| result.get("index", context),
)
}
/// `RegExp.prototype [ @@split ] ( string, limit )`

13
boa_engine/src/builtins/regexp/regexp_string_iterator.rs

@ -22,7 +22,12 @@ use boa_gc::{Finalize, Trace};
use boa_profiler::Profiler;
use regexp::{advance_string_index, RegExp};
// TODO: See todos in create_regexp_string_iterator and next.
/// The `RegExp String Iterator` object.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-regexp-string-iterator-objects
#[derive(Debug, Clone, Finalize, Trace)]
pub struct RegExpStringIterator {
matcher: JsObject,
@ -81,6 +86,12 @@ impl RegExpStringIterator {
regexp_string_iterator.into()
}
/// `%RegExpStringIteratorPrototype%.next ( )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%regexpstringiteratorprototype%.next
pub fn next(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let mut iterator = this.as_object().map(JsObject::borrow_mut);
let iterator = iterator

12
boa_engine/src/builtins/set/mod.rs

@ -33,7 +33,7 @@ pub mod set_iterator;
mod tests;
#[derive(Debug, Clone)]
pub(crate) struct Set(OrderedSet<JsValue>);
pub(crate) struct Set;
impl BuiltIn for Set {
const NAME: &'static str = "Set";
@ -363,7 +363,7 @@ impl Set {
let arguments = this
.as_object()
.and_then(|obj| {
obj.borrow().as_set_ref().map(|set| {
obj.borrow().as_set().map(|set| {
set.get_index(index)
.map(|value| [value.clone(), value.clone(), this.clone()])
})
@ -394,11 +394,7 @@ impl Set {
let value = args.get_or_undefined(0);
this.as_object()
.and_then(|obj| {
obj.borrow()
.as_set_ref()
.map(|set| set.contains(value).into())
})
.and_then(|obj| obj.borrow().as_set().map(|set| set.contains(value).into()))
.ok_or_else(|| {
JsNativeError::typ()
.with_message("'this' is not a Set")
@ -448,7 +444,7 @@ impl Set {
/// Helper function to get the size of the `Set` object.
pub(crate) fn get_size(set: &JsValue) -> JsResult<usize> {
set.as_object()
.and_then(|obj| obj.borrow().as_set_ref().map(OrderedSet::size))
.and_then(|obj| obj.borrow().as_set().map(OrderedSet::size))
.ok_or_else(|| {
JsNativeError::typ()
.with_message("'this' is not a Set")

10
boa_engine/src/builtins/set/ordered_set.rs

@ -1,3 +1,5 @@
//! Implements a set type that preserves insertion order.
use boa_gc::{custom_trace, Finalize, Trace};
use indexmap::{
set::{IntoIter, Iter},
@ -43,12 +45,16 @@ impl<V> OrderedSet<V>
where
V: Hash + Eq,
{
/// Creates a new empty `OrderedSet`.
#[must_use]
pub fn new() -> Self {
Self {
inner: IndexSet::new(),
}
}
/// Creates a new empty `OrderedSet` with the specified capacity.
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
inner: IndexSet::with_capacity(capacity),
@ -58,6 +64,7 @@ where
/// Return the number of key-value pairs in the map.
///
/// Computes in **O(1)** time.
#[must_use]
pub fn size(&self) -> usize {
self.inner.len()
}
@ -65,6 +72,7 @@ where
/// Returns true if the map contains no elements.
///
/// Computes in **O(1)** time.
#[must_use]
pub fn is_empty(&self) -> bool {
self.inner.len() == 0
}
@ -108,11 +116,13 @@ where
/// Get a key-value pair by index
/// Valid indices are 0 <= index < self.len()
/// Computes in O(1) time.
#[must_use]
pub fn get_index(&self, index: usize) -> Option<&V> {
self.inner.get_index(index)
}
/// Return an iterator over the values of the set, in their order
#[must_use]
pub fn iter(&self) -> Iter<'_, V> {
self.inner.iter()
}

11
boa_engine/src/builtins/set/set_iterator.rs

@ -1,3 +1,10 @@
//! This module implements the `SetIterator` object.
//!
//! More information:
//! - [ECMAScript reference][spec]
//!
//! [spec]: https://tc39.es/ecma262/#sec-set-iterator-objects
use crate::{
builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Array, JsValue},
error::JsNativeError,
@ -27,7 +34,7 @@ impl SetIterator {
pub(crate) const NAME: &'static str = "SetIterator";
/// Constructs a new `SetIterator`, that will iterate over `set`, starting at index 0
fn new(set: JsValue, kind: PropertyNameKind) -> Self {
const fn new(set: JsValue, kind: PropertyNameKind) -> Self {
Self {
iterated_set: set,
next_index: 0,
@ -90,7 +97,7 @@ impl SetIterator {
let entries = m.as_object().map(JsObject::borrow);
let entries = entries
.as_ref()
.and_then(|obj| obj.as_set_ref())
.and_then(|obj| obj.as_set())
.ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Set"))?;
let num_entries = entries.size();

4
boa_engine/src/builtins/string/mod.rs

@ -41,7 +41,7 @@ pub(crate) enum Placement {
/// Helper function to check if a `char` is trimmable.
#[inline]
pub(crate) fn is_trimmable_whitespace(c: char) -> bool {
pub(crate) const fn is_trimmable_whitespace(c: char) -> bool {
// The rust implementation of `trim` does not regard the same characters whitespace as ecma standard does
//
// Rust uses \p{White_Space} by default, which also includes:
@ -258,7 +258,7 @@ impl String {
}
// c. If ℝ(nextCP) < 0 or ℝ(nextCP) > 0x10FFFF, throw a RangeError exception.
if nextcp < 0.0 || nextcp > f64::from(0x10FFFF) {
if nextcp < 0.0 || nextcp > f64::from(0x0010_FFFF) {
return Err(JsNativeError::range()
.with_message(format!("invalid code point: {nextcp}"))
.into());

27
boa_engine/src/builtins/string/string_iterator.rs

@ -1,3 +1,10 @@
//! This module implements the `StringIterator` object.
//!
//! More information:
//! - [ECMAScript reference][spec]
//!
//! [spec]: https://tc39.es/ecma262/#sec-string-iterator-objects
use crate::{
builtins::{function::make_builtin_fn, iterable::create_iter_result_object},
error::JsNativeError,
@ -9,6 +16,12 @@ use crate::{
use boa_gc::{Finalize, Trace};
use boa_profiler::Profiler;
/// The `StringIterator` object represents an iteration over a string. It implements the iterator protocol.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-string-iterator-objects
#[derive(Debug, Clone, Finalize, Trace)]
pub struct StringIterator {
string: JsValue,
@ -16,13 +29,7 @@ pub struct StringIterator {
}
impl StringIterator {
fn new(string: JsValue) -> Self {
Self {
string,
next_index: 0,
}
}
/// Create a new `StringIterator`.
pub fn create_string_iterator(string: JsValue, context: &mut Context) -> JsResult<JsValue> {
let string_iterator = JsObject::from_proto_and_data(
context
@ -30,11 +37,15 @@ impl StringIterator {
.objects()
.iterator_prototypes()
.string_iterator(),
ObjectData::string_iterator(Self::new(string)),
ObjectData::string_iterator(Self {
string,
next_index: 0,
}),
);
Ok(string_iterator.into())
}
/// `StringIterator.prototype.next( )`
pub fn next(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let mut string_iterator = this.as_object().map(JsObject::borrow_mut);
let string_iterator = string_iterator

15
boa_engine/src/builtins/symbol/mod.rs

@ -70,6 +70,7 @@ impl GlobalSymbolRegistry {
}
}
/// The internal representation of a `Symbol` object.
#[derive(Debug, Clone, Copy)]
pub struct Symbol;
@ -253,12 +254,14 @@ impl Symbol {
_: &[JsValue],
_: &mut Context,
) -> JsResult<JsValue> {
let symbol = Self::this_symbol_value(this)?;
if let Some(ref description) = symbol.description() {
Ok(description.clone().into())
} else {
Ok(JsValue::undefined())
}
// 1. Let s be the this value.
// 2. Let sym be ? thisSymbolValue(s).
let sym = Self::this_symbol_value(this)?;
// 3. Return sym.[[Description]].
Ok(sym
.description()
.map_or(JsValue::undefined(), JsValue::from))
}
/// `Symbol.for( key )`

12
boa_engine/src/builtins/typed_array/integer_indexed_object.rs

@ -34,7 +34,7 @@ pub struct IntegerIndexed {
}
impl IntegerIndexed {
pub(crate) fn new(
pub(crate) const fn new(
viewed_array_buffer: Option<JsObject>,
typed_array_name: TypedArrayKind,
byte_offset: u64,
@ -101,7 +101,7 @@ impl IntegerIndexed {
}
/// Get the integer indexed object's byte offset.
pub(crate) fn byte_offset(&self) -> u64 {
pub(crate) const fn byte_offset(&self) -> u64 {
self.byte_offset
}
@ -111,12 +111,12 @@ impl IntegerIndexed {
}
/// Get the integer indexed object's typed array name.
pub(crate) fn typed_array_name(&self) -> TypedArrayKind {
pub(crate) const fn typed_array_name(&self) -> TypedArrayKind {
self.typed_array_name
}
/// Get a reference to the integer indexed object's viewed array buffer.
pub fn viewed_array_buffer(&self) -> Option<&JsObject> {
pub const fn viewed_array_buffer(&self) -> Option<&JsObject> {
self.viewed_array_buffer.as_ref()
}
@ -126,7 +126,7 @@ impl IntegerIndexed {
}
/// Get the integer indexed object's byte length.
pub fn byte_length(&self) -> u64 {
pub const fn byte_length(&self) -> u64 {
self.byte_length
}
@ -136,7 +136,7 @@ impl IntegerIndexed {
}
/// Get the integer indexed object's array length.
pub fn array_length(&self) -> u64 {
pub const fn array_length(&self) -> u64 {
self.array_length
}

11
boa_engine/src/builtins/typed_array/mod.rs

@ -2757,9 +2757,6 @@ impl TypedArray {
{
return Ok(Ordering::Greater);
}
// 10. Return +0𝔽.
Ok(Ordering::Equal)
} else {
let x = x
.as_number()
@ -2802,10 +2799,10 @@ impl TypedArray {
if x.is_zero() && y.is_zero() && x.is_sign_positive() && y.is_sign_negative() {
return Ok(Ordering::Greater);
}
// 10. Return +0𝔽.
Ok(Ordering::Equal)
}
// 10. Return +0𝔽.
Ok(Ordering::Equal)
};
// 8. Sort items using an implementation-defined sequence of calls to SortCompare.
@ -3542,7 +3539,7 @@ impl TypedArrayKind {
}
}
pub(crate) fn is_big_int_element_type(self) -> bool {
pub(crate) const fn is_big_int_element_type(self) -> bool {
matches!(self, Self::BigUint64 | Self::BigInt64)
}
}

2
boa_engine/src/builtins/weak/mod.rs

@ -1,3 +1,5 @@
//! This module implements the global `Weak*` objects.
mod weak_ref;
pub(crate) use weak_ref::WeakRef;

12
boa_engine/src/bytecompiler/function.rs

@ -26,7 +26,7 @@ pub(crate) struct FunctionCompiler {
impl FunctionCompiler {
/// Create a new `FunctionCompiler`.
#[inline]
pub(crate) fn new() -> Self {
pub(crate) const fn new() -> Self {
Self {
name: Sym::EMPTY_STRING,
generator: false,
@ -52,34 +52,34 @@ impl FunctionCompiler {
/// Indicate if the function is an arrow function.
#[inline]
pub(crate) fn arrow(mut self, arrow: bool) -> Self {
pub(crate) const fn arrow(mut self, arrow: bool) -> Self {
self.arrow = arrow;
self
}
/// Indicate if the function is a generator function.
#[inline]
pub(crate) fn generator(mut self, generator: bool) -> Self {
pub(crate) const fn generator(mut self, generator: bool) -> Self {
self.generator = generator;
self
}
/// Indicate if the function is an async function.
#[inline]
pub(crate) fn r#async(mut self, r#async: bool) -> Self {
pub(crate) const fn r#async(mut self, r#async: bool) -> Self {
self.r#async = r#async;
self
}
/// Indicate if the function is in a strict context.
#[inline]
pub(crate) fn strict(mut self, strict: bool) -> Self {
pub(crate) const fn strict(mut self, strict: bool) -> Self {
self.strict = strict;
self
}
/// Indicate if the function has a binding identifier.
#[inline]
pub(crate) fn has_binding_identifier(mut self, has_binding_identifier: bool) -> Self {
pub(crate) const fn has_binding_identifier(mut self, has_binding_identifier: bool) -> Self {
self.has_binding_identifier = has_binding_identifier;
self
}

68
boa_engine/src/bytecompiler/mod.rs

@ -1,3 +1,5 @@
//! This module contains the bytecode compiler.
mod function;
use crate::{
@ -57,6 +59,7 @@ enum FunctionKind {
/// Describes the complete specification of a function node.
#[derive(Debug, Clone, Copy, PartialEq)]
#[allow(single_use_lifetimes)]
struct FunctionSpec<'a> {
kind: FunctionKind,
name: Option<Identifier>,
@ -65,14 +68,14 @@ struct FunctionSpec<'a> {
has_binding_identifier: bool,
}
impl<'a> FunctionSpec<'a> {
impl FunctionSpec<'_> {
#[inline]
fn is_arrow(&self) -> bool {
const fn is_arrow(&self) -> bool {
matches!(self.kind, FunctionKind::Arrow | FunctionKind::AsyncArrow)
}
#[inline]
fn is_async(&self) -> bool {
const fn is_async(&self) -> bool {
matches!(
self.kind,
FunctionKind::Async | FunctionKind::AsyncGenerator | FunctionKind::AsyncArrow
@ -80,7 +83,7 @@ impl<'a> FunctionSpec<'a> {
}
#[inline]
fn is_generator(&self) -> bool {
const fn is_generator(&self) -> bool {
matches!(
self.kind,
FunctionKind::Generator | FunctionKind::AsyncGenerator
@ -208,7 +211,7 @@ enum Access<'a> {
}
impl Access<'_> {
fn from_assign_target(target: &AssignTarget) -> Result<Access<'_>, &Pattern> {
const fn from_assign_target(target: &AssignTarget) -> Result<Access<'_>, &Pattern> {
match target {
AssignTarget::Identifier(ident) => Ok(Access::Variable { name: *ident }),
AssignTarget::Access(access) => Ok(Access::Property { access }),
@ -216,7 +219,7 @@ impl Access<'_> {
}
}
fn from_expression(expr: &Expression) -> Option<Access<'_>> {
const fn from_expression(expr: &Expression) -> Option<Access<'_>> {
match expr {
Expression::Identifier(name) => Some(Access::Variable { name: *name }),
Expression::PropertyAccess(access) => Some(Access::Property { access }),
@ -226,6 +229,7 @@ impl Access<'_> {
}
}
/// The [`ByteCompiler`] is used to compile ECMAScript AST from [`boa_ast`] to bytecode.
#[derive(Debug)]
pub struct ByteCompiler<'b> {
code_block: CodeBlock,
@ -242,6 +246,7 @@ impl<'b> ByteCompiler<'b> {
/// Represents a placeholder address that will be patched later.
const DUMMY_ADDRESS: u32 = u32::MAX;
/// Creates a new [`ByteCompiler`].
#[inline]
pub fn new(name: Sym, strict: bool, json_parse: bool, context: &'b mut Context) -> Self {
Self {
@ -865,6 +870,7 @@ impl<'b> ByteCompiler<'b> {
Ok(())
}
/// Compile a [`StatementList`].
#[inline]
pub fn compile_statement_list(
&mut self,
@ -910,6 +916,7 @@ impl<'b> ByteCompiler<'b> {
Ok(())
}
/// Compile an [`Expression`].
#[inline]
pub fn compile_expr(&mut self, expr: &Expression, use_expr: bool) -> JsResult<()> {
match expr {
@ -1747,7 +1754,8 @@ impl<'b> ByteCompiler<'b> {
Ok(())
}
pub fn compile_var_decl(&mut self, decl: &VarDeclaration) -> JsResult<()> {
/// Compile a [`VarDeclaration`].
fn compile_var_decl(&mut self, decl: &VarDeclaration) -> JsResult<()> {
for variable in decl.0.as_ref() {
match variable.binding() {
Binding::Identifier(ident) => {
@ -1773,7 +1781,8 @@ impl<'b> ByteCompiler<'b> {
Ok(())
}
pub fn compile_lexical_decl(&mut self, decl: &LexicalDeclaration) -> JsResult<()> {
/// Compile a [`LexicalDeclaration`].
fn compile_lexical_decl(&mut self, decl: &LexicalDeclaration) -> JsResult<()> {
match decl {
LexicalDeclaration::Let(decls) => {
for variable in decls.as_ref() {
@ -1826,8 +1835,9 @@ impl<'b> ByteCompiler<'b> {
Ok(())
}
/// Compile a [`StatementListItem`].
#[inline]
pub fn compile_stmt_list_item(
fn compile_stmt_list_item(
&mut self,
item: &StatementListItem,
use_expr: bool,
@ -1841,6 +1851,7 @@ impl<'b> ByteCompiler<'b> {
}
}
/// Compile a [`Declaration`].
#[inline]
pub fn compile_decl(&mut self, decl: &Declaration) -> JsResult<()> {
match decl {
@ -1861,8 +1872,9 @@ impl<'b> ByteCompiler<'b> {
}
}
/// Compile a [`ForLoop`].
#[inline]
pub fn compile_for_loop(
fn compile_for_loop(
&mut self,
for_loop: &ForLoop,
label: Option<Sym>,
@ -1921,8 +1933,9 @@ impl<'b> ByteCompiler<'b> {
Ok(())
}
/// Compile a [`ForInLoop`].
#[inline]
pub fn compile_for_in_loop(
fn compile_for_in_loop(
&mut self,
for_in_loop: &ForInLoop,
label: Option<Sym>,
@ -2036,8 +2049,9 @@ impl<'b> ByteCompiler<'b> {
Ok(())
}
/// Compile a [`ForOfLoop`].
#[inline]
pub fn compile_for_of_loop(
fn compile_for_of_loop(
&mut self,
for_of_loop: &ForOfLoop,
label: Option<Sym>,
@ -2160,8 +2174,9 @@ impl<'b> ByteCompiler<'b> {
Ok(())
}
/// Compile a [`WhileLoop`].
#[inline]
pub fn compile_while_loop(
fn compile_while_loop(
&mut self,
while_loop: &WhileLoop,
label: Option<Sym>,
@ -2183,8 +2198,9 @@ impl<'b> ByteCompiler<'b> {
Ok(())
}
/// Compile a [`DoWhileLoop`].
#[inline]
pub fn compile_do_while_loop(
fn compile_do_while_loop(
&mut self,
do_while_loop: &DoWhileLoop,
label: Option<Sym>,
@ -2212,6 +2228,7 @@ impl<'b> ByteCompiler<'b> {
Ok(())
}
/// Compile a [`Block`].
#[inline]
pub fn compile_block(
&mut self,
@ -2242,6 +2259,7 @@ impl<'b> ByteCompiler<'b> {
Ok(())
}
/// Compile a [`Statement`].
#[inline]
pub fn compile_stmt(
&mut self,
@ -2346,11 +2364,9 @@ impl<'b> ByteCompiler<'b> {
.filter(|info| info.kind == JumpControlInfoKind::Try)
{
let start_address = info.start_address;
let in_finally = if let Some(finally_start) = info.finally_start {
next > finally_start.index
} else {
false
};
let in_finally = info
.finally_start
.map_or(false, |finally_start| next > finally_start.index);
let in_catch_no_finally = !info.has_finally && info.in_catch;
if in_finally {
@ -2358,11 +2374,10 @@ impl<'b> ByteCompiler<'b> {
}
if in_finally || in_catch_no_finally {
self.emit_opcode(Opcode::CatchEnd2);
self.emit(Opcode::FinallySetJump, &[start_address]);
} else {
self.emit_opcode(Opcode::TryEnd);
self.emit(Opcode::FinallySetJump, &[start_address]);
}
self.emit(Opcode::FinallySetJump, &[start_address]);
let label = self.jump();
self.jump_info
.last_mut()
@ -2428,11 +2443,9 @@ impl<'b> ByteCompiler<'b> {
.last()
.filter(|info| info.kind == JumpControlInfoKind::Try)
{
let in_finally = if let Some(finally_start) = info.finally_start {
next >= finally_start.index
} else {
false
};
let in_finally = info
.finally_start
.map_or(false, |finally_start| next >= finally_start.index);
let in_catch_no_finally = !info.has_finally && info.in_catch;
if in_finally {
@ -2785,7 +2798,10 @@ impl<'b> ByteCompiler<'b> {
Ok(())
}
/// Finish compiling code with the [`ByteCompiler`] and return the generated [`CodeBlock`].
#[inline]
#[must_use]
#[allow(clippy::missing_const_for_fn)]
pub fn finish(self) -> CodeBlock {
self.code_block
}

2
boa_engine/src/context/icu.rs

@ -67,7 +67,7 @@ impl Icu {
}
/// Get the [`LocaleCanonicalizer`] tool.
pub(crate) fn locale_canonicalizer(&self) -> &LocaleCanonicalizer {
pub(crate) const fn locale_canonicalizer(&self) -> &LocaleCanonicalizer {
&self.locale_canonicalizer
}

353
boa_engine/src/context/intrinsics.rs

@ -1,3 +1,5 @@
//! This module implements the data structures that contain intrinsic objects and constructors.
use crate::{
builtins::{
array::Array, error::r#type::create_throw_type_error, iterable::IteratorPrototypes,
@ -7,6 +9,7 @@ use crate::{
Context,
};
/// The intrinsic objects and constructors.
#[derive(Debug, Default)]
pub struct Intrinsics {
/// Cached standard constructors
@ -18,13 +21,13 @@ pub struct Intrinsics {
impl Intrinsics {
/// Return the cached intrinsic objects.
#[inline]
pub fn objects(&self) -> &IntrinsicObjects {
pub const fn objects(&self) -> &IntrinsicObjects {
&self.objects
}
/// Return the cached standard constructors.
#[inline]
pub fn constructors(&self) -> &StandardConstructors {
pub const fn constructors(&self) -> &StandardConstructors {
&self.constructors
}
}
@ -195,218 +198,476 @@ impl Default for StandardConstructors {
}
impl StandardConstructors {
/// Returns the `AsyncGeneratorFunction` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorfunction-constructor
#[inline]
pub fn async_generator_function(&self) -> &StandardConstructor {
pub const fn async_generator_function(&self) -> &StandardConstructor {
&self.async_generator_function
}
/// Returns the `AsyncGenerator` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-asyncgenerator-objects
#[inline]
pub fn async_generator(&self) -> &StandardConstructor {
pub const fn async_generator(&self) -> &StandardConstructor {
&self.async_generator
}
/// Returns the `Object` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-object-constructor
#[inline]
pub fn object(&self) -> &StandardConstructor {
pub const fn object(&self) -> &StandardConstructor {
&self.object
}
/// Returns the `Proxy` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-proxy-constructor
#[inline]
pub fn proxy(&self) -> &StandardConstructor {
pub const fn proxy(&self) -> &StandardConstructor {
&self.proxy
}
/// Returns the `Date` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-date-constructor
#[inline]
pub fn date(&self) -> &StandardConstructor {
pub const fn date(&self) -> &StandardConstructor {
&self.date
}
/// Returns the `Function` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-function-constructor
#[inline]
pub fn function(&self) -> &StandardConstructor {
pub const fn function(&self) -> &StandardConstructor {
&self.function
}
/// Returns the `AsyncFunction` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-async-function-constructor
#[inline]
pub fn async_function(&self) -> &StandardConstructor {
pub const fn async_function(&self) -> &StandardConstructor {
&self.async_function
}
/// Returns the `Generator` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-generator-objects
#[inline]
pub fn generator(&self) -> &StandardConstructor {
pub const fn generator(&self) -> &StandardConstructor {
&self.generator
}
/// Returns the `GeneratorFunction` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-generatorfunction-constructor
#[inline]
pub fn generator_function(&self) -> &StandardConstructor {
pub const fn generator_function(&self) -> &StandardConstructor {
&self.generator_function
}
/// Returns the `Array` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-array-constructor
#[inline]
pub fn array(&self) -> &StandardConstructor {
pub const fn array(&self) -> &StandardConstructor {
&self.array
}
/// Returns the `BigInt` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-bigint-constructor
#[inline]
pub fn bigint_object(&self) -> &StandardConstructor {
pub const fn bigint_object(&self) -> &StandardConstructor {
&self.bigint
}
/// Returns the `Number` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-number-constructor
#[inline]
pub fn number(&self) -> &StandardConstructor {
pub const fn number(&self) -> &StandardConstructor {
&self.number
}
/// Returns the `Boolean` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-boolean-constructor
#[inline]
pub fn boolean(&self) -> &StandardConstructor {
pub const fn boolean(&self) -> &StandardConstructor {
&self.boolean
}
/// Returns the `String` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-string-constructor
#[inline]
pub fn string(&self) -> &StandardConstructor {
pub const fn string(&self) -> &StandardConstructor {
&self.string
}
/// Returns the `RegExp` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-regexp-constructor
#[inline]
pub fn regexp(&self) -> &StandardConstructor {
pub const fn regexp(&self) -> &StandardConstructor {
&self.regexp
}
/// Returns the `Symbol` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-symbol-constructor
#[inline]
pub fn symbol(&self) -> &StandardConstructor {
pub const fn symbol(&self) -> &StandardConstructor {
&self.symbol
}
/// Returns the `Error` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-error-constructor
#[inline]
pub fn error(&self) -> &StandardConstructor {
pub const fn error(&self) -> &StandardConstructor {
&self.error
}
/// Returns the `ReferenceError` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-referenceerror
#[inline]
pub fn reference_error(&self) -> &StandardConstructor {
pub const fn reference_error(&self) -> &StandardConstructor {
&self.reference_error
}
/// Returns the `TypeError` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-typeerror
#[inline]
pub fn type_error(&self) -> &StandardConstructor {
pub const fn type_error(&self) -> &StandardConstructor {
&self.type_error
}
/// Returns the `RangeError` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-rangeerror
#[inline]
pub fn range_error(&self) -> &StandardConstructor {
pub const fn range_error(&self) -> &StandardConstructor {
&self.range_error
}
/// Returns the `SyntaxError` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-syntaxerror
#[inline]
pub fn syntax_error(&self) -> &StandardConstructor {
pub const fn syntax_error(&self) -> &StandardConstructor {
&self.syntax_error
}
/// Returns the `EvalError` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-evalerror
#[inline]
pub fn eval_error(&self) -> &StandardConstructor {
pub const fn eval_error(&self) -> &StandardConstructor {
&self.eval_error
}
/// Returns the `URIError` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-urierror
#[inline]
pub fn uri_error(&self) -> &StandardConstructor {
pub const fn uri_error(&self) -> &StandardConstructor {
&self.uri_error
}
/// Returns the `AggregateError` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-aggregate-error-constructor
#[inline]
pub fn aggregate_error(&self) -> &StandardConstructor {
pub const fn aggregate_error(&self) -> &StandardConstructor {
&self.aggregate_error
}
/// Returns the `Map` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-map-constructor
#[inline]
pub fn map(&self) -> &StandardConstructor {
pub const fn map(&self) -> &StandardConstructor {
&self.map
}
/// Returns the `Set` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-set-constructor
#[inline]
pub fn set(&self) -> &StandardConstructor {
pub const fn set(&self) -> &StandardConstructor {
&self.set
}
/// Returns the `TypedArray` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors
#[inline]
pub fn typed_array(&self) -> &StandardConstructor {
pub const fn typed_array(&self) -> &StandardConstructor {
&self.typed_array
}
/// Returns the `Int8Array` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors
#[inline]
pub fn typed_int8_array(&self) -> &StandardConstructor {
pub const fn typed_int8_array(&self) -> &StandardConstructor {
&self.typed_int8_array
}
/// Returns the `Uint8Array` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors
#[inline]
pub fn typed_uint8_array(&self) -> &StandardConstructor {
pub const fn typed_uint8_array(&self) -> &StandardConstructor {
&self.typed_uint8_array
}
/// Returns the `Uint8ClampedArray` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors
#[inline]
pub fn typed_uint8clamped_array(&self) -> &StandardConstructor {
pub const fn typed_uint8clamped_array(&self) -> &StandardConstructor {
&self.typed_uint8clamped_array
}
/// Returns the `Int16Array` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors
#[inline]
pub fn typed_int16_array(&self) -> &StandardConstructor {
pub const fn typed_int16_array(&self) -> &StandardConstructor {
&self.typed_int16_array
}
/// Returns the `Uint16Array` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors
#[inline]
pub fn typed_uint16_array(&self) -> &StandardConstructor {
pub const fn typed_uint16_array(&self) -> &StandardConstructor {
&self.typed_uint16_array
}
/// Returns the `Uint32Array` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors
#[inline]
pub fn typed_uint32_array(&self) -> &StandardConstructor {
pub const fn typed_uint32_array(&self) -> &StandardConstructor {
&self.typed_uint32_array
}
/// Returns the `Int32Array` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors
#[inline]
pub fn typed_int32_array(&self) -> &StandardConstructor {
pub const fn typed_int32_array(&self) -> &StandardConstructor {
&self.typed_int32_array
}
/// Returns the `BigInt64Array` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors
#[inline]
pub fn typed_bigint64_array(&self) -> &StandardConstructor {
pub const fn typed_bigint64_array(&self) -> &StandardConstructor {
&self.typed_bigint64_array
}
/// Returns the `BigUint64Array` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors
#[inline]
pub fn typed_biguint64_array(&self) -> &StandardConstructor {
pub const fn typed_biguint64_array(&self) -> &StandardConstructor {
&self.typed_biguint64_array
}
/// Returns the `Float32Array` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors
#[inline]
pub fn typed_float32_array(&self) -> &StandardConstructor {
pub const fn typed_float32_array(&self) -> &StandardConstructor {
&self.typed_float32_array
}
/// Returns the `Float64Array` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors
#[inline]
pub fn typed_float64_array(&self) -> &StandardConstructor {
pub const fn typed_float64_array(&self) -> &StandardConstructor {
&self.typed_float64_array
}
/// Returns the `ArrayBuffer` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-arraybuffer-constructor
#[inline]
pub fn array_buffer(&self) -> &StandardConstructor {
pub const fn array_buffer(&self) -> &StandardConstructor {
&self.array_buffer
}
/// Returns the `DataView` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-dataview-constructor
#[inline]
pub fn data_view(&self) -> &StandardConstructor {
pub const fn data_view(&self) -> &StandardConstructor {
&self.data_view
}
/// Returns the `Intl.DateTimeFormat` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma402/#sec-intl-datetimeformat-constructor
#[inline]
pub fn date_time_format(&self) -> &StandardConstructor {
pub const fn date_time_format(&self) -> &StandardConstructor {
&self.date_time_format
}
/// Returns the `Promise` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-promise-constructor
#[inline]
pub fn promise(&self) -> &StandardConstructor {
pub const fn promise(&self) -> &StandardConstructor {
&self.promise
}
/// Returns the `WeakRef` constructor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-weak-ref-constructor
#[inline]
pub fn weak_ref(&self) -> &StandardConstructor {
pub const fn weak_ref(&self) -> &StandardConstructor {
&self.weak_ref
}
}
@ -448,7 +709,7 @@ impl IntrinsicObjects {
/// Get the cached iterator prototypes.
#[inline]
pub fn iterator_prototypes(&self) -> &IteratorPrototypes {
pub const fn iterator_prototypes(&self) -> &IteratorPrototypes {
&self.iterator_prototypes
}
}

24
boa_engine/src/context/mod.rs

@ -117,12 +117,14 @@ impl Default for Context {
impl Context {
/// Create a new [`ContextBuilder`] to specify the [`Interner`] and/or
/// the icu data provider.
#[must_use]
pub fn builder() -> ContextBuilder {
ContextBuilder::default()
}
/// Gets the string interner.
#[inline]
pub fn interner(&self) -> &Interner {
pub const fn interner(&self) -> &Interner {
&self.interner
}
@ -134,7 +136,7 @@ impl Context {
/// A helper function for getting an immutable reference to the `console` object.
#[cfg(feature = "console")]
pub(crate) fn console(&self) -> &Console {
pub(crate) const fn console(&self) -> &Console {
&self.console
}
@ -207,7 +209,7 @@ impl Context {
/// Return the global object.
#[inline]
pub fn global_object(&self) -> &JsObject {
pub const fn global_object(&self) -> &JsObject {
self.realm.global_object()
}
@ -364,11 +366,8 @@ impl Context {
/// <https://tc39.es/ecma262/#sec-hasproperty>
#[inline]
pub(crate) fn has_property(&mut self, obj: &JsValue, key: &PropertyKey) -> JsResult<bool> {
if let Some(obj) = obj.as_object() {
obj.__has_property__(key, self)
} else {
Ok(false)
}
obj.as_object()
.map_or(Ok(false), |obj| obj.__has_property__(key, self))
}
/// Register a global class of type `T`, where `T` implements `Class`.
@ -557,7 +556,7 @@ impl Context {
/// Return the intrinsic constructors and objects.
#[inline]
pub fn intrinsics(&self) -> &Intrinsics {
pub const fn intrinsics(&self) -> &Intrinsics {
&self.intrinsics
}
@ -569,7 +568,7 @@ impl Context {
#[cfg(feature = "intl")]
#[inline]
/// Get the ICU related utilities
pub(crate) fn icu(&self) -> &icu::Icu {
pub(crate) const fn icu(&self) -> &icu::Icu {
&self.icu
}
@ -623,6 +622,7 @@ impl ContextBuilder {
/// This is useful when you want to initialize an [`Interner`] with
/// a collection of words before parsing.
#[must_use]
#[allow(clippy::missing_const_for_fn)]
pub fn interner(mut self, interner: Interner) -> Self {
self.interner = Some(interner);
self
@ -642,19 +642,21 @@ impl ContextBuilder {
/// This function is only available if the `fuzz` feature is enabled.
#[cfg(feature = "fuzz")]
#[must_use]
pub fn instructions_remaining(mut self, instructions_remaining: usize) -> Self {
pub const 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.
#[must_use]
pub fn new() -> Self {
Self::default()
}
/// Builds a new [`Context`] with the provided parameters, and defaults
/// all missing parameters to their default values.
#[must_use]
pub fn build(self) -> Context {
let intrinsics = Intrinsics::default();
let mut context = Context {

28
boa_engine/src/environments/compile.rs

@ -57,7 +57,7 @@ impl CompileTimeEnvironment {
/// Check if the environment is a function environment.
#[inline]
pub(crate) fn is_function(&self) -> bool {
pub(crate) const fn is_function(&self) -> bool {
self.function_scope
}
@ -169,13 +169,14 @@ impl CompileTimeEnvironment {
.borrow()
.initialize_mutable_binding(name, function_scope);
}
if let Some(binding) = self.bindings.get(&name) {
BindingLocator::declarative(name, self.environment_index, binding.index)
} else {
outer
.borrow()
.initialize_mutable_binding(name, function_scope)
}
self.bindings.get(&name).map_or_else(
|| {
outer
.borrow()
.initialize_mutable_binding(name, function_scope)
},
|binding| BindingLocator::declarative(name, self.environment_index, binding.index),
)
} else if let Some(binding) = self.bindings.get(&name) {
BindingLocator::declarative(name, self.environment_index, binding.index)
} else {
@ -203,13 +204,10 @@ impl CompileTimeEnvironment {
}
Some(binding) if binding.strict => BindingLocator::mutate_immutable(name),
Some(_) => BindingLocator::silent(name),
None => {
if let Some(outer) = &self.outer {
outer.borrow().set_mutable_binding_recursive(name)
} else {
BindingLocator::global(name)
}
}
None => self.outer.as_ref().map_or_else(
|| BindingLocator::global(name),
|outer| outer.borrow().set_mutable_binding_recursive(name),
),
}
}
}

38
boa_engine/src/environments/runtime.rs

@ -1,12 +1,10 @@
use std::cell::Cell;
use crate::{
environments::CompileTimeEnvironment, error::JsNativeError, object::JsObject, Context, JsValue,
};
use boa_gc::{Finalize, Gc, GcCell, Trace};
use boa_ast::expression::Identifier;
use boa_gc::{Finalize, Gc, GcCell, Trace};
use rustc_hash::FxHashSet;
use std::cell::Cell;
/// A declarative environment holds binding values at runtime.
///
@ -46,7 +44,7 @@ pub(crate) enum EnvironmentSlots {
impl EnvironmentSlots {
/// Return the slots if they are part of a function environment.
pub(crate) fn as_function_slots(&self) -> Option<&GcCell<FunctionSlots>> {
pub(crate) const fn as_function_slots(&self) -> Option<&GcCell<FunctionSlots>> {
if let Self::Function(env) = &self {
Some(env)
} else {
@ -74,12 +72,12 @@ pub(crate) struct FunctionSlots {
impl FunctionSlots {
/// Returns the value of the `[[FunctionObject]]` internal slot.
pub(crate) fn function_object(&self) -> &JsObject {
pub(crate) const fn function_object(&self) -> &JsObject {
&self.function_object
}
/// Returns the value of the `[[NewTarget]]` internal slot.
pub(crate) fn new_target(&self) -> Option<&JsObject> {
pub(crate) const fn new_target(&self) -> Option<&JsObject> {
self.new_target.as_ref()
}
@ -183,7 +181,7 @@ enum ThisBindingStatus {
impl DeclarativeEnvironment {
/// Returns the internal slot data of the current environment.
pub(crate) fn slots(&self) -> Option<&EnvironmentSlots> {
pub(crate) const fn slots(&self) -> Option<&EnvironmentSlots> {
self.slots.as_ref()
}
@ -408,11 +406,7 @@ impl DeclarativeEnvironmentStack {
ThisBindingStatus::Uninitialized
};
let this = if let Some(this) = this {
this
} else {
JsValue::Null
};
let this = this.unwrap_or(JsValue::Null);
self.stack.push(Gc::new(DeclarativeEnvironment {
bindings: GcCell::new(vec![None; num_bindings]),
@ -786,7 +780,7 @@ pub(crate) struct BindingLocator {
impl BindingLocator {
/// Creates a new declarative binding locator that has knows indices.
#[inline]
pub(in crate::environments) fn declarative(
pub(in crate::environments) const fn declarative(
name: Identifier,
environment_index: usize,
binding_index: usize,
@ -803,7 +797,7 @@ impl BindingLocator {
/// Creates a binding locator that indicates that the binding is on the global object.
#[inline]
pub(in crate::environments) fn global(name: Identifier) -> Self {
pub(in crate::environments) const fn global(name: Identifier) -> Self {
Self {
name,
environment_index: 0,
@ -817,7 +811,7 @@ impl BindingLocator {
/// Creates a binding locator that indicates that it was attempted to mutate an immutable binding.
/// At runtime this should always produce a type error.
#[inline]
pub(in crate::environments) fn mutate_immutable(name: Identifier) -> Self {
pub(in crate::environments) const fn mutate_immutable(name: Identifier) -> Self {
Self {
name,
environment_index: 0,
@ -830,7 +824,7 @@ impl BindingLocator {
/// Creates a binding locator that indicates that any action is silently ignored.
#[inline]
pub(in crate::environments) fn silent(name: Identifier) -> Self {
pub(in crate::environments) const fn silent(name: Identifier) -> Self {
Self {
name,
environment_index: 0,
@ -843,31 +837,31 @@ impl BindingLocator {
/// Returns the name of the binding.
#[inline]
pub(crate) fn name(&self) -> Identifier {
pub(crate) const fn name(&self) -> Identifier {
self.name
}
/// Returns if the binding is located on the global object.
#[inline]
pub(crate) fn is_global(&self) -> bool {
pub(crate) const fn is_global(&self) -> bool {
self.global
}
/// Returns the environment index of the binding.
#[inline]
pub(crate) fn environment_index(&self) -> usize {
pub(crate) const fn environment_index(&self) -> usize {
self.environment_index
}
/// Returns the binding index of the binding.
#[inline]
pub(crate) fn binding_index(&self) -> usize {
pub(crate) const fn binding_index(&self) -> usize {
self.binding_index
}
/// Returns if the binding is a silent operation.
#[inline]
pub(crate) fn is_silent(&self) -> bool {
pub(crate) const fn is_silent(&self) -> bool {
self.silent
}

64
boa_engine/src/error.rs

@ -66,17 +66,35 @@ enum Repr {
/// The error type returned by the [`JsError::try_native`] method.
#[derive(Debug, Clone, Error)]
pub enum TryNativeError {
/// This error is returned when a property of the error object has an invalid type.
#[error("invalid type of property `{0}`")]
InvalidPropertyType(&'static str),
/// This error is returned when the message of the error object could not be decoded.
#[error("property `message` cannot contain unpaired surrogates")]
InvalidMessageEncoding,
/// This error is returned when a property of the error object is not accessible.
#[error("could not access property `{property}`")]
InaccessibleProperty {
/// The name of the property that could not be accessed.
property: &'static str,
/// The source error.
source: JsError,
},
/// This error is returned when any inner error of an aggregate error is not accessible.
#[error("could not get element `{index}` of property `errors`")]
InvalidErrorsIndex { index: u64, source: JsError },
InvalidErrorsIndex {
/// The index of the error that could not be accessed.
index: u64,
/// The source error.
source: JsError,
},
/// This error is returned when the error value not an error object.
#[error("opaque error of type `{:?}` is not an Error object", .0.get_type())]
NotAnErrorObject(JsValue),
}
@ -101,7 +119,8 @@ impl JsError {
///
/// assert!(error.as_native().is_some());
/// ```
pub fn from_native(err: JsNativeError) -> Self {
#[must_use]
pub const fn from_native(err: JsNativeError) -> Self {
Self {
inner: Repr::Native(err),
}
@ -117,7 +136,7 @@ impl JsError {
///
/// assert!(error.as_opaque().is_some());
/// ```
pub fn from_opaque(value: JsValue) -> Self {
pub const fn from_opaque(value: JsValue) -> Self {
Self {
inner: Repr::Opaque(value),
}
@ -246,7 +265,7 @@ impl JsError {
}
})?;
for i in 0..length {
error_list.push(JsError::from_opaque(
error_list.push(Self::from_opaque(
errors.get(i, context).map_err(|e| {
TryNativeError::InvalidErrorsIndex {
index: i,
@ -266,7 +285,7 @@ impl JsError {
Ok(JsNativeError {
kind,
message,
cause: cause.map(|v| Box::new(JsError::from_opaque(v))),
cause: cause.map(|v| Box::new(Self::from_opaque(v))),
})
}
}
@ -289,7 +308,7 @@ impl JsError {
///
/// assert!(error.as_opaque().is_some());
/// ```
pub fn as_opaque(&self) -> Option<&JsValue> {
pub const fn as_opaque(&self) -> Option<&JsValue> {
match self.inner {
Repr::Native(_) => None,
Repr::Opaque(ref v) => Some(v),
@ -311,7 +330,7 @@ impl JsError {
///
/// assert!(error.as_native().is_none());
/// ```
pub fn as_native(&self) -> Option<&JsNativeError> {
pub const fn as_native(&self) -> Option<&JsNativeError> {
match self.inner {
Repr::Native(ref e) => Some(e),
Repr::Opaque(_) => None,
@ -401,6 +420,7 @@ impl JsNativeError {
/// JsNativeErrorKind::Aggregate(ref errors) if errors.len() == 2
/// ));
/// ```
#[must_use]
pub fn aggregate(errors: Vec<JsError>) -> Self {
Self::new(JsNativeErrorKind::Aggregate(errors), Box::default(), None)
}
@ -415,6 +435,7 @@ impl JsNativeError {
///
/// assert!(matches!(error.kind, JsNativeErrorKind::Error));
/// ```
#[must_use]
pub fn error() -> Self {
Self::new(JsNativeErrorKind::Error, Box::default(), None)
}
@ -429,6 +450,7 @@ impl JsNativeError {
///
/// assert!(matches!(error.kind, JsNativeErrorKind::Eval));
/// ```
#[must_use]
pub fn eval() -> Self {
Self::new(JsNativeErrorKind::Eval, Box::default(), None)
}
@ -443,6 +465,7 @@ impl JsNativeError {
///
/// assert!(matches!(error.kind, JsNativeErrorKind::Range));
/// ```
#[must_use]
pub fn range() -> Self {
Self::new(JsNativeErrorKind::Range, Box::default(), None)
}
@ -457,6 +480,7 @@ impl JsNativeError {
///
/// assert!(matches!(error.kind, JsNativeErrorKind::Reference));
/// ```
#[must_use]
pub fn reference() -> Self {
Self::new(JsNativeErrorKind::Reference, Box::default(), None)
}
@ -471,6 +495,7 @@ impl JsNativeError {
///
/// assert!(matches!(error.kind, JsNativeErrorKind::Syntax));
/// ```
#[must_use]
pub fn syntax() -> Self {
Self::new(JsNativeErrorKind::Syntax, Box::default(), None)
}
@ -485,6 +510,7 @@ impl JsNativeError {
///
/// assert!(matches!(error.kind, JsNativeErrorKind::Type));
/// ```
#[must_use]
pub fn typ() -> Self {
Self::new(JsNativeErrorKind::Type, Box::default(), None)
}
@ -499,6 +525,7 @@ impl JsNativeError {
///
/// assert!(matches!(error.kind, JsNativeErrorKind::Uri));
/// ```
#[must_use]
pub fn uri() -> Self {
Self::new(JsNativeErrorKind::Uri, Box::default(), None)
}
@ -506,6 +533,7 @@ impl JsNativeError {
/// Creates a new `JsNativeError` that indicates that the context hit its execution limit. This
/// is only used in a fuzzing context.
#[cfg(feature = "fuzz")]
#[must_use]
pub fn no_instructions_remain() -> Self {
Self::new(
JsNativeErrorKind::NoInstructionsRemain,
@ -568,7 +596,8 @@ impl JsNativeError {
///
/// assert_eq!(error.message(), "number too large");
/// ```
pub fn message(&self) -> &str {
#[must_use]
pub const fn message(&self) -> &str {
&self.message
}
@ -588,6 +617,7 @@ impl JsNativeError {
///
/// assert!(error.cause().unwrap().as_native().is_some());
/// ```
#[must_use]
pub fn cause(&self) -> Option<&JsError> {
self.cause.as_deref()
}
@ -773,16 +803,16 @@ pub enum JsNativeErrorKind {
impl std::fmt::Display for JsNativeErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
JsNativeErrorKind::Aggregate(_) => "AggregateError",
JsNativeErrorKind::Error => "Error",
JsNativeErrorKind::Eval => "EvalError",
JsNativeErrorKind::Range => "RangeError",
JsNativeErrorKind::Reference => "ReferenceError",
JsNativeErrorKind::Syntax => "SyntaxError",
JsNativeErrorKind::Type => "TypeError",
JsNativeErrorKind::Uri => "UriError",
Self::Aggregate(_) => "AggregateError",
Self::Error => "Error",
Self::Eval => "EvalError",
Self::Range => "RangeError",
Self::Reference => "ReferenceError",
Self::Syntax => "SyntaxError",
Self::Type => "TypeError",
Self::Uri => "UriError",
#[cfg(feature = "fuzz")]
JsNativeErrorKind::NoInstructionsRemain => "NoInstructionsRemain",
Self::NoInstructionsRemain => "NoInstructionsRemain",
}
.fmt(f)
}

6
boa_engine/src/job.rs

@ -1,3 +1,5 @@
//! This module contains the data structures for the microtask job queue.
use crate::{prelude::JsObject, Context, JsResult, JsValue};
use boa_gc::{Finalize, Trace};
@ -38,6 +40,10 @@ impl JobCallback {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-hostcalljobcallback
///
/// # Panics
///
/// Panics if the `JobCallback` is not callable.
pub fn call_job_callback(
&self,
v: &JsValue,

104
boa_engine/src/lib.rs

@ -15,63 +15,75 @@
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::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::missing_inline_in_public_items,
// Currently throws a false positive regarding dependencies that are only used in benchmarks.
unused_crate_dependencies,
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::redundant_pub_crate,
clippy::too_many_lines,
clippy::unreadable_literal,
clippy::cognitive_complexity,
clippy::must_use_candidate,
clippy::missing_errors_doc,
clippy::as_conversions,
clippy::let_unit_value,
// TODO deny once false positive is fixed (https://github.com/rust-lang/rust-clippy/issues/9626).
clippy::trait_duplication_in_bounds,
// Ignore because `write!(string, ...)` instead of `string.push_str(&format!(...))` can fail.
// We only use it in `ToInternedString` where performance is not an issue.
clippy::format_push_string,
clippy::option_if_let_else,
// Currently derive macros are linted. Should be fixed in 1.66. See https://github.com/rust-lang/rust-clippy/pull/9454
clippy::use_self,
// It may be worth to look if we can fix the issues highlighted by these lints.
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_precision_loss,
clippy::cast_possible_wrap
)]
extern crate static_assertions as sa;

18
boa_engine/src/object/builtins/jsarray.rs

@ -85,6 +85,7 @@ impl JsArray {
Array::pop(&self.inner.clone().into(), &[], context)
}
/// Calls `Array.prototype.at()`.
#[inline]
pub fn at<T>(&self, index: T, context: &mut Context) -> JsResult<JsValue>
where
@ -93,22 +94,26 @@ impl JsArray {
Array::at(&self.inner.clone().into(), &[index.into().into()], context)
}
/// Calls `Array.prototype.shift()`.
#[inline]
pub fn shift(&self, context: &mut Context) -> JsResult<JsValue> {
Array::shift(&self.inner.clone().into(), &[], context)
}
/// Calls `Array.prototype.unshift()`.
#[inline]
pub fn unshift(&self, items: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Array::shift(&self.inner.clone().into(), items, context)
}
/// Calls `Array.prototype.reverse()`.
#[inline]
pub fn reverse(&self, context: &mut Context) -> JsResult<Self> {
Array::reverse(&self.inner.clone().into(), &[], context)?;
Ok(self.clone())
}
/// Calls `Array.prototype.concat()`.
#[inline]
pub fn concat(&self, items: &[JsValue], context: &mut Context) -> JsResult<Self> {
let object = Array::concat(&self.inner.clone().into(), items, context)?
@ -119,6 +124,7 @@ impl JsArray {
Self::from_object(object)
}
/// Calls `Array.prototype.join()`.
#[inline]
pub fn join(&self, separator: Option<JsString>, context: &mut Context) -> JsResult<JsString> {
Array::join(
@ -133,6 +139,7 @@ impl JsArray {
})
}
/// Calls `Array.prototype.fill()`.
#[inline]
pub fn fill<T>(
&self,
@ -156,6 +163,7 @@ impl JsArray {
Ok(self.clone())
}
/// Calls `Array.prototype.indexOf()`.
#[inline]
pub fn index_of<T>(
&self,
@ -182,6 +190,7 @@ impl JsArray {
}
}
/// Calls `Array.prototype.lastIndexOf()`.
#[inline]
pub fn last_index_of<T>(
&self,
@ -208,6 +217,7 @@ impl JsArray {
}
}
/// Calls `Array.prototype.find()`.
#[inline]
pub fn find(
&self,
@ -222,6 +232,7 @@ impl JsArray {
)
}
/// Calls `Array.prototype.filter()`.
#[inline]
pub fn filter(
&self,
@ -241,6 +252,7 @@ impl JsArray {
Self::from_object(object)
}
/// Calls `Array.prototype.map()`.
#[inline]
pub fn map(
&self,
@ -260,6 +272,7 @@ impl JsArray {
Self::from_object(object)
}
/// Calls `Array.prototype.every()`.
#[inline]
pub fn every(
&self,
@ -278,6 +291,7 @@ impl JsArray {
Ok(result)
}
/// Calls `Array.prototype.some()`.
#[inline]
pub fn some(
&self,
@ -296,6 +310,7 @@ impl JsArray {
Ok(result)
}
/// Calls `Array.prototype.sort()`.
#[inline]
pub fn sort(&self, compare_fn: Option<JsFunction>, context: &mut Context) -> JsResult<Self> {
Array::sort(
@ -307,6 +322,7 @@ impl JsArray {
Ok(self.clone())
}
/// Calls `Array.prototype.slice()`.
#[inline]
pub fn slice(
&self,
@ -326,6 +342,7 @@ impl JsArray {
Self::from_object(object)
}
/// Calls `Array.prototype.reduce()`.
#[inline]
pub fn reduce(
&self,
@ -340,6 +357,7 @@ impl JsArray {
)
}
/// Calls `Array.prototype.reduceRight()`.
#[inline]
pub fn reduce_right(
&self,

2
boa_engine/src/object/builtins/jsdataview.rs

@ -34,6 +34,7 @@ pub struct JsDataView {
}
impl JsDataView {
/// Create a new `JsDataView` object from an existing `JsArrayBuffer`.
#[inline]
pub fn from_js_array_buffer(
array_buffer: &JsArrayBuffer,
@ -105,6 +106,7 @@ impl JsDataView {
Ok(Self { inner: obj })
}
/// Create a new `JsDataView` object from an existing object.
#[inline]
pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.borrow().is_data_view() {

1
boa_engine/src/object/builtins/jsproxy.rs

@ -27,6 +27,7 @@ pub struct JsProxy {
}
impl JsProxy {
/// Creates a new [`JsProxyBuilder`] to easily construct a [`JsProxy`].
pub fn builder(target: JsObject) -> JsProxyBuilder {
JsProxyBuilder::new(target)
}

19
boa_engine/src/object/builtins/jstypedarray.rs

@ -49,6 +49,7 @@ impl JsTypedArray {
Ok(self.length(context)? == 0)
}
/// Calls `TypedArray.prototype.at()`.
#[inline]
pub fn at<T>(&self, index: T, context: &mut Context) -> JsResult<JsValue>
where
@ -57,6 +58,7 @@ impl JsTypedArray {
TypedArray::at(&self.inner, &[index.into().into()], context)
}
/// Returns `TypedArray.prototype.byteLength`.
#[inline]
pub fn byte_length(&self, context: &mut Context) -> JsResult<usize> {
Ok(TypedArray::byte_length(&self.inner, &[], context)?
@ -65,6 +67,7 @@ impl JsTypedArray {
.expect("byteLength should return a number"))
}
/// Returns `TypedArray.prototype.byteOffset`.
#[inline]
pub fn byte_offset(&self, context: &mut Context) -> JsResult<usize> {
Ok(TypedArray::byte_offset(&self.inner, &[], context)?
@ -73,6 +76,7 @@ impl JsTypedArray {
.expect("byteLength should return a number"))
}
/// Calls `TypedArray.prototype.fill()`.
#[inline]
pub fn fill<T>(
&self,
@ -96,6 +100,7 @@ impl JsTypedArray {
Ok(self.clone())
}
/// Calls `TypedArray.prototype.every()`.
pub fn every(
&self,
predicate: JsFunction,
@ -113,6 +118,7 @@ impl JsTypedArray {
Ok(result)
}
/// Calls `TypedArray.prototype.some()`.
#[inline]
pub fn some(
&self,
@ -131,6 +137,7 @@ impl JsTypedArray {
Ok(result)
}
/// Calls `TypedArray.prototype.sort()`.
#[inline]
pub fn sort(&self, compare_fn: Option<JsFunction>, context: &mut Context) -> JsResult<Self> {
TypedArray::sort(&self.inner, &[compare_fn.into_or_undefined()], context)?;
@ -138,6 +145,7 @@ impl JsTypedArray {
Ok(self.clone())
}
/// Calls `TypedArray.prototype.filter()`.
#[inline]
pub fn filter(
&self,
@ -154,6 +162,7 @@ impl JsTypedArray {
Ok(Self { inner: object })
}
/// Calls `TypedArray.prototype.map()`.
#[inline]
pub fn map(
&self,
@ -170,6 +179,7 @@ impl JsTypedArray {
Ok(Self { inner: object })
}
/// Calls `TypedArray.prototype.reduce()`.
#[inline]
pub fn reduce(
&self,
@ -184,6 +194,7 @@ impl JsTypedArray {
)
}
/// Calls `TypedArray.prototype.reduceRight()`.
#[inline]
pub fn reduce_right(
&self,
@ -198,12 +209,14 @@ impl JsTypedArray {
)
}
/// Calls `TypedArray.prototype.reverse()`.
#[inline]
pub fn reverse(&self, context: &mut Context) -> JsResult<Self> {
TypedArray::reverse(&self.inner, &[], context)?;
Ok(self.clone())
}
/// Calls `TypedArray.prototype.slice()`.
#[inline]
pub fn slice(
&self,
@ -220,6 +233,7 @@ impl JsTypedArray {
Ok(Self { inner: object })
}
/// Calls `TypedArray.prototype.find()`.
#[inline]
pub fn find(
&self,
@ -234,6 +248,7 @@ impl JsTypedArray {
)
}
/// Calls `TypedArray.prototype.indexOf()`.
#[inline]
pub fn index_of<T>(
&self,
@ -260,6 +275,7 @@ impl JsTypedArray {
}
}
/// Calls `TypedArray.prototype.lastIndexOf()`.
#[inline]
pub fn last_index_of<T>(
&self,
@ -286,6 +302,7 @@ impl JsTypedArray {
}
}
/// Calls `TypedArray.prototype.join()`.
#[inline]
pub fn join(&self, separator: Option<JsString>, context: &mut Context) -> JsResult<JsString> {
TypedArray::join(&self.inner, &[separator.into_or_undefined()], context).map(|x| {
@ -333,6 +350,7 @@ macro_rules! JsTypedArrayType {
}
impl $name {
/// Create the typed array from a [`JsArrayBuffer`].
#[inline]
pub fn from_array_buffer(
array_buffer: JsArrayBuffer,
@ -360,6 +378,7 @@ macro_rules! JsTypedArrayType {
})
}
/// Create the typed array from an iterator.
#[inline]
pub fn from_iter<I>(elements: I, context: &mut Context) -> JsResult<Self>
where

2
boa_engine/src/object/internal_methods/arguments.rs

@ -141,7 +141,7 @@ pub(crate) fn arguments_exotic_define_own_property(
}
// ii. If Desc.[[Writable]] is present and its value is false, then
if let Some(false) = desc.writable() {
if desc.writable() == Some(false) {
// 1. Call map.[[Delete]](P).
map.delete(index);
}

4
boa_engine/src/object/internal_methods/mod.rs

@ -259,9 +259,9 @@ impl JsObject {
pub(crate) fn __construct__(
&self,
args: &[JsValue],
new_target: &JsObject,
new_target: &Self,
context: &mut Context,
) -> JsResult<JsObject> {
) -> JsResult<Self> {
let _timer = Profiler::global().start_event("Object::__construct__", "object");
let func = self.borrow().data.internal_methods.__construct__;
func.expect("called `[[Construct]]` for object without a `[[Construct]]` internal method")(

2
boa_engine/src/object/internal_methods/proxy.rs

@ -360,7 +360,7 @@ pub(crate) fn proxy_exotic_get_own_property(
match &target_desc {
Some(desc) if !desc.expect_configurable() => {
// b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] is false, then
if let Some(false) = result_desc.writable() {
if result_desc.writable() == Some(false) {
// i. If targetDesc.[[Writable]] is true, throw a TypeError exception.
if desc.expect_writable() {
return

12
boa_engine/src/object/jsobject.rs

@ -43,6 +43,7 @@ impl JsObject {
/// Create a new empty `JsObject`, with `prototype` set to `JsValue::Null`
/// and `data` set to `ObjectData::ordinary`
#[must_use]
pub fn empty() -> Self {
Self::from_object(Object::default())
}
@ -503,6 +504,12 @@ impl JsObject {
self.borrow().is_native_object()
}
/// The abstract operation `ToPropertyDescriptor`.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-topropertydescriptor
pub fn to_property_descriptor(&self, context: &mut Context) -> JsResult<PropertyDescriptor> {
// 1 is implemented on the method `to_property_descriptor` of value
@ -737,7 +744,7 @@ Cannot both specify accessors and a value or writable attribute",
)
}
pub(crate) fn inner(&self) -> &Gc<GcCell<Object>> {
pub(crate) const fn inner(&self) -> &Gc<GcCell<Object>> {
&self.inner
}
}
@ -752,7 +759,7 @@ impl AsRef<GcCell<Object>> for JsObject {
impl From<Gc<GcCell<Object>>> for JsObject {
#[inline]
fn from(inner: Gc<GcCell<Object>>) -> Self {
JsObject { inner }
Self { inner }
}
}
@ -848,6 +855,7 @@ impl RecursionLimiter {
/// by the hashset.
pub fn new(o: &JsObject) -> Self {
// We shouldn't have to worry too much about this being moved during Debug::fmt.
#[allow(trivial_casts)]
let ptr = (o.as_ref() as *const _) as usize;
let (top_level, visited, live) = Self::SEEN.with(|hm| {
let mut hm = hm.borrow_mut();

337
boa_engine/src/object/mod.rs

@ -88,6 +88,9 @@ pub(crate) trait JsObjectType:
/// Static `prototype`, usually set on constructors as a key to point to their respective prototype object.
pub static PROTOTYPE: &str = "prototype";
/// A type alias for an object prototype.
///
/// A `None` values means that the prototype is the `null` value.
pub type JsPrototype = Option<JsObject>;
/// This trait allows Rust types to be passed around as objects.
@ -104,12 +107,12 @@ pub trait NativeObject: Debug + Any + Trace {
impl<T: Any + Debug + Trace> NativeObject for T {
#[inline]
fn as_any(&self) -> &dyn Any {
self as &dyn Any
self
}
#[inline]
fn as_mut_any(&mut self) -> &mut dyn Any {
self as &mut dyn Any
self
}
}
@ -142,10 +145,18 @@ unsafe impl Trace for Object {
/// The representation of private object elements.
#[derive(Clone, Debug, Trace, Finalize)]
pub enum PrivateElement {
/// A private field.
Field(JsValue),
/// A private method.
Method(JsObject),
/// A private element accessor.
Accessor {
/// A getter function.
getter: Option<JsObject>,
/// A setter function.
setter: Option<JsObject>,
},
}
@ -160,40 +171,109 @@ pub struct ObjectData {
/// Defines the different types of objects.
#[derive(Debug, Finalize)]
pub enum ObjectKind {
/// The `AsyncFromSyncIterator` object kind.
AsyncFromSyncIterator(AsyncFromSyncIterator),
/// The `AsyncGenerator` object kind.
AsyncGenerator(AsyncGenerator),
/// The `AsyncGeneratorFunction` object kind.
AsyncGeneratorFunction(Function),
/// The `Array` object kind.
Array,
/// The `ArrayIterator` object kind.
ArrayIterator(ArrayIterator),
/// The `ArrayBuffer` object kind.
ArrayBuffer(ArrayBuffer),
/// The `Map` object kind.
Map(OrderedMap<JsValue>),
/// The `MapIterator` object kind.
MapIterator(MapIterator),
/// The `RegExp` object kind.
RegExp(Box<RegExp>),
/// The `RegExpStringIterator` object kind.
RegExpStringIterator(RegExpStringIterator),
/// The `BigInt` object kind.
BigInt(JsBigInt),
/// The `Boolean` object kind.
Boolean(bool),
/// The `DataView` object kind.
DataView(DataView),
/// The `ForInIterator` object kind.
ForInIterator(ForInIterator),
/// The `Function` object kind.
Function(Function),
/// The `BoundFunction` object kind.
BoundFunction(BoundFunction),
/// The `Generator` object kind.
Generator(Generator),
/// The `GeneratorFunction` object kind.
GeneratorFunction(Function),
/// The `Set` object kind.
Set(OrderedSet<JsValue>),
/// The `SetIterator` object kind.
SetIterator(SetIterator),
/// The `String` object kind.
String(JsString),
/// The `StringIterator` object kind.
StringIterator(StringIterator),
/// The `Number` object kind.
Number(f64),
/// The `Symbol` object kind.
Symbol(JsSymbol),
/// The `Error` object kind.
Error(ErrorKind),
/// The ordinary object kind.
Ordinary,
/// The `Proxy` object kind.
Proxy(Proxy),
/// The `Date` object kind.
Date(Date),
/// The `Global` object kind.
Global,
/// The arguments exotic object kind.
Arguments(Arguments),
/// The rust native object kind.
NativeObject(Box<dyn NativeObject>),
/// The integer-indexed exotic object kind.
IntegerIndexed(IntegerIndexed),
/// The `Promise` object kind.
Promise(Promise),
/// The `WeakRef` object kind.
WeakRef(WeakGc<GcCell<Object>>),
/// The `Intl.DateTimeFormat` object kind.
#[cfg(feature = "intl")]
DateTimeFormat(Box<DateTimeFormat>),
}
@ -269,6 +349,7 @@ impl ObjectData {
}
/// Create the `Array` object data and reference its exclusive internal methods
#[must_use]
pub fn array() -> Self {
Self {
kind: ObjectKind::Array,
@ -293,6 +374,7 @@ impl ObjectData {
}
/// Create the `Map` object data
#[must_use]
pub fn map(map: OrderedMap<JsValue>) -> Self {
Self {
kind: ObjectKind::Map(map),
@ -309,6 +391,7 @@ impl ObjectData {
}
/// Create the `RegExp` object data
#[must_use]
pub fn reg_exp(reg_exp: Box<RegExp>) -> Self {
Self {
kind: ObjectKind::RegExp(reg_exp),
@ -325,6 +408,7 @@ impl ObjectData {
}
/// Create the `BigInt` object data
#[must_use]
pub fn big_int(big_int: JsBigInt) -> Self {
Self {
kind: ObjectKind::BigInt(big_int),
@ -333,6 +417,7 @@ impl ObjectData {
}
/// Create the `Boolean` object data
#[must_use]
pub fn boolean(boolean: bool) -> Self {
Self {
kind: ObjectKind::Boolean(boolean),
@ -409,6 +494,7 @@ impl ObjectData {
}
/// Create the `Set` object data
#[must_use]
pub fn set(set: OrderedSet<JsValue>) -> Self {
Self {
kind: ObjectKind::Set(set),
@ -425,6 +511,7 @@ impl ObjectData {
}
/// Create the `String` object data and reference its exclusive internal methods
#[must_use]
pub fn string(string: JsString) -> Self {
Self {
kind: ObjectKind::String(string),
@ -441,6 +528,7 @@ impl ObjectData {
}
/// Create the `Number` object data
#[must_use]
pub fn number(number: f64) -> Self {
Self {
kind: ObjectKind::Number(number),
@ -449,6 +537,7 @@ impl ObjectData {
}
/// Create the `Symbol` object data
#[must_use]
pub fn symbol(symbol: JsSymbol) -> Self {
Self {
kind: ObjectKind::Symbol(symbol),
@ -465,6 +554,7 @@ impl ObjectData {
}
/// Create the `Ordinary` object data
#[must_use]
pub fn ordinary() -> Self {
Self {
kind: ObjectKind::Ordinary,
@ -487,6 +577,7 @@ impl ObjectData {
}
/// Create the `Date` object data
#[must_use]
pub fn date(date: Date) -> Self {
Self {
kind: ObjectKind::Date(date),
@ -495,6 +586,7 @@ impl ObjectData {
}
/// Create the `Global` object data
#[must_use]
pub fn global() -> Self {
Self {
kind: ObjectKind::Global,
@ -523,6 +615,7 @@ impl ObjectData {
}
/// Create the `NativeObject` object data
#[must_use]
pub fn native_object(native_object: Box<dyn NativeObject>) -> Self {
Self {
kind: ObjectKind::NativeObject(native_object),
@ -540,6 +633,7 @@ impl ObjectData {
/// Create the `DateTimeFormat` object data
#[cfg(feature = "intl")]
#[must_use]
pub fn date_time_format(date_time_fmt: Box<DateTimeFormat>) -> Self {
Self {
kind: ObjectKind::DateTimeFormat(date_time_fmt),
@ -615,14 +709,15 @@ impl Default for Object {
}
impl Object {
/// Returns the kind of the object.
#[inline]
pub fn kind(&self) -> &ObjectKind {
pub const fn kind(&self) -> &ObjectKind {
&self.data.kind
}
/// Checks if it's an `AsyncFromSyncIterator` object.
#[inline]
pub fn is_async_from_sync_iterator(&self) -> bool {
pub const fn is_async_from_sync_iterator(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -634,7 +729,7 @@ impl Object {
/// Returns a reference to the `AsyncFromSyncIterator` data on the object.
#[inline]
pub fn as_async_from_sync_iterator(&self) -> Option<&AsyncFromSyncIterator> {
pub const fn as_async_from_sync_iterator(&self) -> Option<&AsyncFromSyncIterator> {
match self.data {
ObjectData {
kind: ObjectKind::AsyncFromSyncIterator(ref async_from_sync_iterator),
@ -646,7 +741,7 @@ impl Object {
/// Checks if it's an `AsyncGenerator` object.
#[inline]
pub fn is_async_generator(&self) -> bool {
pub const fn is_async_generator(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -658,7 +753,7 @@ impl Object {
/// Returns a reference to the async generator data on the object.
#[inline]
pub fn as_async_generator(&self) -> Option<&AsyncGenerator> {
pub const fn as_async_generator(&self) -> Option<&AsyncGenerator> {
match self.data {
ObjectData {
kind: ObjectKind::AsyncGenerator(ref async_generator),
@ -680,9 +775,9 @@ impl Object {
}
}
/// Checks if it an `Array` object.
/// Checks if the object is a `Array` object.
#[inline]
pub fn is_array(&self) -> bool {
pub const fn is_array(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -692,37 +787,14 @@ impl Object {
)
}
/// Checks if it is an `ArrayIterator` object.
#[inline]
pub fn is_array_iterator(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::ArrayIterator(_),
..
}
)
}
#[inline]
pub fn as_array_iterator(&self) -> Option<&ArrayIterator> {
match self.data {
ObjectData {
kind: ObjectKind::ArrayIterator(ref iter),
..
} => Some(iter),
_ => None,
}
}
#[inline]
pub(crate) fn has_viewed_array_buffer(&self) -> bool {
pub(crate) const fn has_viewed_array_buffer(&self) -> bool {
self.is_typed_array() || self.is_data_view()
}
/// Checks if it an `DataView` object.
/// Checks if the object is a `DataView` object.
#[inline]
pub fn is_data_view(&self) -> bool {
pub const fn is_data_view(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -732,9 +804,9 @@ impl Object {
)
}
/// Checks if it an `ArrayBuffer` object.
/// Checks if the object is a `ArrayBuffer` object.
#[inline]
pub fn is_array_buffer(&self) -> bool {
pub const fn is_array_buffer(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -744,8 +816,9 @@ impl Object {
)
}
/// Gets the array buffer data if the object is a `ArrayBuffer`.
#[inline]
pub fn as_array_buffer(&self) -> Option<&ArrayBuffer> {
pub const fn as_array_buffer(&self) -> Option<&ArrayBuffer> {
match &self.data {
ObjectData {
kind: ObjectKind::ArrayBuffer(buffer),
@ -755,6 +828,7 @@ impl Object {
}
}
/// Gets the mutable array buffer data if the object is a `ArrayBuffer`.
#[inline]
pub fn as_array_buffer_mut(&mut self) -> Option<&mut ArrayBuffer> {
match &mut self.data {
@ -766,6 +840,31 @@ impl Object {
}
}
/// Checks if the object is a `ArrayIterator` object.
#[inline]
pub const fn is_array_iterator(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::ArrayIterator(_),
..
}
)
}
/// Gets the array-iterator data if the object is a `ArrayIterator`.
#[inline]
pub const fn as_array_iterator(&self) -> Option<&ArrayIterator> {
match self.data {
ObjectData {
kind: ObjectKind::ArrayIterator(ref iter),
..
} => Some(iter),
_ => None,
}
}
/// Gets the mutable array-iterator data if the object is a `ArrayIterator`.
#[inline]
pub fn as_array_iterator_mut(&mut self) -> Option<&mut ArrayIterator> {
match &mut self.data {
@ -777,6 +876,7 @@ impl Object {
}
}
/// Gets the mutable string-iterator data if the object is a `StringIterator`.
#[inline]
pub fn as_string_iterator_mut(&mut self) -> Option<&mut StringIterator> {
match &mut self.data {
@ -788,6 +888,7 @@ impl Object {
}
}
/// Gets the mutable regexp-string-iterator data if the object is a `RegExpStringIterator`.
#[inline]
pub fn as_regexp_string_iterator_mut(&mut self) -> Option<&mut RegExpStringIterator> {
match &mut self.data {
@ -799,8 +900,9 @@ impl Object {
}
}
/// Gets the for-in-iterator data if the object is a `ForInIterator`.
#[inline]
pub fn as_for_in_iterator(&self) -> Option<&ForInIterator> {
pub const fn as_for_in_iterator(&self) -> Option<&ForInIterator> {
match &self.data {
ObjectData {
kind: ObjectKind::ForInIterator(iter),
@ -810,6 +912,7 @@ impl Object {
}
}
/// Gets the mutable for-in-iterator data if the object is a `ForInIterator`.
#[inline]
pub fn as_for_in_iterator_mut(&mut self) -> Option<&mut ForInIterator> {
match &mut self.data {
@ -821,9 +924,9 @@ impl Object {
}
}
/// Checks if it is a `Map` object.pub
/// Checks if the object is a `Map` object.
#[inline]
pub fn is_map(&self) -> bool {
pub const fn is_map(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -833,8 +936,9 @@ impl Object {
)
}
/// Gets the map data if the object is a `Map`.
#[inline]
pub fn as_map_ref(&self) -> Option<&OrderedMap<JsValue>> {
pub const fn as_map(&self) -> Option<&OrderedMap<JsValue>> {
match self.data {
ObjectData {
kind: ObjectKind::Map(ref map),
@ -844,6 +948,7 @@ impl Object {
}
}
/// Gets the mutable map data if the object is a `Map`.
#[inline]
pub fn as_map_mut(&mut self) -> Option<&mut OrderedMap<JsValue>> {
match &mut self.data {
@ -855,8 +960,9 @@ impl Object {
}
}
/// Checks if the object is a `MapIterator` object.
#[inline]
pub fn is_map_iterator(&self) -> bool {
pub const fn is_map_iterator(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -866,8 +972,9 @@ impl Object {
)
}
/// Gets the map iterator data if the object is a `MapIterator`.
#[inline]
pub fn as_map_iterator_ref(&self) -> Option<&MapIterator> {
pub const fn as_map_iterator_ref(&self) -> Option<&MapIterator> {
match &self.data {
ObjectData {
kind: ObjectKind::MapIterator(iter),
@ -877,6 +984,7 @@ impl Object {
}
}
/// Gets the mutable map iterator data if the object is a `MapIterator`.
#[inline]
pub fn as_map_iterator_mut(&mut self) -> Option<&mut MapIterator> {
match &mut self.data {
@ -888,8 +996,9 @@ impl Object {
}
}
/// Checks if the object is a `Set` object.
#[inline]
pub fn is_set(&self) -> bool {
pub const fn is_set(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -899,20 +1008,9 @@ impl Object {
)
}
/// Checks if it is an `SetIterator` object.
#[inline]
pub fn is_set_iterator(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::SetIterator(_),
..
}
)
}
/// Gets the set data if the object is a `Set`.
#[inline]
pub fn as_set_ref(&self) -> Option<&OrderedSet<JsValue>> {
pub const fn as_set(&self) -> Option<&OrderedSet<JsValue>> {
match self.data {
ObjectData {
kind: ObjectKind::Set(ref set),
@ -922,6 +1020,7 @@ impl Object {
}
}
/// Gets the mutable set data if the object is a `Set`.
#[inline]
pub fn as_set_mut(&mut self) -> Option<&mut OrderedSet<JsValue>> {
match &mut self.data {
@ -933,6 +1032,19 @@ impl Object {
}
}
/// Checks if the object is a `SetIterator` object.
#[inline]
pub const fn is_set_iterator(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::SetIterator(_),
..
}
)
}
/// Gets the mutable set iterator data if the object is a `SetIterator`.
#[inline]
pub fn as_set_iterator_mut(&mut self) -> Option<&mut SetIterator> {
match &mut self.data {
@ -944,9 +1056,9 @@ impl Object {
}
}
/// Checks if it a `String` object.
/// Checks if the object is a `String` object.
#[inline]
pub fn is_string(&self) -> bool {
pub const fn is_string(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -956,6 +1068,7 @@ impl Object {
)
}
/// Gets the string data if the object is a `String`.
#[inline]
pub fn as_string(&self) -> Option<JsString> {
match self.data {
@ -967,9 +1080,9 @@ impl Object {
}
}
/// Checks if it a `Function` object.
/// Checks if the object is a `Function` object.
#[inline]
pub fn is_function(&self) -> bool {
pub const fn is_function(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -979,8 +1092,9 @@ impl Object {
)
}
/// Gets the function data if the object is a `Function`.
#[inline]
pub fn as_function(&self) -> Option<&Function> {
pub const fn as_function(&self) -> Option<&Function> {
match self.data {
ObjectData {
kind:
@ -991,6 +1105,7 @@ impl Object {
}
}
/// Gets the mutable function data if the object is a `Function`.
#[inline]
pub fn as_function_mut(&mut self) -> Option<&mut Function> {
match self.data {
@ -1004,8 +1119,9 @@ impl Object {
}
}
/// Gets the bound function data if the object is a `BoundFunction`.
#[inline]
pub fn as_bound_function(&self) -> Option<&BoundFunction> {
pub const fn as_bound_function(&self) -> Option<&BoundFunction> {
match self.data {
ObjectData {
kind: ObjectKind::BoundFunction(ref bound_function),
@ -1015,9 +1131,9 @@ impl Object {
}
}
/// Checks if it's a `Generator` object.
/// Checks if the object is a `Generator` object.
#[inline]
pub fn is_generator(&self) -> bool {
pub const fn is_generator(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -1027,9 +1143,9 @@ impl Object {
)
}
/// Returns a reference to the generator data on the object.
/// Gets the generator data if the object is a `Generator`.
#[inline]
pub fn as_generator(&self) -> Option<&Generator> {
pub const fn as_generator(&self) -> Option<&Generator> {
match self.data {
ObjectData {
kind: ObjectKind::Generator(ref generator),
@ -1039,7 +1155,7 @@ impl Object {
}
}
/// Returns a mutable reference to the generator data on the object.
/// Gets the mutable generator data if the object is a `Generator`.
#[inline]
pub fn as_generator_mut(&mut self) -> Option<&mut Generator> {
match self.data {
@ -1051,9 +1167,9 @@ impl Object {
}
}
/// Checks if it a Symbol object.
/// Checks if the object is a `Symbol` object.
#[inline]
pub fn is_symbol(&self) -> bool {
pub const fn is_symbol(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -1063,6 +1179,7 @@ impl Object {
)
}
/// Gets the error data if the object is a `Symbol`.
#[inline]
pub fn as_symbol(&self) -> Option<JsSymbol> {
match self.data {
@ -1074,9 +1191,9 @@ impl Object {
}
}
/// Checks if it an Error object.
/// Checks if the object is a `Error` object.
#[inline]
pub fn is_error(&self) -> bool {
pub const fn is_error(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -1086,8 +1203,9 @@ impl Object {
)
}
/// Gets the error data if the object is a `Error`.
#[inline]
pub fn as_error(&self) -> Option<ErrorKind> {
pub const fn as_error(&self) -> Option<ErrorKind> {
match self.data {
ObjectData {
kind: ObjectKind::Error(e),
@ -1097,9 +1215,9 @@ impl Object {
}
}
/// Checks if it a Boolean object.
/// Checks if the object is a `Boolean` object.
#[inline]
pub fn is_boolean(&self) -> bool {
pub const fn is_boolean(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -1109,8 +1227,9 @@ impl Object {
)
}
/// Gets the boolean data if the object is a `Boolean`.
#[inline]
pub fn as_boolean(&self) -> Option<bool> {
pub const fn as_boolean(&self) -> Option<bool> {
match self.data {
ObjectData {
kind: ObjectKind::Boolean(boolean),
@ -1120,9 +1239,9 @@ impl Object {
}
}
/// Checks if it a `Number` object.
/// Checks if the object is a `Number` object.
#[inline]
pub fn is_number(&self) -> bool {
pub const fn is_number(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -1132,8 +1251,9 @@ impl Object {
)
}
/// Gets the number data if the object is a `Number`.
#[inline]
pub fn as_number(&self) -> Option<f64> {
pub const fn as_number(&self) -> Option<f64> {
match self.data {
ObjectData {
kind: ObjectKind::Number(number),
@ -1143,9 +1263,9 @@ impl Object {
}
}
/// Checks if it a `BigInt` object.
/// Checks if the object is a `BigInt` object.
#[inline]
pub fn is_bigint(&self) -> bool {
pub const fn is_bigint(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -1155,8 +1275,9 @@ impl Object {
)
}
/// Gets the bigint data if the object is a `BigInt`.
#[inline]
pub fn as_bigint(&self) -> Option<&JsBigInt> {
pub const fn as_bigint(&self) -> Option<&JsBigInt> {
match self.data {
ObjectData {
kind: ObjectKind::BigInt(ref bigint),
@ -1166,8 +1287,9 @@ impl Object {
}
}
/// Checks if the object is a `Date` object.
#[inline]
pub fn is_date(&self) -> bool {
pub const fn is_date(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -1177,8 +1299,9 @@ impl Object {
)
}
/// Gets the date data if the object is a `Date`.
#[inline]
pub fn as_date(&self) -> Option<&Date> {
pub const fn as_date(&self) -> Option<&Date> {
match self.data {
ObjectData {
kind: ObjectKind::Date(ref date),
@ -1188,6 +1311,7 @@ impl Object {
}
}
/// Gets the mutable date data if the object is a `Date`.
#[inline]
pub fn as_date_mut(&mut self) -> Option<&mut Date> {
match self.data {
@ -1201,7 +1325,7 @@ impl Object {
/// Checks if it a `RegExp` object.
#[inline]
pub fn is_regexp(&self) -> bool {
pub const fn is_regexp(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -1213,7 +1337,7 @@ impl Object {
/// Gets the regexp data if the object is a regexp.
#[inline]
pub fn as_regexp(&self) -> Option<&RegExp> {
pub const fn as_regexp(&self) -> Option<&RegExp> {
match self.data {
ObjectData {
kind: ObjectKind::RegExp(ref regexp),
@ -1225,7 +1349,7 @@ impl Object {
/// Checks if it a `TypedArray` object.
#[inline]
pub fn is_typed_array(&self) -> bool {
pub const fn is_typed_array(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -1235,8 +1359,9 @@ impl Object {
)
}
/// Gets the data view data if the object is a `DataView`.
#[inline]
pub fn as_data_view(&self) -> Option<&DataView> {
pub const fn as_data_view(&self) -> Option<&DataView> {
match &self.data {
ObjectData {
kind: ObjectKind::DataView(data_view),
@ -1246,6 +1371,7 @@ impl Object {
}
}
/// Gets the mutable data view data if the object is a `DataView`.
#[inline]
pub fn as_data_view_mut(&mut self) -> Option<&mut DataView> {
match &mut self.data {
@ -1259,7 +1385,7 @@ impl Object {
/// Checks if it is an `Arguments` object.
#[inline]
pub fn is_arguments(&self) -> bool {
pub const fn is_arguments(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -1271,7 +1397,7 @@ impl Object {
/// Gets the mapped arguments data if this is a mapped arguments object.
#[inline]
pub fn as_mapped_arguments(&self) -> Option<&ParameterMap> {
pub const fn as_mapped_arguments(&self) -> Option<&ParameterMap> {
match self.data {
ObjectData {
kind: ObjectKind::Arguments(Arguments::Mapped(ref args)),
@ -1295,7 +1421,7 @@ impl Object {
/// Gets the typed array data (integer indexed object) if this is a typed array.
#[inline]
pub fn as_typed_array(&self) -> Option<&IntegerIndexed> {
pub const fn as_typed_array(&self) -> Option<&IntegerIndexed> {
match self.data {
ObjectData {
kind: ObjectKind::IntegerIndexed(ref integer_indexed_object),
@ -1319,7 +1445,7 @@ impl Object {
/// Checks if it an ordinary object.
#[inline]
pub fn is_ordinary(&self) -> bool {
pub const fn is_ordinary(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -1331,7 +1457,7 @@ impl Object {
/// Checks if it's an proxy object.
#[inline]
pub fn is_proxy(&self) -> bool {
pub const fn is_proxy(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -1341,8 +1467,9 @@ impl Object {
)
}
/// Gets the proxy data if the object is a `Proxy`.
#[inline]
pub fn as_proxy(&self) -> Option<&Proxy> {
pub const fn as_proxy(&self) -> Option<&Proxy> {
match self.data {
ObjectData {
kind: ObjectKind::Proxy(ref proxy),
@ -1352,6 +1479,7 @@ impl Object {
}
}
/// Gets the mutable proxy data if the object is a `Proxy`.
#[inline]
pub fn as_proxy_mut(&mut self) -> Option<&mut Proxy> {
match self.data {
@ -1365,7 +1493,7 @@ impl Object {
/// Gets the prototype instance of this object.
#[inline]
pub fn prototype(&self) -> &JsPrototype {
pub const fn prototype(&self) -> &JsPrototype {
&self.prototype
}
@ -1390,7 +1518,7 @@ impl Object {
/// Returns `true` if it holds an Rust type that implements `NativeObject`.
#[inline]
pub fn is_native_object(&self) -> bool {
pub const fn is_native_object(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -1400,6 +1528,7 @@ impl Object {
)
}
/// Gets the native object data if the object is a `NativeObject`.
#[inline]
pub fn as_native_object(&self) -> Option<&dyn NativeObject> {
match self.data {
@ -1413,7 +1542,7 @@ impl Object {
/// Checks if it is a `Promise` object.
#[inline]
pub fn is_promise(&self) -> bool {
pub const fn is_promise(&self) -> bool {
matches!(
self.data,
ObjectData {
@ -1423,9 +1552,9 @@ impl Object {
)
}
/// Gets the promise data if the object is a promise.
/// Gets the promise data if the object is a `Promise`.
#[inline]
pub fn as_promise(&self) -> Option<&Promise> {
pub const fn as_promise(&self) -> Option<&Promise> {
match self.data {
ObjectData {
kind: ObjectKind::Promise(ref promise),
@ -1435,6 +1564,7 @@ impl Object {
}
}
/// Gets the mutable promise data if the object is a `Promise`.
#[inline]
pub fn as_promise_mut(&mut self) -> Option<&mut Promise> {
match self.data {
@ -1448,7 +1578,7 @@ impl Object {
/// Gets the `WeakRef`data if the object is a `WeakRef`.
#[inline]
pub fn as_weak_ref(&self) -> Option<&WeakGc<GcCell<Object>>> {
pub const fn as_weak_ref(&self) -> Option<&WeakGc<GcCell<Self>>> {
match self.data {
ObjectData {
kind: ObjectKind::WeakRef(ref weak_ref),
@ -1505,8 +1635,9 @@ impl Object {
}
}
/// Returns the properties of the object.
#[inline]
pub fn properties(&self) -> &PropertyMap {
pub const fn properties(&self) -> &PropertyMap {
&self.properties
}
@ -1755,7 +1886,7 @@ impl<'context> FunctionBuilder<'context> {
/// The default is `0`.
#[inline]
#[must_use]
pub fn length(mut self, length: usize) -> Self {
pub const fn length(mut self, length: usize) -> Self {
self.length = length;
self
}

23
boa_engine/src/object/operations.rs

@ -32,12 +32,14 @@ pub enum IntegrityLevel {
impl IntegrityLevel {
/// Returns `true` if the integrity level is sealed.
pub fn is_sealed(&self) -> bool {
#[must_use]
pub const fn is_sealed(&self) -> bool {
matches!(self, Self::Sealed)
}
/// Returns `true` if the integrity level is frozen.
pub fn is_frozen(&self) -> bool {
#[must_use]
pub const fn is_frozen(&self) -> bool {
matches!(self, Self::Frozen)
}
}
@ -335,9 +337,9 @@ impl JsObject {
pub fn construct(
&self,
args: &[JsValue],
new_target: Option<&JsObject>,
new_target: Option<&Self>,
context: &mut Context,
) -> JsResult<JsObject> {
) -> JsResult<Self> {
// 1. If newTarget is not present, set newTarget to F.
let new_target = new_target.unwrap_or(self);
// 2. If argumentsList is not present, set argumentsList to a new empty List.
@ -515,13 +517,14 @@ impl JsObject {
}
// 7. If IsConstructor(S) is true, return S.
// 8. Throw a TypeError exception.
match s.as_object() {
Some(obj) if obj.is_constructor() => Ok(obj.clone()),
_ => Err(JsNativeError::typ()
.with_message("property 'constructor' is not a constructor")
.into()),
if let Some(s) = s.as_constructor() {
return Ok(s.clone());
}
// 8. Throw a TypeError exception.
Err(JsNativeError::typ()
.with_message("property 'constructor' is not a constructor")
.into())
}
/// It is used to iterate over names of object's keys.

58
boa_engine/src/object/property_map.rs

@ -221,20 +221,31 @@ impl IndexedProperties {
}
}
/// A [`PropertyMap`] contains all the properties of an object.
///
/// The property values are stored in different data structures based on keys.
#[derive(Default, Debug, Trace, Finalize)]
pub struct PropertyMap {
/// Properties stored with integers as keys.
indexed_properties: IndexedProperties,
/// Properties
/// Properties stored with `String`s a keys.
string_properties: OrderedHashMap<JsString>,
/// Symbol Properties
/// Properties stored with `Symbol`s a keys.
symbol_properties: OrderedHashMap<JsSymbol>,
}
impl PropertyMap {
/// Create a new [`PropertyMap`].
#[must_use]
#[inline]
pub fn new() -> Self {
Self::default()
}
/// Get the property with the given key from the [`PropertyMap`].
#[must_use]
pub fn get(&self, key: &PropertyKey) -> Option<PropertyDescriptor> {
match key {
PropertyKey::Index(index) => self.indexed_properties.get(*index),
@ -243,6 +254,7 @@ impl PropertyMap {
}
}
/// Insert the given property descriptor with the given key [`PropertyMap`].
pub fn insert(
&mut self,
key: &PropertyKey,
@ -259,6 +271,7 @@ impl PropertyMap {
}
}
/// Remove the property with the given key from the [`PropertyMap`].
pub fn remove(&mut self, key: &PropertyKey) -> Option<PropertyDescriptor> {
match key {
PropertyKey::Index(index) => self.indexed_properties.remove(*index),
@ -273,7 +286,7 @@ impl PropertyMap {
}
/// Returns the vec of dense indexed properties if they exist.
pub(crate) fn dense_indexed_properties(&self) -> Option<&Vec<JsValue>> {
pub(crate) const fn dense_indexed_properties(&self) -> Option<&Vec<JsValue>> {
if let IndexedProperties::Dense(properties) = &self.indexed_properties {
Some(properties)
} else {
@ -285,6 +298,7 @@ impl PropertyMap {
///
/// This iterator does not recurse down the prototype chain.
#[inline]
#[must_use]
pub fn iter(&self) -> Iter<'_> {
Iter {
indexed_properties: self.indexed_properties.iter(),
@ -297,6 +311,7 @@ impl PropertyMap {
///
/// This iterator does not recurse down the prototype chain.
#[inline]
#[must_use]
pub fn keys(&self) -> Keys<'_> {
Keys(self.iter())
}
@ -305,6 +320,7 @@ impl PropertyMap {
///
/// This iterator does not recurse down the prototype chain.
#[inline]
#[must_use]
pub fn values(&self) -> Values<'_> {
Values(self.iter())
}
@ -314,6 +330,7 @@ impl PropertyMap {
///
/// This iterator does not recurse down the prototype chain.
#[inline]
#[must_use]
pub fn symbol_properties(&self) -> SymbolProperties<'_> {
SymbolProperties(self.symbol_properties.0.iter())
}
@ -322,6 +339,7 @@ impl PropertyMap {
///
/// This iterator does not recurse down the prototype chain.
#[inline]
#[must_use]
pub fn symbol_property_keys(&self) -> SymbolPropertyKeys<'_> {
SymbolPropertyKeys(self.symbol_properties.0.keys())
}
@ -330,6 +348,7 @@ impl PropertyMap {
///
/// This iterator does not recurse down the prototype chain.
#[inline]
#[must_use]
pub fn symbol_property_values(&self) -> SymbolPropertyValues<'_> {
SymbolPropertyValues(self.symbol_properties.0.values())
}
@ -338,6 +357,7 @@ impl PropertyMap {
///
/// This iterator does not recurse down the prototype chain.
#[inline]
#[must_use]
pub fn index_properties(&self) -> IndexProperties<'_> {
self.indexed_properties.iter()
}
@ -346,6 +366,7 @@ impl PropertyMap {
///
/// This iterator does not recurse down the prototype chain.
#[inline]
#[must_use]
pub fn index_property_keys(&self) -> IndexPropertyKeys<'_> {
self.indexed_properties.keys()
}
@ -354,6 +375,7 @@ impl PropertyMap {
///
/// This iterator does not recurse down the prototype chain.
#[inline]
#[must_use]
pub fn index_property_values(&self) -> IndexPropertyValues<'_> {
self.indexed_properties.values()
}
@ -362,6 +384,7 @@ impl PropertyMap {
///
/// This iterator does not recurse down the prototype chain.
#[inline]
#[must_use]
pub fn string_properties(&self) -> StringProperties<'_> {
StringProperties(self.string_properties.0.iter())
}
@ -370,6 +393,7 @@ impl PropertyMap {
///
/// This iterator does not recurse down the prototype chain.
#[inline]
#[must_use]
pub fn string_property_keys(&self) -> StringPropertyKeys<'_> {
StringPropertyKeys(self.string_properties.0.keys())
}
@ -378,11 +402,14 @@ impl PropertyMap {
///
/// This iterator does not recurse down the prototype chain.
#[inline]
#[must_use]
pub fn string_property_values(&self) -> StringPropertyValues<'_> {
StringPropertyValues(self.string_properties.0.values())
}
/// Returns `true` if the given key is contained in the [`PropertyMap`].
#[inline]
#[must_use]
pub fn contains_key(&self, key: &PropertyKey) -> bool {
match key {
PropertyKey::Index(index) => self.indexed_properties.contains_key(*index),
@ -392,7 +419,7 @@ impl PropertyMap {
}
#[inline]
pub(crate) fn string_property_map(&self) -> &GlobalPropertyMap {
pub(crate) const fn string_property_map(&self) -> &GlobalPropertyMap {
&self.string_properties.0
}
@ -410,7 +437,7 @@ pub struct Iter<'a> {
symbol_properties: indexmap::map::Iter<'a, JsSymbol, PropertyDescriptor>,
}
impl<'a> Iterator for Iter<'a> {
impl Iterator for Iter<'_> {
type Item = (PropertyKey, PropertyDescriptor);
fn next(&mut self) -> Option<Self::Item> {
if let Some((key, value)) = self.indexed_properties.next() {
@ -437,7 +464,7 @@ impl FusedIterator for Iter<'_> {}
#[derive(Debug, Clone)]
pub struct Keys<'a>(Iter<'a>);
impl<'a> Iterator for Keys<'a> {
impl Iterator for Keys<'_> {
type Item = PropertyKey;
fn next(&mut self) -> Option<Self::Item> {
let (key, _) = self.0.next()?;
@ -458,7 +485,7 @@ impl FusedIterator for Keys<'_> {}
#[derive(Debug, Clone)]
pub struct Values<'a>(Iter<'a>);
impl<'a> Iterator for Values<'a> {
impl Iterator for Values<'_> {
type Item = PropertyDescriptor;
fn next(&mut self) -> Option<Self::Item> {
let (_, value) = self.0.next()?;
@ -556,14 +583,17 @@ impl ExactSizeIterator for SymbolPropertyValues<'_> {
impl FusedIterator for SymbolPropertyValues<'_> {}
/// An iterator over the indexed property entries of an `Object`
/// An iterator over the indexed property entries of an `Object`.
#[derive(Debug, Clone)]
pub enum IndexProperties<'a> {
/// An iterator over dense, Vec backed indexed property entries of an `Object`.
Dense(std::iter::Enumerate<std::slice::Iter<'a, JsValue>>),
/// An iterator over sparse, HashMap backed indexed property entries of an `Object`.
Sparse(hash_map::Iter<'a, u32, PropertyDescriptor>),
}
impl<'a> Iterator for IndexProperties<'a> {
impl Iterator for IndexProperties<'_> {
type Item = (u32, PropertyDescriptor);
#[inline]
@ -608,11 +638,14 @@ impl FusedIterator for IndexProperties<'_> {}
/// An iterator over the index keys (`u32`) of an `Object`.
#[derive(Debug, Clone)]
pub enum IndexPropertyKeys<'a> {
/// An iterator over dense, Vec backed indexed property entries of an `Object`.
Dense(std::ops::Range<u32>),
/// An iterator over sparse, HashMap backed indexed property entries of an `Object`.
Sparse(hash_map::Keys<'a, u32, PropertyDescriptor>),
}
impl<'a> Iterator for IndexPropertyKeys<'a> {
impl Iterator for IndexPropertyKeys<'_> {
type Item = u32;
#[inline]
@ -647,11 +680,14 @@ impl FusedIterator for IndexPropertyKeys<'_> {}
/// An iterator over the index values (`Property`) of an `Object`.
#[derive(Debug, Clone)]
pub enum IndexPropertyValues<'a> {
/// An iterator over dense, Vec backed indexed property entries of an `Object`.
Dense(std::slice::Iter<'a, JsValue>),
/// An iterator over sparse, HashMap backed indexed property entries of an `Object`.
Sparse(hash_map::Values<'a, u32, PropertyDescriptor>),
}
impl<'a> Iterator for IndexPropertyValues<'a> {
impl Iterator for IndexPropertyValues<'_> {
type Item = PropertyDescriptor;
#[inline]

9
boa_engine/src/property/attribute/mod.rs

@ -55,7 +55,8 @@ impl Attribute {
/// Gets the `writable` flag.
#[inline]
pub fn writable(self) -> bool {
#[must_use]
pub const fn writable(self) -> bool {
self.contains(Self::WRITABLE)
}
@ -71,7 +72,8 @@ impl Attribute {
/// Gets the `enumerable` flag.
#[inline]
pub fn enumerable(self) -> bool {
#[must_use]
pub const fn enumerable(self) -> bool {
self.contains(Self::ENUMERABLE)
}
@ -87,7 +89,8 @@ impl Attribute {
/// Gets the `configurable` flag.
#[inline]
pub fn configurable(self) -> bool {
#[must_use]
pub const fn configurable(self) -> bool {
self.contains(Self::CONFIGURABLE)
}
}

249
boa_engine/src/property/mod.rs

@ -15,11 +15,12 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
//! [section]: https://tc39.es/ecma262/#sec-property-attributes
mod attribute;
use crate::{js_string, JsString, JsSymbol, JsValue};
use boa_gc::{Finalize, Trace};
use std::fmt;
mod attribute;
pub use attribute::Attribute;
/// This represents a JavaScript Property AKA The Property Descriptor.
@ -51,16 +52,28 @@ pub struct PropertyDescriptor {
kind: DescriptorKind,
}
/// `DescriptorKind` represents the different kinds of property descriptors.
#[derive(Debug, Clone, Trace, Finalize)]
pub enum DescriptorKind {
/// A data property descriptor.
Data {
/// The value of the property.
value: Option<JsValue>,
/// Whether the property is writable.
writable: Option<bool>,
},
/// An accessor property descriptor.
Accessor {
/// The getter of the property.
get: Option<JsValue>,
/// The setter of the property.
set: Option<JsValue>,
},
/// A generic property descriptor.
Generic,
}
@ -71,150 +84,179 @@ impl Default for DescriptorKind {
}
impl PropertyDescriptor {
/// An accessor Property Descriptor is one that includes any fields named either `[[Get]]` or `[[Set]]`.
/// An accessor property descriptor is one that includes any fields named either `[[Get]]` or `[[Set]]`.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-isaccessordescriptor
#[inline]
pub fn is_accessor_descriptor(&self) -> bool {
pub const fn is_accessor_descriptor(&self) -> bool {
matches!(self.kind, DescriptorKind::Accessor { .. })
}
/// A data Property Descriptor is one that includes any fields named either `[[Value]]` or `[[Writable]]`.
/// A data property descriptor is one that includes any fields named either `[[Value]]` or `[[Writable]]`.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-isdatadescriptor
#[inline]
pub fn is_data_descriptor(&self) -> bool {
pub const fn is_data_descriptor(&self) -> bool {
matches!(self.kind, DescriptorKind::Data { .. })
}
/// A generic Property Descriptor is one that is neither a data descriptor nor an accessor descriptor.
/// A generic property descriptor is one that is neither a data descriptor nor an accessor descriptor.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-isgenericdescriptor
#[inline]
pub fn is_generic_descriptor(&self) -> bool {
pub const fn is_generic_descriptor(&self) -> bool {
matches!(self.kind, DescriptorKind::Generic)
}
/// Returns if the property descriptor is empty.
#[inline]
pub fn is_empty(&self) -> bool {
pub const fn is_empty(&self) -> bool {
self.is_generic_descriptor() && self.enumerable.is_none() && self.configurable.is_none()
}
/// Returns if the property descriptor is enumerable.
/// Returns `None` if the `enumerable` field is not set.
#[inline]
pub fn enumerable(&self) -> Option<bool> {
pub const fn enumerable(&self) -> Option<bool> {
self.enumerable
}
/// Returns if the property descriptor is configurable.
/// Returns `None` if the `configurable` field is not set.
#[inline]
pub fn configurable(&self) -> Option<bool> {
pub const fn configurable(&self) -> Option<bool> {
self.configurable
}
/// Returns if the property descriptor is writable.
/// Returns `None` if the `writable` field is not set or the property descriptor is not a data descriptor.
#[inline]
pub fn writable(&self) -> Option<bool> {
pub const fn writable(&self) -> Option<bool> {
match self.kind {
DescriptorKind::Data { writable, .. } => writable,
_ => None,
}
}
/// Returns the value of the property descriptor.
/// Returns `None` if the value is not set or the property descriptor is not a data descriptor.
#[inline]
pub fn value(&self) -> Option<&JsValue> {
pub const fn value(&self) -> Option<&JsValue> {
match &self.kind {
DescriptorKind::Data { value, .. } => value.as_ref(),
_ => None,
}
}
/// Returns the getter of the property descriptor.
/// Returns `None` if the getter is not set or the property descriptor is not an accessor descriptor.
#[inline]
pub fn get(&self) -> Option<&JsValue> {
pub const fn get(&self) -> Option<&JsValue> {
match &self.kind {
DescriptorKind::Accessor { get, .. } => get.as_ref(),
_ => None,
}
}
/// Returns the setter of the property descriptor.
/// Returns `None` if the setter is not set or the property descriptor is not an accessor descriptor.
#[inline]
pub fn set(&self) -> Option<&JsValue> {
pub const fn set(&self) -> Option<&JsValue> {
match &self.kind {
DescriptorKind::Accessor { set, .. } => set.as_ref(),
_ => None,
}
}
/// Returns if the property descriptor is enumerable.
///
/// # Panics
///
/// Panics if the `enumerable` field is not set.
#[inline]
pub fn expect_enumerable(&self) -> bool {
if let Some(enumerable) = self.enumerable {
enumerable
} else {
panic!("[[enumerable]] field not in property descriptor")
}
self.enumerable
.expect("[[enumerable]] field not in property descriptor")
}
/// Returns if the property descriptor is configurable.
///
/// # Panics
///
/// Panics if the `configurable` field is not set.
#[inline]
pub fn expect_configurable(&self) -> bool {
if let Some(configurable) = self.configurable {
configurable
} else {
panic!("[[configurable]] field not in property descriptor")
}
self.configurable
.expect("[[configurable]] field not in property descriptor")
}
/// Returns if the property descriptor is writable.
///
/// # Panics
///
/// Panics if the `writable` field is not set.
#[inline]
pub fn expect_writable(&self) -> bool {
if let Some(writable) = self.writable() {
writable
} else {
panic!("[[writable]] field not in property descriptor")
}
self.writable()
.expect("[[writable]] field not in property descriptor")
}
/// Returns the value of the property descriptor.
///
/// # Panics
///
/// Panics if the `value` field is not set.
#[inline]
pub fn expect_value(&self) -> &JsValue {
if let Some(value) = self.value() {
value
} else {
panic!("[[value]] field not in property descriptor")
}
self.value()
.expect("[[value]] field not in property descriptor")
}
/// Returns the getter of the property descriptor.
///
/// # Panics
///
/// Panics if the `getter` field is not set.
#[inline]
pub fn expect_get(&self) -> &JsValue {
if let Some(get) = self.get() {
get
} else {
panic!("[[get]] field not in property descriptor")
}
self.get()
.expect("[[get]] field not in property descriptor")
}
/// Returns the setter of the property descriptor.
///
/// # Panics
///
/// Panics if the `setter` field is not set.
#[inline]
pub fn expect_set(&self) -> &JsValue {
if let Some(set) = self.set() {
set
} else {
panic!("[[set]] field not in property descriptor")
}
self.set()
.expect("[[set]] field not in property descriptor")
}
/// Returns the kind of the property descriptor.
#[inline]
pub fn kind(&self) -> &DescriptorKind {
pub const fn kind(&self) -> &DescriptorKind {
&self.kind
}
/// Creates a new [`PropertyDescriptorBuilder`].
#[inline]
#[must_use]
pub fn builder() -> PropertyDescriptorBuilder {
PropertyDescriptorBuilder::new()
}
/// Creates an accessor property descriptor with default values.
#[inline]
#[must_use]
pub fn into_accessor_defaulted(mut self) -> Self {
@ -227,6 +269,7 @@ impl PropertyDescriptor {
.build()
}
/// Creates a data property descriptor with default values.
#[must_use]
pub fn into_data_defaulted(mut self) -> Self {
self.kind = DescriptorKind::Data {
@ -238,6 +281,7 @@ impl PropertyDescriptor {
.build()
}
/// Creates an generic property descriptor with default values.
#[inline]
#[must_use]
pub fn complete_property_descriptor(self) -> Self {
@ -246,6 +290,12 @@ impl PropertyDescriptor {
.build()
}
/// Fills the fields of the `PropertyDescriptor` that are not set
/// with fields from the given `PropertyDescriptor`.
///
/// # Panics
///
/// Panics if the given `PropertyDescriptor` is not compatible with this one.
#[inline]
pub fn fill_with(&mut self, desc: &Self) {
match (&mut self.kind, &desc.kind) {
@ -290,16 +340,20 @@ impl PropertyDescriptor {
}
}
/// A builder for [`PropertyDescriptor`].
#[derive(Default, Debug, Clone)]
pub struct PropertyDescriptorBuilder {
inner: PropertyDescriptor,
}
impl PropertyDescriptorBuilder {
/// Creates a new [`PropertyDescriptorBuilder`].
#[must_use]
pub fn new() -> Self {
Self::default()
}
/// Sets the `value` field of the property descriptor.
#[must_use]
pub fn value<V: Into<JsValue>>(mut self, value: V) -> Self {
match self.inner.kind {
@ -317,6 +371,7 @@ impl PropertyDescriptorBuilder {
self
}
/// Sets the `writable` field of the property descriptor.
#[must_use]
pub fn writable(mut self, writable: bool) -> Self {
match self.inner.kind {
@ -335,6 +390,7 @@ impl PropertyDescriptorBuilder {
self
}
/// Sets the `get` field of the property descriptor.
#[must_use]
pub fn get<V: Into<JsValue>>(mut self, get: V) -> Self {
match self.inner.kind {
@ -350,6 +406,7 @@ impl PropertyDescriptorBuilder {
self
}
/// Sets the `set` field of the property descriptor.
#[must_use]
pub fn set<V: Into<JsValue>>(mut self, set: V) -> Self {
match self.inner.kind {
@ -365,22 +422,25 @@ impl PropertyDescriptorBuilder {
self
}
/// Optionally sets the `enumerable` field of the property descriptor.
#[must_use]
pub fn maybe_enumerable(mut self, enumerable: Option<bool>) -> Self {
pub const fn maybe_enumerable(mut self, enumerable: Option<bool>) -> Self {
if let Some(enumerable) = enumerable {
self = self.enumerable(enumerable);
}
self
}
/// Optionally sets the `configurable` field of the property descriptor.
#[must_use]
pub fn maybe_configurable(mut self, configurable: Option<bool>) -> Self {
pub const fn maybe_configurable(mut self, configurable: Option<bool>) -> Self {
if let Some(configurable) = configurable {
self = self.configurable(configurable);
}
self
}
/// Optionally sets the `value` field of the property descriptor.
#[must_use]
pub fn maybe_value<V: Into<JsValue>>(mut self, value: Option<V>) -> Self {
if let Some(value) = value {
@ -389,6 +449,7 @@ impl PropertyDescriptorBuilder {
self
}
/// Optionally sets the `writable` field of the property descriptor.
#[must_use]
pub fn maybe_writable(mut self, writable: Option<bool>) -> Self {
if let Some(writable) = writable {
@ -397,6 +458,7 @@ impl PropertyDescriptorBuilder {
self
}
/// Optionally sets the `get` field of the property descriptor.
#[must_use]
pub fn maybe_get<V: Into<JsValue>>(mut self, get: Option<V>) -> Self {
if let Some(get) = get {
@ -405,6 +467,7 @@ impl PropertyDescriptorBuilder {
self
}
/// Optionally sets the `set` field of the property descriptor.
#[must_use]
pub fn maybe_set<V: Into<JsValue>>(mut self, set: Option<V>) -> Self {
if let Some(set) = set {
@ -413,18 +476,21 @@ impl PropertyDescriptorBuilder {
self
}
/// Sets the `enumerable` field of the property descriptor.
#[must_use]
pub fn enumerable(mut self, enumerable: bool) -> Self {
pub const fn enumerable(mut self, enumerable: bool) -> Self {
self.inner.enumerable = Some(enumerable);
self
}
/// Sets the `configurable` field of the property descriptor.
#[must_use]
pub fn configurable(mut self, configurable: bool) -> Self {
pub const fn configurable(mut self, configurable: bool) -> Self {
self.inner.configurable = Some(configurable);
self
}
/// Fill any missing fields in the property descriptor.
#[must_use]
pub fn complete_with_defaults(mut self) -> Self {
match self.inner.kind {
@ -466,10 +532,13 @@ impl PropertyDescriptorBuilder {
self
}
pub fn inner(&self) -> &PropertyDescriptor {
/// Returns a reference to the currently built [`PropertyDescriptor`].
pub const fn inner(&self) -> &PropertyDescriptor {
&self.inner
}
/// Consumes the builder and returns the [`PropertyDescriptor`].
#[allow(clippy::missing_const_for_fn)]
pub fn build(self) -> PropertyDescriptor {
self.inner
}
@ -490,52 +559,51 @@ impl From<PropertyDescriptorBuilder> for PropertyDescriptor {
/// [spec]: https://tc39.es/ecma262/#sec-ispropertykey
#[derive(PartialEq, Debug, Clone, Eq, Hash)]
pub enum PropertyKey {
/// A string property key.
String(JsString),
/// A symbol property key.
Symbol(JsSymbol),
/// A numeric property key.
Index(u32),
}
impl From<JsString> for PropertyKey {
#[inline]
fn from(string: JsString) -> Self {
if let Some(index) = string.to_std_string().ok().and_then(|s| s.parse().ok()) {
Self::Index(index)
} else {
Self::String(string)
}
string
.to_std_string()
.ok()
.and_then(|s| s.parse().ok())
.map_or(Self::String(string), Self::Index)
}
}
impl From<&str> for PropertyKey {
#[inline]
fn from(string: &str) -> Self {
if let Ok(index) = string.parse() {
Self::Index(index)
} else {
Self::String(string.into())
}
string
.parse()
.map_or_else(|_| Self::String(string.into()), Self::Index)
}
}
impl From<String> for PropertyKey {
#[inline]
fn from(string: String) -> Self {
if let Ok(index) = string.parse() {
Self::Index(index)
} else {
Self::String(string.into())
}
string
.parse()
.map_or_else(|_| Self::String(string.into()), Self::Index)
}
}
impl From<Box<str>> for PropertyKey {
#[inline]
fn from(string: Box<str>) -> Self {
if let Ok(index) = string.parse() {
Self::Index(index)
} else {
Self::String(string.as_ref().into())
}
string
.parse()
.map_or_else(|_| Self::String(string.as_ref().into()), Self::Index)
}
}
@ -564,11 +632,7 @@ impl From<&PropertyKey> for JsValue {
PropertyKey::String(ref string) => string.clone().into(),
PropertyKey::Symbol(ref symbol) => symbol.clone().into(),
PropertyKey::Index(index) => {
if let Ok(integer) = i32::try_from(*index) {
Self::new(integer)
} else {
Self::new(*index)
}
i32::try_from(*index).map_or_else(|_| Self::new(*index), Self::new)
}
}
}
@ -605,51 +669,36 @@ impl From<u32> for PropertyKey {
impl From<usize> for PropertyKey {
fn from(value: usize) -> Self {
if let Ok(index) = u32::try_from(value) {
Self::Index(index)
} else {
Self::String(js_string!(value.to_string()))
}
u32::try_from(value)
.map_or_else(|_| Self::String(js_string!(value.to_string())), Self::Index)
}
}
impl From<i64> for PropertyKey {
fn from(value: i64) -> Self {
if let Ok(index) = u32::try_from(value) {
Self::Index(index)
} else {
Self::String(js_string!(value.to_string()))
}
u32::try_from(value)
.map_or_else(|_| Self::String(js_string!(value.to_string())), Self::Index)
}
}
impl From<u64> for PropertyKey {
fn from(value: u64) -> Self {
if let Ok(index) = u32::try_from(value) {
Self::Index(index)
} else {
Self::String(js_string!(value.to_string()))
}
u32::try_from(value)
.map_or_else(|_| Self::String(js_string!(value.to_string())), Self::Index)
}
}
impl From<isize> for PropertyKey {
fn from(value: isize) -> Self {
if let Ok(index) = u32::try_from(value) {
Self::Index(index)
} else {
Self::String(js_string!(value.to_string()))
}
u32::try_from(value)
.map_or_else(|_| Self::String(js_string!(value.to_string())), Self::Index)
}
}
impl From<i32> for PropertyKey {
fn from(value: i32) -> Self {
if let Ok(index) = u32::try_from(value) {
Self::Index(index)
} else {
Self::String(js_string!(value.to_string()))
}
u32::try_from(value)
.map_or_else(|_| Self::String(js_string!(value.to_string())), Self::Index)
}
}

3
boa_engine/src/realm.rs

@ -25,6 +25,7 @@ pub struct Realm {
}
impl Realm {
/// Create a new Realm.
#[inline]
pub fn create(global_prototype: JsPrototype) -> Self {
let _timer = Profiler::global().start_event("Realm::create", "realm");
@ -46,7 +47,7 @@ impl Realm {
}
#[inline]
pub(crate) fn global_object(&self) -> &JsObject {
pub(crate) const fn global_object(&self) -> &JsObject {
&self.global_object
}

27
boa_engine/src/string/mod.rs

@ -112,13 +112,17 @@ macro_rules! js_string {
/// [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CodePoint {
/// A valid Unicode scalar value.
Unicode(char),
/// An unpaired surrogate.
UnpairedSurrogate(u16),
}
impl CodePoint {
/// Get the number of UTF-16 code units needed to encode this code point.
pub fn code_unit_count(self) -> usize {
#[must_use]
pub const fn code_unit_count(self) -> usize {
match self {
Self::Unicode(c) => c.len_utf16(),
Self::UnpairedSurrogate(_) => 1,
@ -126,6 +130,7 @@ impl CodePoint {
}
/// Convert the code point to its [`u32`] representation.
#[must_use]
pub fn as_u32(self) -> u32 {
match self {
Self::Unicode(c) => u32::from(c),
@ -135,7 +140,8 @@ impl CodePoint {
/// If the code point represents a valid 'Unicode scalar value', returns its [`char`]
/// representation, otherwise returns [`None`] on unpaired surrogates.
pub fn as_char(self) -> Option<char> {
#[must_use]
pub const fn as_char(self) -> Option<char> {
match self {
Self::Unicode(c) => Some(c),
Self::UnpairedSurrogate(_) => None,
@ -151,8 +157,8 @@ impl CodePoint {
/// code point.
pub fn encode_utf16(self, dst: &mut [u16]) -> &mut [u16] {
match self {
CodePoint::Unicode(c) => c.encode_utf16(dst),
CodePoint::UnpairedSurrogate(surr) => {
Self::Unicode(c) => c.encode_utf16(dst),
Self::UnpairedSurrogate(surr) => {
dst[0] = surr;
&mut dst[0..=0]
}
@ -210,7 +216,7 @@ impl TaggedJsString {
/// `inner` must point to a valid instance of [`RawJsString`], which should be deallocated only
/// by [`JsString`].
#[inline]
unsafe fn new_heap(inner: NonNull<RawJsString>) -> Self {
const unsafe fn new_heap(inner: NonNull<RawJsString>) -> Self {
Self(inner)
}
@ -297,17 +303,20 @@ unsafe impl Trace for JsString {
impl JsString {
/// Obtains the underlying [`&[u16]`][slice] slice of a [`JsString`]
#[must_use]
pub fn as_slice(&self) -> &[u16] {
self
}
/// Creates a new [`JsString`] from the concatenation of `x` and `y`.
#[must_use]
pub fn concat(x: &[u16], y: &[u16]) -> Self {
Self::concat_array(&[x, y])
}
/// Creates a new [`JsString`] from the concatenation of every element of
/// `strings`.
#[must_use]
pub fn concat_array(strings: &[&[u16]]) -> Self {
let mut full_count = 0usize;
for &string in strings {
@ -356,6 +365,7 @@ impl JsString {
/// Decodes a [`JsString`] into a [`String`], replacing invalid data with its escaped representation
/// in 4 digit hexadecimal.
#[must_use]
pub fn to_std_string_escaped(&self) -> String {
self.to_string_escaped()
}
@ -500,10 +510,10 @@ impl JsString {
}
// Slow path
let mut value = 0.0;
let mut value: f64 = 0.0;
for c in s {
if let Some(digit) = char::from(c).to_digit(base) {
value = value * f64::from(base) + f64::from(digit);
value = value.mul_add(f64::from(base), f64::from(digit));
} else {
return f64::NAN;
}
@ -577,6 +587,7 @@ impl JsString {
debug_assert_eq!(offset, DATA_OFFSET);
#[allow(clippy::cast_ptr_alignment)]
// SAFETY:
// The layout size of `RawJsString` is never zero, since it has to store
// the length of the string and the reference count.
@ -767,7 +778,7 @@ impl From<&[u16]> for JsString {
impl From<Vec<u16>> for JsString {
fn from(vec: Vec<u16>) -> Self {
JsString::from(&vec[..])
Self::from(&vec[..])
}
}

17
boa_engine/src/symbol.rs

@ -120,6 +120,7 @@ impl WellKnownSymbols {
/// A method that returns the default `AsyncIterator` for an object.
/// Called by the semantics of the `for-await-of` statement.
#[inline]
#[must_use]
pub fn async_iterator() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.async_iterator.clone())
}
@ -130,6 +131,7 @@ impl WellKnownSymbols {
/// recognizes an object as one of the `constructor`'s instances.
/// Called by the semantics of the instanceof operator.
#[inline]
#[must_use]
pub fn has_instance() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.has_instance.clone())
}
@ -140,6 +142,7 @@ impl WellKnownSymbols {
/// an object should be flattened to its array elements
/// by `Array.prototype.concat`.
#[inline]
#[must_use]
pub fn is_concat_spreadable() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.is_concat_spreadable.clone())
}
@ -149,6 +152,7 @@ impl WellKnownSymbols {
/// A method that returns the default Iterator for an object.
/// Called by the semantics of the `for-of` statement.
#[inline]
#[must_use]
pub fn iterator() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.iterator.clone())
}
@ -158,6 +162,7 @@ impl WellKnownSymbols {
/// A regular expression method that matches the regular expression
/// against a string. Called by the `String.prototype.match` method.
#[inline]
#[must_use]
pub fn r#match() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.r#match.clone())
}
@ -168,6 +173,7 @@ impl WellKnownSymbols {
/// matches of the regular expression against a string.
/// Called by the `String.prototype.matchAll` method.
#[inline]
#[must_use]
pub fn match_all() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.match_all.clone())
}
@ -177,6 +183,7 @@ impl WellKnownSymbols {
/// A regular expression method that replaces matched substrings
/// of a string. Called by the `String.prototype.replace` method.
#[inline]
#[must_use]
pub fn replace() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.replace.clone())
}
@ -187,6 +194,7 @@ impl WellKnownSymbols {
/// string that matches the regular expression.
/// Called by the `String.prototype.search` method.
#[inline]
#[must_use]
pub fn search() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.search.clone())
}
@ -196,6 +204,7 @@ impl WellKnownSymbols {
/// A function valued property that is the `constructor` function
/// that is used to create derived objects.
#[inline]
#[must_use]
pub fn species() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.species.clone())
}
@ -206,6 +215,7 @@ impl WellKnownSymbols {
/// that match the regular expression.
/// Called by the `String.prototype.split` method.
#[inline]
#[must_use]
pub fn split() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.split.clone())
}
@ -215,6 +225,7 @@ impl WellKnownSymbols {
/// A method that converts an object to a corresponding primitive value.
/// Called by the `ToPrimitive` (`Value::to_primitive`) abstract operation.
#[inline]
#[must_use]
pub fn to_primitive() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.to_primitive.clone())
}
@ -225,6 +236,7 @@ impl WellKnownSymbols {
/// string description of an object.
/// Accessed by the built-in method `Object.prototype.toString`.
#[inline]
#[must_use]
pub fn to_string_tag() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.to_string_tag.clone())
}
@ -234,6 +246,7 @@ impl WellKnownSymbols {
/// An object valued property whose own and inherited property names are property
/// names that are excluded from the `with` environment bindings of the associated object.
#[inline]
#[must_use]
pub fn unscopables() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.unscopables.clone())
}
@ -261,6 +274,7 @@ unsafe impl Trace for JsSymbol {
impl JsSymbol {
/// Create a new symbol.
#[inline]
#[must_use]
pub fn new(description: Option<JsString>) -> Self {
let hash = SYMBOL_HASH_COUNT.with(|count| {
let hash = count.get();
@ -283,6 +297,7 @@ impl JsSymbol {
/// Returns the `Symbol`s description.
#[inline]
#[must_use]
pub fn description(&self) -> Option<JsString> {
self.inner.description.clone()
}
@ -291,6 +306,7 @@ impl JsSymbol {
///
/// The hash is guaranteed to be unique.
#[inline]
#[must_use]
pub fn hash(&self) -> u64 {
self.inner.hash
}
@ -301,6 +317,7 @@ impl JsSymbol {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-symboldescriptivestring
#[must_use]
pub fn descriptive_string(&self) -> JsString {
self.inner.description.as_ref().map_or_else(
|| js_string!("Symbol()"),

96
boa_engine/src/tests.rs

@ -1070,14 +1070,16 @@ fn to_length() {
assert_eq!(JsValue::new(20.9).to_length(&mut context).unwrap(), 20);
assert_eq!(JsValue::new(-20.9).to_length(&mut context).unwrap(), 0);
assert_eq!(
JsValue::new(100000000000.0)
JsValue::new(100_000_000_000.0)
.to_length(&mut context)
.unwrap(),
100000000000
100_000_000_000
);
assert_eq!(
JsValue::new(4010101101.0).to_length(&mut context).unwrap(),
4010101101
JsValue::new(4_010_101_101.0)
.to_length(&mut context)
.unwrap(),
4_010_101_101
);
}
@ -1112,43 +1114,43 @@ fn to_int32() {
check_to_int32!(-0.6 => 0);
check_to_int32!(-1.6 => -1);
check_to_int32!(2147483647.0 => 2147483647);
check_to_int32!(2147483648.0 => -2147483648);
check_to_int32!(2147483649.0 => -2147483647);
check_to_int32!(4294967295.0 => -1);
check_to_int32!(4294967296.0 => 0);
check_to_int32!(4294967297.0 => 1);
check_to_int32!(-2147483647.0 => -2147483647);
check_to_int32!(-2147483648.0 => -2147483648);
check_to_int32!(-2147483649.0 => 2147483647);
check_to_int32!(-4294967295.0 => 1);
check_to_int32!(-4294967296.0 => 0);
check_to_int32!(-4294967297.0 => -1);
check_to_int32!(2147483648.25 => -2147483648);
check_to_int32!(2147483648.5 => -2147483648);
check_to_int32!(2147483648.75 => -2147483648);
check_to_int32!(4294967295.25 => -1);
check_to_int32!(4294967295.5 => -1);
check_to_int32!(4294967295.75 => -1);
check_to_int32!(3000000000.25 => -1294967296);
check_to_int32!(3000000000.5 => -1294967296);
check_to_int32!(3000000000.75 => -1294967296);
check_to_int32!(-2147483648.25 => -2147483648);
check_to_int32!(-2147483648.5 => -2147483648);
check_to_int32!(-2147483648.75 => -2147483648);
check_to_int32!(-4294967295.25 => 1);
check_to_int32!(-4294967295.5 => 1);
check_to_int32!(-4294967295.75 => 1);
check_to_int32!(-3000000000.25 => 1294967296);
check_to_int32!(-3000000000.5 => 1294967296);
check_to_int32!(-3000000000.75 => 1294967296);
let base = 2f64.powf(64.0);
check_to_int32!(2_147_483_647.0 => 2_147_483_647);
check_to_int32!(2_147_483_648.0 => -2_147_483_648);
check_to_int32!(2_147_483_649.0 => -2_147_483_647);
check_to_int32!(4_294_967_295.0 => -1);
check_to_int32!(4_294_967_296.0 => 0);
check_to_int32!(4_294_967_297.0 => 1);
check_to_int32!(-2_147_483_647.0 => -2_147_483_647);
check_to_int32!(-2_147_483_648.0 => -2_147_483_648);
check_to_int32!(-2_147_483_649.0 => 2_147_483_647);
check_to_int32!(-4_294_967_295.0 => 1);
check_to_int32!(-4_294_967_296.0 => 0);
check_to_int32!(-4_294_967_297.0 => -1);
check_to_int32!(2_147_483_648.25 => -2_147_483_648);
check_to_int32!(2_147_483_648.5 => -2_147_483_648);
check_to_int32!(2_147_483_648.75 => -2_147_483_648);
check_to_int32!(4_294_967_295.25 => -1);
check_to_int32!(4_294_967_295.5 => -1);
check_to_int32!(4_294_967_295.75 => -1);
check_to_int32!(3_000_000_000.25 => -1_294_967_296);
check_to_int32!(3_000_000_000.5 => -1_294_967_296);
check_to_int32!(3_000_000_000.75 => -1_294_967_296);
check_to_int32!(-2_147_483_648.25 => -2_147_483_648);
check_to_int32!(-2_147_483_648.5 => -2_147_483_648);
check_to_int32!(-2_147_483_648.75 => -2_147_483_648);
check_to_int32!(-4_294_967_295.25 => 1);
check_to_int32!(-4_294_967_295.5 => 1);
check_to_int32!(-4_294_967_295.75 => 1);
check_to_int32!(-3_000_000_000.25 => 1_294_967_296);
check_to_int32!(-3_000_000_000.5 => 1_294_967_296);
check_to_int32!(-3_000_000_000.75 => 1_294_967_296);
let base = 2f64.powi(64);
check_to_int32!(base + 0.0 => 0);
check_to_int32!(base + 1117.0 => 0);
check_to_int32!(base + 2234.0 => 4096);
@ -1180,16 +1182,16 @@ fn to_int32() {
check_to_int32!(base + 31276.0 => 32768);
// bignum is (2^53 - 1) * 2^31 - highest number with bit 31 set.
let bignum = 2f64.powf(84.0) - 2f64.powf(31.0);
check_to_int32!(bignum => -2147483648);
check_to_int32!(-bignum => -2147483648);
let bignum = 2f64.powi(84) - 2f64.powi(31);
check_to_int32!(bignum => -2_147_483_648);
check_to_int32!(-bignum => -2_147_483_648);
check_to_int32!(2.0 * bignum => 0);
check_to_int32!(-(2.0 * bignum) => 0);
check_to_int32!(bignum - 2f64.powf(31.0) => 0);
check_to_int32!(-(bignum - 2f64.powf(31.0)) => 0);
check_to_int32!(bignum - 2f64.powi(31) => 0);
check_to_int32!(-(bignum - 2f64.powi(31)) => 0);
// max_fraction is largest number below 1.
let max_fraction = 1.0 - 2f64.powf(-53.0);
let max_fraction = 1.0 - 2f64.powi(-53);
check_to_int32!(max_fraction => 0);
check_to_int32!(-max_fraction => 0);
}

35
boa_engine/src/value/conversions.rs

@ -1,4 +1,4 @@
use super::{Display, JsBigInt, JsObject, JsString, JsSymbol, JsValue, Profiler};
use super::{JsBigInt, JsObject, JsString, JsSymbol, JsValue, Profiler};
impl From<&Self> for JsValue {
#[inline]
@ -33,15 +33,6 @@ impl From<JsSymbol> for JsValue {
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct TryFromCharError;
impl Display for TryFromCharError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Could not convert value to a char type")
}
}
impl From<f32> for JsValue {
#[allow(clippy::float_cmp)]
#[inline]
@ -97,11 +88,7 @@ impl From<i16> for JsValue {
impl From<u32> for JsValue {
#[inline]
fn from(value: u32) -> Self {
if let Ok(integer) = i32::try_from(value) {
Self::Integer(integer)
} else {
Self::Rational(value.into())
}
i32::try_from(value).map_or_else(|_| Self::Rational(value.into()), Self::Integer)
}
}
@ -122,33 +109,21 @@ impl From<JsBigInt> for JsValue {
impl From<usize> for JsValue {
#[inline]
fn from(value: usize) -> Self {
if let Ok(value) = i32::try_from(value) {
Self::Integer(value)
} else {
Self::Rational(value as f64)
}
i32::try_from(value).map_or(Self::Rational(value as f64), Self::Integer)
}
}
impl From<u64> for JsValue {
#[inline]
fn from(value: u64) -> Self {
if let Ok(value) = i32::try_from(value) {
Self::Integer(value)
} else {
Self::Rational(value as f64)
}
i32::try_from(value).map_or(Self::Rational(value as f64), Self::Integer)
}
}
impl From<i64> for JsValue {
#[inline]
fn from(value: i64) -> Self {
if let Ok(value) = i32::try_from(value) {
Self::Integer(value)
} else {
Self::Rational(value as f64)
}
i32::try_from(value).map_or(Self::Rational(value as f64), Self::Integer)
}
}

2
boa_engine/src/value/display.rs

@ -20,7 +20,7 @@ impl ValueDisplay<'_> {
/// By default this is `false`.
#[inline]
#[must_use]
pub fn internals(mut self, yes: bool) -> Self {
pub const fn internals(mut self, yes: bool) -> Self {
self.internals = yes;
self
}

28
boa_engine/src/value/equality.rs

@ -64,16 +64,14 @@ impl JsValue {
// a. Let n be ! StringToBigInt(y).
// b. If n is NaN, return false.
// c. Return the result of the comparison x == n.
(Self::BigInt(ref a), Self::String(ref b)) => match b.to_big_int() {
Some(ref b) => a == b,
None => false,
},
(Self::BigInt(ref a), Self::String(ref b)) => {
b.to_big_int().as_ref().map_or(false, |b| a == b)
}
// 7. If Type(x) is String and Type(y) is BigInt, return the result of the comparison y == x.
(Self::String(ref a), Self::BigInt(ref b)) => match a.to_big_int() {
Some(ref a) => a == b,
None => false,
},
(Self::String(ref a), Self::BigInt(ref b)) => {
a.to_big_int().as_ref().map_or(false, |a| a == b)
}
// 8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y.
(Self::Boolean(x), _) => return other.equals(&Self::new(i32::from(*x)), context),
@ -170,16 +168,12 @@ impl JsValue {
match (x, y) {
// 2. If Type(x) is Number or BigInt, then
// a. Return ! Type(x)::SameValueZero(x, y).
(JsValue::BigInt(x), JsValue::BigInt(y)) => JsBigInt::same_value_zero(x, y),
(Self::BigInt(x), Self::BigInt(y)) => JsBigInt::same_value_zero(x, y),
(JsValue::Rational(x), JsValue::Rational(y)) => Number::same_value_zero(*x, *y),
(JsValue::Rational(x), JsValue::Integer(y)) => {
Number::same_value_zero(*x, f64::from(*y))
}
(JsValue::Integer(x), JsValue::Rational(y)) => {
Number::same_value_zero(f64::from(*x), *y)
}
(JsValue::Integer(x), JsValue::Integer(y)) => x == y,
(Self::Rational(x), Self::Rational(y)) => Number::same_value_zero(*x, *y),
(Self::Rational(x), Self::Integer(y)) => Number::same_value_zero(*x, f64::from(*y)),
(Self::Integer(x), Self::Rational(y)) => Number::same_value_zero(f64::from(*x), *y),
(Self::Integer(x), Self::Integer(y)) => x == y,
// 3. Return ! SameValueNonNumeric(x, y).
(_, _) => Self::same_value_non_numeric(x, y),

15
boa_engine/src/value/integer.rs

@ -3,14 +3,24 @@ use std::cmp::Ordering;
/// Represents the result of the `ToIntegerOrInfinity` operation
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum IntegerOrInfinity {
/// Positive infinity.
PositiveInfinity,
/// An integer.
Integer(i64),
/// Negative infinity.
NegativeInfinity,
}
impl IntegerOrInfinity {
/// Clamps an `IntegerOrInfinity` between two `i64`, effectively converting
/// it to an i64.
///
/// # Panics
///
/// Panics if `min > max`.
#[must_use]
pub fn clamp_finite(self, min: i64, max: i64) -> i64 {
assert!(min <= max);
@ -22,7 +32,8 @@ impl IntegerOrInfinity {
}
/// Gets the wrapped `i64` if the variant is an `Integer`.
pub fn as_integer(self) -> Option<i64> {
#[must_use]
pub const fn as_integer(self) -> Option<i64> {
match self {
Self::Integer(i) => Some(i),
_ => None,
@ -100,7 +111,7 @@ pub(crate) enum IntegerOrNan {
impl IntegerOrNan {
/// Gets the wrapped `i64` if the variant is an `Integer`.
pub(crate) fn as_integer(self) -> Option<i64> {
pub(crate) const fn as_integer(self) -> Option<i64> {
match self {
Self::Integer(i) => Some(i),
Self::Nan => None,

115
boa_engine/src/value/mod.rs

@ -2,6 +2,16 @@
//!
//! Javascript values, utility methods and conversion between Javascript values and Rust values.
mod conversions;
mod equality;
mod hash;
mod integer;
mod operations;
mod serde_json;
mod r#type;
pub(crate) mod display;
#[cfg(test)]
mod tests;
@ -29,16 +39,8 @@ use std::{
ops::Sub,
};
mod conversions;
pub(crate) mod display;
mod equality;
mod hash;
mod integer;
mod operations;
mod serde_json;
mod r#type;
pub(crate) use conversions::*;
pub use conversions::*;
pub use display::ValueDisplay;
pub use integer::IntegerOrInfinity;
pub use operations::*;
@ -99,42 +101,48 @@ impl JsValue {
/// Creates a new `undefined` value.
#[inline]
pub fn undefined() -> Self {
#[must_use]
pub const fn undefined() -> Self {
Self::Undefined
}
/// Creates a new `null` value.
#[inline]
pub fn null() -> Self {
#[must_use]
pub const fn null() -> Self {
Self::Null
}
/// Creates a new number with `NaN` value.
#[inline]
pub fn nan() -> Self {
#[must_use]
pub const fn nan() -> Self {
Self::Rational(f64::NAN)
}
/// Creates a new number with `Infinity` value.
#[inline]
pub fn positive_infinity() -> Self {
#[must_use]
pub const fn positive_infinity() -> Self {
Self::Rational(f64::INFINITY)
}
/// Creates a new number with `-Infinity` value.
#[inline]
pub fn negative_infinity() -> Self {
#[must_use]
pub const fn negative_infinity() -> Self {
Self::Rational(f64::NEG_INFINITY)
}
/// Returns true if the value is an object
/// Returns true if the value is an object.
#[inline]
pub fn is_object(&self) -> bool {
pub const fn is_object(&self) -> bool {
matches!(self, Self::Object(_))
}
/// Returns the object if the value is object, otherwise `None`.
#[inline]
pub fn as_object(&self) -> Option<&JsObject> {
pub const fn as_object(&self) -> Option<&JsObject> {
match *self {
Self::Object(ref o) => Some(o),
_ => None,
@ -152,6 +160,7 @@ impl JsValue {
matches!(self, Self::Object(obj) if obj.is_callable())
}
/// Returns the callable value if the value is callable, otherwise `None`.
#[inline]
pub fn as_callable(&self) -> Option<&JsObject> {
self.as_object().filter(|obj| obj.is_callable())
@ -163,6 +172,7 @@ impl JsValue {
matches!(self, Self::Object(obj) if obj.is_constructor())
}
/// Returns the constructor if the value is a constructor, otherwise `None`.
#[inline]
pub fn as_constructor(&self) -> Option<&JsObject> {
self.as_object().filter(|obj| obj.is_constructor())
@ -174,6 +184,7 @@ impl JsValue {
matches!(self, Self::Object(obj) if obj.is_promise())
}
/// Returns the promise if the value is a promise, otherwise `None`.
#[inline]
pub fn as_promise(&self) -> Option<&JsObject> {
self.as_object().filter(|obj| obj.is_promise())
@ -181,10 +192,11 @@ impl JsValue {
/// Returns true if the value is a symbol.
#[inline]
pub fn is_symbol(&self) -> bool {
pub const fn is_symbol(&self) -> bool {
matches!(self, Self::Symbol(_))
}
/// Returns the symbol if the value is a symbol, otherwise `None`.
pub fn as_symbol(&self) -> Option<JsSymbol> {
match self {
Self::Symbol(symbol) => Some(symbol.clone()),
@ -194,25 +206,25 @@ impl JsValue {
/// Returns true if the value is undefined.
#[inline]
pub fn is_undefined(&self) -> bool {
pub const fn is_undefined(&self) -> bool {
matches!(self, Self::Undefined)
}
/// Returns true if the value is null.
#[inline]
pub fn is_null(&self) -> bool {
pub const fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
/// Returns true if the value is null or undefined.
#[inline]
pub fn is_null_or_undefined(&self) -> bool {
pub const fn is_null_or_undefined(&self) -> bool {
matches!(self, Self::Null | Self::Undefined)
}
/// Returns true if the value is a 64-bit floating-point number.
#[inline]
pub fn is_double(&self) -> bool {
pub const fn is_double(&self) -> bool {
matches!(self, Self::Rational(_))
}
@ -233,10 +245,11 @@ impl JsValue {
/// Returns true if the value is a number.
#[inline]
pub fn is_number(&self) -> bool {
pub const fn is_number(&self) -> bool {
matches!(self, Self::Rational(_) | Self::Integer(_))
}
/// Returns the number if the value is a number, otherwise `None`.
#[inline]
pub fn as_number(&self) -> Option<f64> {
match *self {
@ -248,13 +261,13 @@ impl JsValue {
/// Returns true if the value is a string.
#[inline]
pub fn is_string(&self) -> bool {
pub const fn is_string(&self) -> bool {
matches!(self, Self::String(_))
}
/// Returns the string if the values is a string, otherwise `None`.
/// Returns the string if the value is a string, otherwise `None`.
#[inline]
pub fn as_string(&self) -> Option<&JsString> {
pub const fn as_string(&self) -> Option<&JsString> {
match self {
Self::String(ref string) => Some(string),
_ => None,
@ -263,12 +276,13 @@ impl JsValue {
/// Returns true if the value is a boolean.
#[inline]
pub fn is_boolean(&self) -> bool {
pub const fn is_boolean(&self) -> bool {
matches!(self, Self::Boolean(_))
}
/// Returns the boolean if the value is a boolean, otherwise `None`.
#[inline]
pub fn as_boolean(&self) -> Option<bool> {
pub const fn as_boolean(&self) -> Option<bool> {
match self {
Self::Boolean(boolean) => Some(*boolean),
_ => None,
@ -277,13 +291,13 @@ impl JsValue {
/// Returns true if the value is a bigint.
#[inline]
pub fn is_bigint(&self) -> bool {
pub const fn is_bigint(&self) -> bool {
matches!(self, Self::BigInt(_))
}
/// Returns an optional reference to a `BigInt` if the value is a `BigInt` primitive.
#[inline]
pub fn as_bigint(&self) -> Option<&JsBigInt> {
pub const fn as_bigint(&self) -> Option<&JsBigInt> {
match self {
Self::BigInt(bigint) => Some(bigint),
_ => None,
@ -404,18 +418,17 @@ impl JsValue {
Self::Undefined => Err(JsNativeError::typ()
.with_message("cannot convert undefined to a BigInt")
.into()),
Self::String(ref string) => {
if let Some(value) = string.to_big_int() {
Ok(value)
} else {
Self::String(ref string) => string.to_big_int().map_or_else(
|| {
Err(JsNativeError::syntax()
.with_message(format!(
"cannot convert string '{}' to bigint primitive",
string.to_std_string_escaped()
))
.into())
}
}
},
Ok,
),
Self::Boolean(true) => Ok(JsBigInt::one()),
Self::Boolean(false) => Ok(JsBigInt::zero()),
Self::Integer(_) | Self::Rational(_) => Err(JsNativeError::typ()
@ -447,7 +460,7 @@ impl JsValue {
/// println!("{}", value.display());
/// ```
#[inline]
pub fn display(&self) -> ValueDisplay<'_> {
pub const fn display(&self) -> ValueDisplay<'_> {
ValueDisplay {
value: self,
internals: false,
@ -588,8 +601,10 @@ impl JsValue {
/// See: <https://tc39.es/ecma262/#sec-touint32>
pub fn to_u32(&self, context: &mut Context) -> JsResult<u32> {
// This is the fast path, if the value is Integer we can just return it.
if let JsValue::Integer(number) = *self {
return Ok(number as u32);
if let Self::Integer(number) = *self {
if let Ok(number) = u32::try_from(number) {
return Ok(number);
}
}
let number = self.to_number(context)?;
@ -601,7 +616,7 @@ impl JsValue {
/// See: <https://tc39.es/ecma262/#sec-toint32>
pub fn to_i32(&self, context: &mut Context) -> JsResult<i32> {
// This is the fast path, if the value is Integer we can just return it.
if let JsValue::Integer(number) = *self {
if let Self::Integer(number) = *self {
return Ok(number);
}
let number = self.to_number(context)?;
@ -940,6 +955,12 @@ impl JsValue {
}
}
/// The abstract operation `ToPropertyDescriptor`.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-topropertydescriptor
#[inline]
pub fn to_property_descriptor(&self, context: &mut Context) -> JsResult<PropertyDescriptor> {
// 1. If Type(Obj) is not Object, throw a TypeError exception.
@ -991,13 +1012,8 @@ impl JsValue {
// The main part of the function is implemented for JsObject.
// 1. If Type(argument) is not Object, return false.
if let Some(object) = self.as_object() {
object.is_array_abstract()
}
// 4. Return false.
else {
Ok(false)
}
self.as_object()
.map_or(Ok(false), JsObject::is_array_abstract)
}
}
@ -1010,8 +1026,13 @@ impl Default for JsValue {
/// The preferred type to convert an object to a primitive `Value`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PreferredType {
/// Prefer to convert to a `String` primitive.
String,
/// Prefer to convert to a `Number` primitive.
Number,
/// Do not prefer a type to convert to.
Default,
}

44
boa_engine/src/value/operations.rs

@ -10,6 +10,7 @@ use crate::{
};
impl JsValue {
/// Perform the binary `+` operator on the value and return the result.
#[inline]
pub fn add(&self, other: &Self, context: &mut Context) -> JsResult<Self> {
Ok(match (self, other) {
@ -50,6 +51,7 @@ impl JsValue {
})
}
/// Perform the binary `-` operator on the value and return the result.
#[inline]
pub fn sub(&self, other: &Self, context: &mut Context) -> JsResult<Self> {
Ok(match (self, other) {
@ -76,6 +78,7 @@ impl JsValue {
})
}
/// Perform the binary `*` operator on the value and return the result.
#[inline]
pub fn mul(&self, other: &Self, context: &mut Context) -> JsResult<Self> {
Ok(match (self, other) {
@ -102,6 +105,7 @@ impl JsValue {
})
}
/// Perform the binary `/` operator on the value and return the result.
#[inline]
pub fn div(&self, other: &Self, context: &mut Context) -> JsResult<Self> {
Ok(match (self, other) {
@ -143,6 +147,7 @@ impl JsValue {
})
}
/// Perform the binary `%` operator on the value and return the result.
#[inline]
pub fn rem(&self, other: &Self, context: &mut Context) -> JsResult<Self> {
Ok(match (self, other) {
@ -194,6 +199,7 @@ impl JsValue {
})
}
/// Perform the binary `**` operator on the value and return the result.
#[inline]
pub fn pow(&self, other: &Self, context: &mut Context) -> JsResult<Self> {
Ok(match (self, other) {
@ -221,6 +227,7 @@ impl JsValue {
})
}
/// Perform the binary `&` operator on the value and return the result.
#[inline]
pub fn bitand(&self, other: &Self, context: &mut Context) -> JsResult<Self> {
Ok(match (self, other) {
@ -251,6 +258,7 @@ impl JsValue {
})
}
/// Perform the binary `|` operator on the value and return the result.
#[inline]
pub fn bitor(&self, other: &Self, context: &mut Context) -> JsResult<Self> {
Ok(match (self, other) {
@ -281,6 +289,7 @@ impl JsValue {
})
}
/// Perform the binary `^` operator on the value and return the result.
#[inline]
pub fn bitxor(&self, other: &Self, context: &mut Context) -> JsResult<Self> {
Ok(match (self, other) {
@ -311,6 +320,7 @@ impl JsValue {
})
}
/// Perform the binary `<<` operator on the value and return the result.
#[inline]
pub fn shl(&self, other: &Self, context: &mut Context) -> JsResult<Self> {
Ok(match (self, other) {
@ -343,6 +353,7 @@ impl JsValue {
})
}
/// Perform the binary `>>` operator on the value and return the result.
#[inline]
pub fn shr(&self, other: &Self, context: &mut Context) -> JsResult<Self> {
Ok(match (self, other) {
@ -375,6 +386,7 @@ impl JsValue {
})
}
/// Perform the binary `>>>` operator on the value and return the result.
#[inline]
pub fn ushr(&self, other: &Self, context: &mut Context) -> JsResult<Self> {
Ok(match (self, other) {
@ -449,14 +461,15 @@ impl JsValue {
}
}
/// Returns the negated value.
#[inline]
pub fn neg(&self, context: &mut Context) -> JsResult<Self> {
Ok(match *self {
Self::Symbol(_) | Self::Undefined => Self::new(f64::NAN),
Self::Object(_) => Self::new(match self.to_numeric_number(context) {
Ok(num) => -num,
Err(_) => f64::NAN,
}),
Self::Object(_) => Self::new(
self.to_numeric_number(context)
.map_or(f64::NAN, std::ops::Neg::neg),
),
Self::String(ref str) => Self::new(-str.to_number()),
Self::Rational(num) => Self::new(-num),
Self::Integer(num) if num == 0 => Self::new(-f64::from(0)),
@ -467,8 +480,9 @@ impl JsValue {
})
}
/// Returns the negated boolean value.
#[inline]
pub fn not(&self, _: &mut Context) -> JsResult<bool> {
pub fn not(&self) -> JsResult<bool> {
Ok(!self.to_boolean())
}
@ -518,20 +532,12 @@ impl JsValue {
match (px, py) {
(Self::String(ref x), Self::String(ref y)) => (x < y).into(),
(Self::BigInt(ref x), Self::String(ref y)) => {
if let Some(y) = y.to_big_int() {
(*x < y).into()
} else {
AbstractRelation::Undefined
}
}
(Self::String(ref x), Self::BigInt(ref y)) => {
if let Some(x) = x.to_big_int() {
(x < *y).into()
} else {
AbstractRelation::Undefined
}
}
(Self::BigInt(ref x), Self::String(ref y)) => y
.to_big_int()
.map_or(AbstractRelation::Undefined, |y| (*x < y).into()),
(Self::String(ref x), Self::BigInt(ref y)) => x
.to_big_int()
.map_or(AbstractRelation::Undefined, |x| (x < *y).into()),
(px, py) => match (px.to_numeric(context)?, py.to_numeric(context)?) {
(Numeric::Number(x), Numeric::Number(y)) => Number::less_than(x, y),
(Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => (x < y).into(),

6
boa_engine/src/value/serde_json.rs

@ -104,6 +104,10 @@ impl JsValue {
/// #
/// # assert_eq!(json, back_to_json);
/// ```
///
/// # Panics
///
/// Panics if the `JsValue` is `Undefined`.
pub fn to_json(&self, context: &mut Context) -> JsResult<Value> {
match self {
Self::Null => Ok(Value::Null),
@ -279,6 +283,6 @@ mod tests {
let pow: u32 = serde_json::from_value(pow.to_json(&mut context).unwrap()).unwrap();
assert_eq!(pow, 60466176);
assert_eq!(pow, 60_466_176);
}
}

19
boa_engine/src/value/tests.rs

@ -208,27 +208,30 @@ fn to_string() {
assert_eq!(f64_to_str(f64::INFINITY), "Infinity");
assert_eq!(f64_to_str(f64::NEG_INFINITY), "-Infinity");
assert_eq!(f64_to_str(90.12), "90.12");
assert_eq!(f64_to_str(111111111111111111111.0), "111111111111111110000");
assert_eq!(
f64_to_str(1111111111111111111111.0),
f64_to_str(111_111_111_111_111_111_111.0),
"111111111111111110000"
);
assert_eq!(
f64_to_str(1_111_111_111_111_111_111_111.0),
"1.1111111111111111e+21"
);
assert_eq!(f64_to_str(-90.12), "-90.12");
assert_eq!(
f64_to_str(-111111111111111111111.0),
f64_to_str(-111_111_111_111_111_111_111.0),
"-111111111111111110000"
);
assert_eq!(
f64_to_str(-1111111111111111111111.0),
f64_to_str(-1_111_111_111_111_111_111_111.0),
"-1.1111111111111111e+21"
);
assert_eq!(f64_to_str(0.0000001), "1e-7");
assert_eq!(f64_to_str(0.000001), "0.000001");
assert_eq!(f64_to_str(0.0000002), "2e-7");
assert_eq!(f64_to_str(-0.0000001), "-1e-7");
assert_eq!(f64_to_str(0.000_000_1), "1e-7");
assert_eq!(f64_to_str(0.000_001), "0.000001");
assert_eq!(f64_to_str(0.000_000_2), "2e-7");
assert_eq!(f64_to_str(-0.000_000_1), "-1e-7");
assert_eq!(f64_to_str(3e50), "3e+50");
}

17
boa_engine/src/value/type.rs

@ -3,13 +3,28 @@ use super::JsValue;
/// Possible types of values as defined at <https://tc39.es/ecma262/#sec-typeof-operator>.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Type {
/// The "undefined" type.
Undefined,
/// The "null" type.
Null,
/// The "boolean" type.
Boolean,
/// The "number" type.
Number,
/// The "string" type.
String,
/// The "symbol" type.
Symbol,
/// The "bigint" type.
BigInt,
/// The "object" type.
Object,
}
@ -20,7 +35,7 @@ impl JsValue {
/// <https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-ecmascript-language-types>.
///
/// Check [`JsValue::type_of`] if you need to call the `typeof` operator.
pub fn get_type(&self) -> Type {
pub const fn get_type(&self) -> Type {
match *self {
Self::Rational(_) | Self::Integer(_) => Type::Number,
Self::String(_) => Type::String,

1
boa_engine/src/vm/call_frame.rs

@ -5,6 +5,7 @@
use crate::{object::JsObject, vm::CodeBlock};
use boa_gc::{Finalize, Gc, Trace};
/// A `CallFrame` holds the state of a function call.
#[derive(Clone, Debug, Finalize, Trace)]
pub struct CallFrame {
pub(crate) code: Gc<CodeBlock>,

40
boa_engine/src/vm/code_block.rs

@ -117,6 +117,7 @@ pub struct CodeBlock {
impl CodeBlock {
/// Constructs a new `CodeBlock`.
#[must_use]
pub fn new(name: Sym, length: u32, strict: bool) -> Self {
Self {
code: Vec::new(),
@ -147,9 +148,12 @@ impl CodeBlock {
where
T: Readable,
{
// Safety:
// The function caller must ensure that the read is in bounds.
//
// This has to be an unaligned read because we can't guarantee that
// the types are aligned.
self.code.as_ptr().add(offset).cast::<T>().read_unaligned()
unsafe { self.code.as_ptr().add(offset).cast::<T>().read_unaligned() }
}
/// Read type T from code.
@ -1085,15 +1089,14 @@ impl JsObject {
std::mem::swap(&mut environments, &mut context.realm.environments);
std::mem::swap(&mut context.vm.stack, &mut stack);
let prototype = if let Some(prototype) = this_function_object
let prototype = this_function_object
.get("prototype", context)
.expect("GeneratorFunction must have a prototype property")
.as_object()
{
prototype.clone()
} else {
context.intrinsics().constructors().generator().prototype()
};
.map_or_else(
|| context.intrinsics().constructors().generator().prototype(),
Clone::clone,
);
let generator = Self::from_proto_and_data(
prototype,
@ -1224,19 +1227,20 @@ impl JsObject {
std::mem::swap(&mut environments, &mut context.realm.environments);
std::mem::swap(&mut context.vm.stack, &mut stack);
let prototype = if let Some(prototype) = this_function_object
let prototype = this_function_object
.get("prototype", context)
.expect("AsyncGeneratorFunction must have a prototype property")
.as_object()
{
prototype.clone()
} else {
context
.intrinsics()
.constructors()
.async_generator()
.prototype()
};
.map_or_else(
|| {
context
.intrinsics()
.constructors()
.async_generator()
.prototype()
},
Clone::clone,
);
let generator = Self::from_proto_and_data(
prototype,
@ -1272,7 +1276,7 @@ impl JsObject {
args: &[JsValue],
this_target: &JsValue,
context: &mut Context,
) -> JsResult<JsObject> {
) -> JsResult<Self> {
let this_function_object = self.clone();
let create_this = |context| {

14
boa_engine/src/vm/flowgraph/color.rs

@ -16,13 +16,21 @@ pub enum Color {
/// Represents the color purple.
Purple,
/// Represents a RGB color.
Rgb { r: u8, g: u8, b: u8 },
Rgb {
/// Red.
r: u8,
/// Green.
g: u8,
/// Blue.
b: u8,
},
}
impl Color {
/// Function for converting HSV to RGB color format.
#[allow(clippy::many_single_char_names)]
#[inline]
#[must_use]
pub fn hsv_to_rgb(h: f64, s: f64, v: f64) -> Self {
let h_i = (h * 6.0) as i64;
let f = h * 6.0 - h_i as f64;
@ -50,8 +58,9 @@ impl Color {
/// This funcition takes a random value and converts it to
/// a pleasant to look at RGB color.
#[inline]
#[must_use]
pub fn from_random_number(mut random: f64) -> Self {
const GOLDEN_RATIO_CONJUGATE: f64 = 0.618033988749895;
const GOLDEN_RATIO_CONJUGATE: f64 = 0.618_033_988_749_895;
random += GOLDEN_RATIO_CONJUGATE;
random %= 1.0;
@ -60,6 +69,7 @@ impl Color {
/// Check if the color is [`Self::None`].
#[inline]
#[must_use]
pub fn is_none(&self) -> bool {
*self == Self::None
}

2
boa_engine/src/vm/flowgraph/edge.rs

@ -40,7 +40,7 @@ pub struct Edge {
impl Edge {
/// Construct a new edge.
#[inline]
pub(super) fn new(
pub(super) const fn new(
from: usize,
to: usize,
label: Option<Box<str>>,

10
boa_engine/src/vm/flowgraph/graph.rs

@ -3,9 +3,16 @@ use crate::vm::flowgraph::{Color, Edge, EdgeStyle, EdgeType, Node, NodeShape};
/// This represents the direction of flow in the flowgraph.
#[derive(Debug, Clone, Copy)]
pub enum Direction {
/// Represents a top to bottom direction.
TopToBottom,
/// Represents a bottom to top direction.
BottomToTop,
/// Represents a left to right direction.
LeftToRight,
/// Represents a right to left direction.
RightToLeft,
}
@ -241,6 +248,7 @@ pub struct Graph {
impl Graph {
/// Construct a [`Graph`]
#[inline]
#[must_use]
pub fn new(direction: Direction) -> Self {
Graph {
subgraphs: Vec::default(),
@ -262,6 +270,7 @@ impl Graph {
/// Output the graph into the graphviz format.
#[inline]
#[must_use]
pub fn to_graphviz_format(&self) -> String {
let mut result = String::new();
result += "digraph {\n";
@ -284,6 +293,7 @@ impl Graph {
/// Output the graph into the mermaid format.
#[inline]
#[must_use]
pub fn to_mermaid_format(&self) -> String {
let mut result = String::new();
let rankdir = match self.direction {

15
boa_engine/src/vm/flowgraph/mod.rs

@ -1,10 +1,8 @@
//! This module is responsible for generating the vm instruction flowgraph.
use std::mem::size_of;
use boa_interner::{Interner, Sym};
use crate::vm::{CodeBlock, Opcode};
use boa_interner::{Interner, Sym};
use std::mem::size_of;
mod color;
mod edge;
@ -20,11 +18,12 @@ impl CodeBlock {
/// Output the [`CodeBlock`] VM instructions into a [`Graph`].
#[inline]
pub fn to_graph(&self, interner: &Interner, graph: &mut SubGraph) {
let mut name = interner.resolve_expect(self.name).to_string();
// Have to remove any invalid graph chars like `<` or `>`.
if self.name == Sym::MAIN {
name = "__main__".to_string();
}
let name = if self.name == Sym::MAIN {
"__main__".to_string()
} else {
interner.resolve_expect(self.name).to_string()
};
graph.set_label(name);

2
boa_engine/src/vm/flowgraph/node.rs

@ -3,7 +3,7 @@ use crate::vm::flowgraph::Color;
/// Reperesents the shape of a node in the flowgraph.
#[derive(Debug, Clone, Copy)]
pub enum NodeShape {
// Represents the default shape used in the graph.
/// Represents the default shape used in the graph.
None,
/// Represents a rectangular node shape.
Record,

14
boa_engine/src/vm/mod.rs

@ -216,17 +216,11 @@ impl Context {
format!("{}μs", duration.as_micros()),
opcode.as_str(),
match self.vm.stack.last() {
Some(value) if value.is_callable() => "[function]".to_string(),
Some(value) if value.is_object() => "[object]".to_string(),
Some(value) => value.display().to_string(),
None => "<empty>".to_string(),
Some(value) => {
if value.is_callable() {
"[function]".to_string()
} else if value.is_object() {
"[object]".to_string()
} else {
value.display().to_string()
}
}
},
}
);
result

15
boa_engine/src/vm/opcode/mod.rs

@ -103,6 +103,7 @@ macro_rules! generate_impl {
$(,)?
}
) => {
/// The opcodes of the vm.
$(#[$outer])*
pub enum $Type {
$(
@ -118,11 +119,16 @@ macro_rules! generate_impl {
/// # Safety
///
/// Does not check if `u8` type is a valid `Opcode`.
#[must_use]
pub unsafe fn from_raw(value: u8) -> Self {
std::mem::transmute(value)
// Safety:
// The caller is responsible for ensuring that the value is a valid opcode.
unsafe { std::mem::transmute(value) }
}
pub fn as_str(self) -> &'static str {
/// Name of this opcode.
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
$(
Self::$Variant => $Variant::NAME
@ -130,8 +136,9 @@ macro_rules! generate_impl {
}
}
/// Name of the profiler event for this opcode
pub fn as_instruction_str(self) -> &'static str {
/// Name of the profiler event for this opcode.
#[must_use]
pub const fn as_instruction_str(self) -> &'static str {
match self {
$(
Self::$Variant => $Variant::INSTRUCTION

Loading…
Cancel
Save