Browse Source

Implement new `get_or_undefined` method for `[JsValue]` (#1492)

* Create `JsArgs` trait

* Implement trait `JsArgs` for `[JsValue]`

* Replace calls to `get/unwrap_or` with `get_or_undefined`
pull/1563/head
jedel1043 3 years ago committed by GitHub
parent
commit
2a8d343fdd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 48
      boa/src/builtins/array/mod.rs
  2. 16
      boa/src/builtins/bigint/mod.rs
  3. 7
      boa/src/builtins/console/mod.rs
  4. 4
      boa/src/builtins/date/mod.rs
  5. 14
      boa/src/builtins/function/mod.rs
  6. 8
      boa/src/builtins/json/mod.rs
  7. 27
      boa/src/builtins/map/mod.rs
  8. 22
      boa/src/builtins/mod.rs
  9. 10
      boa/src/builtins/number/mod.rs
  10. 45
      boa/src/builtins/object/mod.rs
  11. 27
      boa/src/builtins/reflect/mod.rs
  12. 18
      boa/src/builtins/regexp/mod.rs
  13. 29
      boa/src/builtins/set/mod.rs
  14. 115
      boa/src/builtins/string/mod.rs
  15. 4
      boa/src/builtins/symbol/mod.rs
  16. 3
      boa/src/class.rs

48
boa/src/builtins/array/mod.rs

@ -25,6 +25,8 @@ use crate::{
};
use std::cmp::{max, min, Ordering};
use super::JsArgs;
/// JavaScript `Array` built-in implementation.
#[derive(Debug, Clone, Copy)]
pub(crate) struct Array;
@ -689,8 +691,8 @@ impl Array {
// i. Let kValue be ? Get(O, Pk).
let k_value = o.get(pk, context)?;
// ii. Perform ? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »).
let this_arg = args.get(1).cloned().unwrap_or_else(JsValue::undefined);
callback.call(&this_arg, &[k_value, k.into(), o.clone().into()], context)?;
let this_arg = args.get_or_undefined(1);
callback.call(this_arg, &[k_value, k.into(), o.clone().into()], context)?;
}
// d. Set k to k + 1.
}
@ -1024,7 +1026,7 @@ impl Array {
return context.throw_type_error("Array.prototype.every: callback is not callable");
};
let this_arg = args.get(1).cloned().unwrap_or_default();
let this_arg = args.get_or_undefined(1);
// 4. Let k be 0.
// 5. Repeat, while k < len,
@ -1038,7 +1040,7 @@ impl Array {
let k_value = o.get(k, context)?;
// ii. Let testResult be ! ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)).
let test_result = callback
.call(&this_arg, &[k_value, k.into(), o.clone().into()], context)?
.call(this_arg, &[k_value, k.into(), o.clone().into()], context)?
.to_boolean();
// iii. If testResult is false, return false.
if !test_result {
@ -1072,7 +1074,7 @@ impl Array {
// 2. Let len be ? LengthOfArrayLike(O).
let len = o.length_of_array_like(context)?;
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
let callback = args.get(0).cloned().unwrap_or_default();
let callback = args.get_or_undefined(0);
if !callback.is_function() {
return context.throw_type_error("Array.prototype.map: Callbackfn is not callable");
}
@ -1080,7 +1082,7 @@ impl Array {
// 4. Let A be ? ArraySpeciesCreate(O, len).
let a = Self::array_species_create(&o, len, context)?;
let this_arg = args.get(1).cloned().unwrap_or_default();
let this_arg = args.get_or_undefined(1);
// 5. Let k be 0.
// 6. Repeat, while k < len,
@ -1094,7 +1096,7 @@ impl Array {
let k_value = o.get(k, context)?;
// ii. Let mappedValue be ? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »).
let mapped_value =
context.call(&callback, &this_arg, &[k_value, k.into(), this.into()])?;
context.call(callback, this_arg, &[k_value, k.into(), this.into()])?;
// iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue).
a.create_data_property_or_throw(k, mapped_value, context)?;
}
@ -1158,7 +1160,7 @@ impl Array {
}
};
let search_element = args.get(0).cloned().unwrap_or_default();
let search_element = args.get_or_undefined(0);
// 10. Repeat, while k < len,
while k < len {
@ -1234,7 +1236,7 @@ impl Array {
IntegerOrInfinity::Integer(n) => len + n,
};
let search_element = args.get(0).cloned().unwrap_or_default();
let search_element = args.get_or_undefined(0);
// 8. Repeat, while k ≥ 0,
while k >= 0 {
@ -1246,7 +1248,7 @@ impl Array {
let element_k = o.get(k, context)?;
// ii. Let same be IsStrictlyEqual(searchElement, elementK).
// iii. If same is true, return 𝔽(k).
if JsValue::strict_equals(&search_element, &element_k) {
if JsValue::strict_equals(search_element, &element_k) {
return Ok(JsValue::new(k));
}
}
@ -1288,7 +1290,7 @@ impl Array {
}
};
let this_arg = args.get(1).cloned().unwrap_or_default();
let this_arg = args.get_or_undefined(1);
// 4. Let k be 0.
let mut k = 0;
@ -1301,7 +1303,7 @@ impl Array {
// c. Let testResult be ! ToBoolean(? Call(predicate, thisArg, « kValue, 𝔽(k), O »)).
let test_result = predicate
.call(
&this_arg,
this_arg,
&[k_value.clone(), k.into(), o.clone().into()],
context,
)?
@ -1349,7 +1351,7 @@ impl Array {
}
};
let this_arg = args.get(1).cloned().unwrap_or_default();
let this_arg = args.get_or_undefined(1);
// 4. Let k be 0.
let mut k = 0;
@ -1361,7 +1363,7 @@ impl Array {
let k_value = o.get(pk, context)?;
// c. Let testResult be ! ToBoolean(? Call(predicate, thisArg, « kValue, 𝔽(k), O »)).
let test_result = predicate
.call(&this_arg, &[k_value, k.into(), o.clone().into()], context)?
.call(this_arg, &[k_value, k.into(), o.clone().into()], context)?
.to_boolean();
// d. If testResult is true, return 𝔽(k).
if test_result {
@ -1453,7 +1455,7 @@ impl Array {
let source_len = o.length_of_array_like(context)?;
// 3. If ! IsCallable(mapperFunction) is false, throw a TypeError exception.
let mapper_function = args.get(0).cloned().unwrap_or_default();
let mapper_function = args.get_or_undefined(0);
if !mapper_function.is_function() {
return context.throw_type_error("flatMap mapper function is not callable");
}
@ -1469,7 +1471,7 @@ impl Array {
0,
1,
Some(mapper_function.as_object().unwrap()),
&args.get(1).cloned().unwrap_or_default(),
args.get_or_undefined(1),
context,
)?;
@ -1624,7 +1626,7 @@ impl Array {
// 10. Else, let final be min(relativeEnd, len).
let final_ = Self::get_relative_end(context, args.get(2), len)?;
let value = args.get(0).cloned().unwrap_or_default();
let value = args.get_or_undefined(0);
// 11. Repeat, while k < final,
while k < final_ {
@ -1695,14 +1697,14 @@ impl Array {
}
}
let search_element = args.get(0).cloned().unwrap_or_default();
let search_element = args.get_or_undefined(0);
// 10. Repeat, while k < len,
while k < len {
// a. Let elementK be ? Get(O, ! ToString(𝔽(k))).
let element_k = o.get(k, context)?;
// b. If SameValueZero(searchElement, elementK) is true, return true.
if JsValue::same_value_zero(&search_element, &element_k) {
if JsValue::same_value_zero(search_element, &element_k) {
return Ok(JsValue::new(true));
}
// c. Set k to k + 1.
@ -1992,7 +1994,7 @@ impl Array {
"missing argument 0 when calling function Array.prototype.filter",
)
})?;
let this_val = args.get(1).cloned().unwrap_or_else(JsValue::undefined);
let this_arg = args.get_or_undefined(1);
if !callback.is_callable() {
return context.throw_type_error("the callback must be callable");
@ -2016,7 +2018,7 @@ impl Array {
let args = [element.clone(), JsValue::new(idx), JsValue::new(o.clone())];
// ii. Let selected be ! ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)).
let selected = callback.call(&this_val, &args, context)?.to_boolean();
let selected = callback.call(this_arg, &args, context)?.to_boolean();
// iii. If selected is true, then
if selected {
@ -2078,9 +2080,9 @@ impl Array {
// i. Let kValue be ? Get(O, Pk).
let k_value = o.get(k, context)?;
// ii. Let testResult be ! ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)).
let this_arg = args.get(1).cloned().unwrap_or_default();
let this_arg = args.get_or_undefined(1);
let test_result = callback
.call(&this_arg, &[k_value, k.into(), o.clone().into()], context)?
.call(this_arg, &[k_value, k.into(), o.clone().into()], context)?
.to_boolean();
// iii. If testResult is true, return true.
if test_result {

16
boa/src/builtins/bigint/mod.rs

@ -13,8 +13,12 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt
use crate::{
builtins::BuiltIn, object::ConstructorBuilder, property::Attribute, symbol::WellKnownSymbols,
value::IntegerOrInfinity, BoaProfiler, Context, JsBigInt, JsResult, JsValue,
builtins::{BuiltIn, JsArgs},
object::ConstructorBuilder,
property::Attribute,
symbol::WellKnownSymbols,
value::IntegerOrInfinity,
BoaProfiler, Context, JsBigInt, JsResult, JsValue,
};
#[cfg(test)]
mod tests;
@ -131,7 +135,7 @@ impl BigInt {
// 1. Let x be ? thisBigIntValue(this value).
let x = Self::this_bigint_value(this, context)?;
let radix = args.get(0).cloned().unwrap_or_default();
let radix = args.get_or_undefined(0);
// 2. If radix is undefined, let radixMV be 10.
let radix_mv = if radix.is_undefined() {
@ -234,10 +238,8 @@ impl BigInt {
fn calculate_as_uint_n(args: &[JsValue], context: &mut Context) -> JsResult<(JsBigInt, u32)> {
use std::convert::TryFrom;
let undefined_value = JsValue::undefined();
let bits_arg = args.get(0).unwrap_or(&undefined_value);
let bigint_arg = args.get(1).unwrap_or(&undefined_value);
let bits_arg = args.get_or_undefined(0);
let bigint_arg = args.get_or_undefined(1);
let bits = bits_arg.to_index(context)?;
let bits = u32::try_from(bits).unwrap_or(u32::MAX);

7
boa/src/builtins/console/mod.rs

@ -17,7 +17,7 @@
mod tests;
use crate::{
builtins::BuiltIn,
builtins::{BuiltIn, JsArgs},
object::ObjectInitializer,
property::Attribute,
value::{display::display_obj, JsValue},
@ -90,7 +90,7 @@ pub fn formatter(data: &[JsValue], context: &mut Context) -> JsResult<String> {
}
/* object, FIXME: how to render this properly? */
'o' | 'O' => {
let arg = data.get(arg_index).cloned().unwrap_or_default();
let arg = data.get_or_undefined(arg_index);
formatted.push_str(&format!("{}", arg.display()));
arg_index += 1
}
@ -564,9 +564,8 @@ impl Console {
/// [spec]: https://console.spec.whatwg.org/#dir
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/dir
pub(crate) fn dir(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let undefined = JsValue::undefined();
logger(
LogMessage::Info(display_obj(args.get(0).unwrap_or(&undefined), true)),
LogMessage::Info(display_obj(args.get_or_undefined(0), true)),
context.console(),
);

4
boa/src/builtins/date/mod.rs

@ -13,6 +13,8 @@ use crate::{
use chrono::{prelude::*, Duration, LocalResult};
use std::fmt::Display;
use super::JsArgs;
/// The number of nanoseconds in a millisecond.
const NANOS_PER_MS: i64 = 1_000_000;
/// The number of milliseconds in an hour.
@ -523,7 +525,7 @@ impl Date {
return context.throw_type_error("Date.prototype[@@toPrimitive] called on non object");
};
let hint = args.get(0).cloned().unwrap_or_default();
let hint = args.get_or_undefined(0);
let try_first = match hint.as_string().map(|s| s.as_str()) {
// 3. If hint is "string" or "default", then

14
boa/src/builtins/function/mod.rs

@ -26,6 +26,8 @@ use dyn_clone::DynClone;
use sealed::Sealed;
use std::fmt::{self, Debug};
use super::JsArgs;
#[cfg(test)]
mod tests;
@ -340,10 +342,10 @@ impl BuiltInFunctionObject {
if !this.is_function() {
return context.throw_type_error(format!("{} is not a function", this.display()));
}
let this_arg: JsValue = args.get(0).cloned().unwrap_or_default();
let this_arg = args.get_or_undefined(0);
// TODO?: 3. Perform PrepareForTailCall
let start = if !args.is_empty() { 1 } else { 0 };
context.call(this, &this_arg, &args[start..])
context.call(this, this_arg, &args[start..])
}
/// `Function.prototype.apply`
@ -361,15 +363,15 @@ impl BuiltInFunctionObject {
if !this.is_function() {
return context.throw_type_error(format!("{} is not a function", this.display()));
}
let this_arg = args.get(0).cloned().unwrap_or_default();
let arg_array = args.get(1).cloned().unwrap_or_default();
let this_arg = args.get_or_undefined(0);
let arg_array = args.get_or_undefined(1);
if arg_array.is_null_or_undefined() {
// TODO?: 3.a. PrepareForTailCall
return context.call(this, &this_arg, &[]);
return context.call(this, this_arg, &[]);
}
let arg_list = arg_array.create_list_from_array_like(&[], context)?;
// TODO?: 5. PrepareForTailCall
context.call(this, &this_arg, &arg_list)
context.call(this, this_arg, &arg_list)
}
}

8
boa/src/builtins/json/mod.rs

@ -26,6 +26,8 @@ use crate::{
};
use serde_json::{self, Value as JSONValue};
use super::JsArgs;
#[cfg(test)]
mod tests;
@ -156,7 +158,7 @@ impl Json {
let mut property_list = None;
let mut replacer_function = None;
let replacer = args.get(1).cloned().unwrap_or_default();
let replacer = args.get_or_undefined(1);
// 4. If Type(replacer) is Object, then
if let Some(replacer_obj) = replacer.as_object() {
@ -212,7 +214,7 @@ impl Json {
}
}
let mut space = args.get(2).cloned().unwrap_or_default();
let mut space = args.get_or_undefined(2).clone();
// 5. If Type(space) is Object, then
if let Some(space_obj) = space.as_object() {
@ -264,7 +266,7 @@ impl Json {
// 10. Perform ! CreateDataPropertyOrThrow(wrapper, the empty String, value).
wrapper
.create_data_property_or_throw("", args.get(0).cloned().unwrap_or_default(), context)
.create_data_property_or_throw("", args.get_or_undefined(0).clone(), context)
.expect("CreateDataPropertyOrThrow should never fail here");
// 11. Let state be the Record { [[ReplacerFunction]]: ReplacerFunction, [[Stack]]: stack, [[Indent]]: indent, [[Gap]]: gap, [[PropertyList]]: PropertyList }.

27
boa/src/builtins/map/mod.rs

@ -26,6 +26,8 @@ use map_iterator::MapIterator;
use self::ordered_map::MapLock;
use super::JsArgs;
pub mod ordered_map;
#[cfg(test)]
mod tests;
@ -245,15 +247,12 @@ impl Map {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let (key, value) = match args.len() {
0 => (JsValue::undefined(), JsValue::undefined()),
1 => (args[0].clone(), JsValue::undefined()),
_ => (args[0].clone(), args[1].clone()),
};
let key = args.get_or_undefined(0);
let value = args.get_or_undefined(1);
let size = if let Some(object) = this.as_object() {
if let Some(map) = object.borrow_mut().as_map_mut() {
map.insert(key, value);
map.insert(key.clone(), value.clone());
map.len()
} else {
return Err(context.construct_type_error("'this' is not a Map"));
@ -281,11 +280,11 @@ impl Map {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let key = args.get(0).cloned().unwrap_or_default();
let key = args.get_or_undefined(0);
let (deleted, size) = if let Some(object) = this.as_object() {
if let Some(map) = object.borrow_mut().as_map_mut() {
let deleted = map.remove(&key).is_some();
let deleted = map.remove(key).is_some();
(deleted, map.len())
} else {
return Err(context.construct_type_error("'this' is not a Map"));
@ -312,12 +311,12 @@ impl Map {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let key = args.get(0).cloned().unwrap_or_default();
let key = args.get_or_undefined(0);
if let JsValue::Object(ref object) = this {
let object = object.borrow();
if let Some(map) = object.as_map_ref() {
return Ok(if let Some(result) = map.get(&key) {
return Ok(if let Some(result) = map.get(key) {
result.clone()
} else {
JsValue::undefined()
@ -361,12 +360,12 @@ impl Map {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let key = args.get(0).cloned().unwrap_or_default();
let key = args.get_or_undefined(0);
if let JsValue::Object(ref object) = this {
let object = object.borrow();
if let Some(map) = object.as_map_ref() {
return Ok(map.contains_key(&key).into());
return Ok(map.contains_key(key).into());
}
}
@ -393,7 +392,7 @@ impl Map {
}
let callback_arg = &args[0];
let this_arg = args.get(1).cloned().unwrap_or_else(JsValue::undefined);
let this_arg = args.get_or_undefined(1);
let mut index = 0;
@ -416,7 +415,7 @@ impl Map {
};
if let Some(arguments) = arguments {
context.call(callback_arg, &this_arg, &arguments)?;
context.call(callback_arg, this_arg, &arguments)?;
}
index += 1;

22
boa/src/builtins/mod.rs

@ -112,3 +112,25 @@ pub fn init(context: &mut Context) {
global_object.borrow_mut().insert(name, property);
}
}
pub trait JsArgs {
/// Utility function to `get` a parameter from
/// a `[JsValue]` or default to `JsValue::Undefined`
/// if `get` returns `None`.
///
/// Call this if you are thinking of calling something similar to
/// `args.get(n).cloned().unwrap_or_default()` or
/// `args.get(n).unwrap_or(&undefined)`.
///
/// This returns a reference for efficiency, in case
/// you only need to call methods of `JsValue`, so
/// try to minimize calling `clone`.
fn get_or_undefined(&self, index: usize) -> &JsValue;
}
impl JsArgs for [JsValue] {
fn get_or_undefined(&self, index: usize) -> &JsValue {
const UNDEFINED: &JsValue = &JsValue::Undefined;
self.get(index).unwrap_or(UNDEFINED)
}
}

10
boa/src/builtins/number/mod.rs

@ -13,8 +13,8 @@
//! [spec]: https://tc39.es/ecma262/#sec-number-object
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number
use super::function::make_builtin_fn;
use super::string::is_trimmable_whitespace;
use super::{function::make_builtin_fn, JsArgs};
use crate::{
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData, PROTOTYPE},
@ -392,12 +392,12 @@ impl Number {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let precision = args.get(0).cloned().unwrap_or_default();
let precision = args.get_or_undefined(0);
// 1 & 6
let mut this_num = Self::this_number_value(this, context)?;
// 2
if precision == JsValue::undefined() {
if precision.is_undefined() {
return Self::to_string(this, &[], context);
}
@ -720,7 +720,7 @@ impl Number {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
if let (Some(val), radix) = (args.get(0), args.get(1)) {
if let (Some(val), radix) = (args.get(0), args.get_or_undefined(1)) {
// 1. Let inputString be ? ToString(string).
let input_string = val.to_string(context)?;
@ -745,7 +745,7 @@ impl Number {
}
// 6. Let R be ℝ(? ToInt32(radix)).
let mut var_r = radix.cloned().unwrap_or_default().to_i32(context)?;
let mut var_r = radix.to_i32(context)?;
// 7. Let stripPrefix be true.
let mut strip_prefix = true;

45
boa/src/builtins/object/mod.rs

@ -14,7 +14,7 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
use crate::{
builtins::BuiltIn,
builtins::{BuiltIn, JsArgs},
object::{
ConstructorBuilder, IntegrityLevel, JsObject, Object as BuiltinObject, ObjectData,
ObjectInitializer, ObjectKind, PROTOTYPE,
@ -135,12 +135,12 @@ impl Object {
/// [spec]: https://tc39.es/ecma262/#sec-object.create
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
pub fn create(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let prototype = args.get(0).cloned().unwrap_or_else(JsValue::undefined);
let properties = args.get(1).cloned().unwrap_or_else(JsValue::undefined);
let prototype = args.get_or_undefined(0);
let properties = args.get_or_undefined(1);
let obj = match prototype {
JsValue::Object(_) | JsValue::Null => JsObject::new(BuiltinObject::with_prototype(
prototype,
prototype.clone(),
ObjectData::ordinary(),
)),
_ => {
@ -152,7 +152,7 @@ impl Object {
};
if !properties.is_undefined() {
object_define_properties(&obj, properties, context)?;
object_define_properties(&obj, properties.clone(), context)?;
return Ok(obj.into());
}
@ -174,10 +174,7 @@ impl Object {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let object = args
.get(0)
.unwrap_or(&JsValue::undefined())
.to_object(context)?;
let object = args.get_or_undefined(0).to_object(context)?;
if let Some(key) = args.get(1) {
let key = key.to_property_key(context)?;
@ -276,10 +273,10 @@ impl Object {
/// Uses the SameValue algorithm to check equality of objects
pub fn is(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
let x = args.get(0).cloned().unwrap_or_else(JsValue::undefined);
let y = args.get(1).cloned().unwrap_or_else(JsValue::undefined);
let x = args.get_or_undefined(0);
let y = args.get_or_undefined(1);
Ok(JsValue::same_value(&x, &y).into())
Ok(JsValue::same_value(x, y).into())
}
/// Get the `prototype` of an object.
@ -319,7 +316,7 @@ impl Object {
.clone();
// 2. If Type(proto) is neither Object nor Null, throw a TypeError exception.
let proto = args.get(1).cloned().unwrap_or_default();
let proto = args.get_or_undefined(1);
if !matches!(proto.get_type(), Type::Object | Type::Null) {
return ctx.throw_type_error(format!(
"expected an object or null, got {}",
@ -336,7 +333,7 @@ impl Object {
let status = obj
.as_object()
.expect("obj was not an object")
.__set_prototype_of__(proto, ctx)?;
.__set_prototype_of__(proto.clone(), ctx)?;
// 5. If status is false, throw a TypeError exception.
if !status {
@ -362,11 +359,11 @@ impl Object {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let undefined = JsValue::undefined();
let mut v = args.get(0).unwrap_or(&undefined).clone();
let v = args.get_or_undefined(0);
if !v.is_object() {
return Ok(JsValue::new(false));
}
let mut v = v.clone();
let o = JsValue::new(this.to_object(context)?);
loop {
v = Self::get_prototype_of(this, &[v], context)?;
@ -385,7 +382,7 @@ impl Object {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let object = args.get(0).cloned().unwrap_or_else(JsValue::undefined);
let object = args.get_or_undefined(0);
if let Some(object) = object.as_object() {
let key = args
.get(1)
@ -419,12 +416,12 @@ impl Object {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let arg = args.get(0).cloned().unwrap_or_default();
let arg = args.get_or_undefined(0);
let arg_obj = arg.as_object();
if let Some(obj) = arg_obj {
let props = args.get(1).cloned().unwrap_or_else(JsValue::undefined);
object_define_properties(&obj, props, context)?;
Ok(arg)
let props = args.get_or_undefined(1);
object_define_properties(&obj, props.clone(), context)?;
Ok(arg.clone())
} else {
context.throw_type_error("Expected an object")
}
@ -572,11 +569,7 @@ impl Object {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
pub fn assign(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
// 1. Let to be ? ToObject(target).
let to = args
.get(0)
.cloned()
.unwrap_or_default()
.to_object(context)?;
let to = args.get_or_undefined(0).to_object(context)?;
// 2. If only one argument was passed, return to.
if args.len() == 1 {

27
boa/src/builtins/reflect/mod.rs

@ -18,7 +18,7 @@ use crate::{
BoaProfiler, Context, JsResult, JsValue,
};
use super::Array;
use super::{Array, JsArgs};
#[cfg(test)]
mod tests;
@ -81,14 +81,14 @@ impl Reflect {
.get(0)
.and_then(|v| v.as_object())
.ok_or_else(|| context.construct_type_error("target must be a function"))?;
let this_arg = args.get(1).cloned().unwrap_or_default();
let args_list = args.get(2).cloned().unwrap_or_default();
let this_arg = args.get_or_undefined(1);
let args_list = args.get_or_undefined(2);
if !target.is_callable() {
return context.throw_type_error("target must be a function");
}
let args = args_list.create_list_from_array_like(&[], context)?;
target.call(&this_arg, &args, context)
target.call(this_arg, &args, context)
}
/// Calls a target function as a constructor with arguments.
@ -108,7 +108,7 @@ impl Reflect {
.get(0)
.and_then(|v| v.as_object())
.ok_or_else(|| context.construct_type_error("target must be a function"))?;
let args_list = args.get(1).cloned().unwrap_or_default();
let args_list = args.get_or_undefined(1);
if !target.is_constructable() {
return context.throw_type_error("target must be a constructor");
@ -140,12 +140,11 @@ impl Reflect {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let undefined = JsValue::undefined();
let target = args
.get(0)
.and_then(|v| v.as_object())
.ok_or_else(|| context.construct_type_error("target must be an object"))?;
let key = args.get(1).unwrap_or(&undefined).to_property_key(context)?;
let key = args.get_or_undefined(1).to_property_key(context)?;
let prop_desc: JsValue = args
.get(2)
.and_then(|v| v.as_object())
@ -170,12 +169,11 @@ impl Reflect {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let undefined = JsValue::undefined();
let target = args
.get(0)
.and_then(|v| v.as_object())
.ok_or_else(|| context.construct_type_error("target must be an object"))?;
let key = args.get(1).unwrap_or(&undefined).to_property_key(context)?;
let key = args.get_or_undefined(1).to_property_key(context)?;
Ok(target.__delete__(&key, context)?.into())
}
@ -189,14 +187,13 @@ impl Reflect {
/// [spec]: https://tc39.es/ecma262/#sec-reflect.get
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/get
pub(crate) fn get(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let undefined = JsValue::undefined();
// 1. If Type(target) is not Object, throw a TypeError exception.
let target = args
.get(0)
.and_then(|v| v.as_object())
.ok_or_else(|| context.construct_type_error("target must be an object"))?;
// 2. Let key be ? ToPropertyKey(propertyKey).
let key = args.get(1).unwrap_or(&undefined).to_property_key(context)?;
let key = args.get_or_undefined(1).to_property_key(context)?;
// 3. If receiver is not present, then
let receiver = if let Some(receiver) = args.get(2).cloned() {
receiver
@ -347,13 +344,12 @@ impl Reflect {
/// [spec]: https://tc39.es/ecma262/#sec-reflect.set
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/set
pub(crate) fn set(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let undefined = JsValue::undefined();
let target = args
.get(0)
.and_then(|v| v.as_object())
.ok_or_else(|| context.construct_type_error("target must be an object"))?;
let key = args.get(1).unwrap_or(&undefined).to_property_key(context)?;
let value = args.get(2).unwrap_or(&undefined);
let key = args.get_or_undefined(1).to_property_key(context)?;
let value = args.get_or_undefined(2);
let receiver = if let Some(receiver) = args.get(3).cloned() {
receiver
} else {
@ -377,12 +373,11 @@ impl Reflect {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let undefined = JsValue::undefined();
let mut target = args
.get(0)
.and_then(|v| v.as_object())
.ok_or_else(|| context.construct_type_error("target must be an object"))?;
let proto = args.get(1).unwrap_or(&undefined);
let proto = args.get_or_undefined(1);
if !proto.is_null() && !proto.is_object() {
return context.throw_type_error("proto must be an object or null");
}

18
boa/src/builtins/regexp/mod.rs

@ -23,6 +23,8 @@ use crate::{
use regexp_string_iterator::RegExpStringIterator;
use regress::Regex;
use super::JsArgs;
#[cfg(test)]
mod tests;
@ -187,8 +189,8 @@ impl RegExp {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let pattern = args.get(0).cloned().unwrap_or_else(JsValue::undefined);
let flags = args.get(1).cloned().unwrap_or_else(JsValue::undefined);
let pattern = args.get_or_undefined(0);
let flags = args.get_or_undefined(1);
// 1. Let patternIsRegExp be ? IsRegExp(pattern).
let pattern_is_regexp = if let JsValue::Object(obj) = &pattern {
@ -233,12 +235,12 @@ impl RegExp {
JsValue::new(regexp.original_flags.clone()),
)
} else {
(JsValue::new(regexp.original_source.clone()), flags)
(JsValue::new(regexp.original_source.clone()), flags.clone())
}
} else {
// a. Let P be pattern.
// b. Let F be flags.
(pattern, flags)
(pattern.clone(), flags.clone())
};
// 7. Let O be ? RegExpAlloc(newTarget).
@ -275,8 +277,8 @@ impl RegExp {
///
/// [spec]: https://tc39.es/ecma262/#sec-regexpinitialize
fn initialize(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let pattern = args.get(0).cloned().unwrap_or_else(JsValue::undefined);
let flags = args.get(1).cloned().unwrap_or_else(JsValue::undefined);
let pattern = args.get_or_undefined(0);
let flags = args.get_or_undefined(1);
// 1. If pattern is undefined, let P be the empty String.
// 2. Else, let P be ? ToString(pattern).
@ -1282,7 +1284,7 @@ impl RegExp {
let length_arg_str = arg_str.encode_utf16().count();
// 5. Let functionalReplace be IsCallable(replaceValue).
let mut replace_value = args.get(1).cloned().unwrap_or_default();
let mut replace_value = args.get_or_undefined(1).clone();
let functional_replace = replace_value.is_function();
// 6. If functionalReplace is false, then
@ -1619,7 +1621,7 @@ impl RegExp {
let mut length_a = 0;
// 13. If limit is undefined, let lim be 2^32 - 1; else let lim be ℝ(? ToUint32(limit)).
let limit = args.get(1).cloned().unwrap_or_default();
let limit = args.get_or_undefined(1);
let lim = if limit.is_undefined() {
u32::MAX
} else {

29
boa/src/builtins/set/mod.rs

@ -22,6 +22,8 @@ use ordered_set::OrderedSet;
pub mod set_iterator;
use set_iterator::SetIterator;
use super::JsArgs;
pub mod ordered_set;
#[cfg(test)]
mod tests;
@ -139,7 +141,7 @@ impl Set {
// 3
set.set_data(ObjectData::set(OrderedSet::default()));
let iterable = args.get(0).cloned().unwrap_or_default();
let iterable = args.get_or_undefined(0);
// 4
if iterable.is_null_or_undefined() {
return Ok(set);
@ -154,7 +156,7 @@ impl Set {
}
// 7
let iterator_record = get_iterator(context, iterable)?;
let iterator_record = get_iterator(context, iterable.clone())?;
// 8.a
let mut next = iterator_record.next(context)?;
@ -206,14 +208,15 @@ impl Set {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let mut value = args.get(0).cloned().unwrap_or_default();
let value = args.get_or_undefined(0);
if let Some(object) = this.as_object() {
if let Some(set) = object.borrow_mut().as_set_mut() {
if value.as_number().map(|n| n == -0f64).unwrap_or(false) {
value = JsValue::Integer(0);
}
set.add(value);
set.add(if value.as_number().map(|n| n == -0f64).unwrap_or(false) {
JsValue::Integer(0)
} else {
value.clone()
});
} else {
return context.throw_type_error("'this' is not a Set");
}
@ -263,11 +266,11 @@ impl Set {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let value = args.get(0).cloned().unwrap_or_default();
let value = args.get_or_undefined(0);
let res = if let Some(object) = this.as_object() {
if let Some(set) = object.borrow_mut().as_set_mut() {
set.delete(&value)
set.delete(value)
} else {
return context.throw_type_error("'this' is not a Set");
}
@ -332,12 +335,12 @@ impl Set {
}
let callback_arg = &args[0];
let this_arg = args.get(1).cloned().unwrap_or_else(JsValue::undefined);
let this_arg = args.get_or_undefined(1);
// TODO: if condition should also check that we are not in strict mode
let this_arg = if this_arg.is_undefined() {
JsValue::Object(context.global_object())
} else {
this_arg
this_arg.clone()
};
let mut index = 0;
@ -380,12 +383,12 @@ impl Set {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let value = args.get(0).cloned().unwrap_or_default();
let value = args.get_or_undefined(0);
if let JsValue::Object(ref object) = this {
let object = object.borrow();
if let Some(set) = object.as_set_ref() {
return Ok(set.contains(&value).into());
return Ok(set.contains(value).into());
}
}

115
boa/src/builtins/string/mod.rs

@ -29,6 +29,8 @@ use std::{
};
use unicode_normalization::UnicodeNormalization;
use super::JsArgs;
pub(crate) fn code_point_at(string: JsString, position: i32) -> Option<(u32, u8, bool)> {
let size = string.encode_utf16().count() as i32;
if position < 0 || position >= size {
@ -562,9 +564,9 @@ impl String {
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(context)?;
let arg = args.get(0).cloned().unwrap_or_else(JsValue::undefined);
let arg = args.get_or_undefined(0);
if Self::is_regexp_object(&arg) {
if Self::is_regexp_object(arg) {
context.throw_type_error(
"First argument to String.prototype.startsWith must not be a regular expression",
)?;
@ -576,12 +578,10 @@ impl String {
let search_length = search_string.chars().count() as i32;
// If less than 2 args specified, position is 'undefined', defaults to 0
let position = if args.len() < 2 {
0
let position = if let Some(integer) = args.get(1) {
integer.to_integer(context)? as i32
} else {
args.get(1)
.expect("failed to get arg")
.to_integer(context)? as i32
0
};
let start = min(max(position, 0), length);
@ -617,9 +617,9 @@ impl String {
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(context)?;
let arg = args.get(0).cloned().unwrap_or_else(JsValue::undefined);
let arg = args.get_or_undefined(0);
if Self::is_regexp_object(&arg) {
if Self::is_regexp_object(arg) {
context.throw_type_error(
"First argument to String.prototype.endsWith must not be a regular expression",
)?;
@ -632,12 +632,10 @@ impl String {
// If less than 2 args specified, end_position is 'undefined', defaults to
// length of this
let end_position = if args.len() < 2 {
length
let end_position = if let Some(integer) = args.get(1) {
integer.to_integer(context)? as i32
} else {
args.get(1)
.expect("Could not get argument")
.to_integer(context)? as i32
length
};
let end = min(max(end_position, 0), length);
@ -671,9 +669,9 @@ impl String {
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(context)?;
let arg = args.get(0).cloned().unwrap_or_else(JsValue::undefined);
let arg = args.get_or_undefined(0);
if Self::is_regexp_object(&arg) {
if Self::is_regexp_object(arg) {
context.throw_type_error(
"First argument to String.prototype.includes must not be a regular expression",
)?;
@ -684,12 +682,11 @@ impl String {
let length = primitive_val.chars().count() as i32;
// If less than 2 args specified, position is 'undefined', defaults to 0
let position = if args.len() < 2 {
0
let position = if let Some(integer) = args.get(1) {
integer.to_integer(context)? as i32
} else {
args.get(1)
.expect("Could not get argument")
.to_integer(context)? as i32
0
};
let start = min(max(position, 0), length);
@ -730,9 +727,9 @@ impl String {
// 1. Let O be ? RequireObjectCoercible(this value).
this.require_object_coercible(context)?;
let search_value = args.get(0).cloned().unwrap_or_default();
let search_value = args.get_or_undefined(0);
let replace_value = args.get(1).cloned().unwrap_or_default();
let replace_value = args.get_or_undefined(1);
// 2. If searchValue is neither undefined nor null, then
if !search_value.is_null_or_undefined() {
@ -747,8 +744,8 @@ impl String {
// i. Return ? Call(replacer, searchValue, « O, replaceValue »).
return context.call(
&replacer.into(),
&search_value,
&[this.clone(), replace_value],
search_value,
&[this.clone(), replace_value.clone()],
);
}
}
@ -787,7 +784,7 @@ impl String {
// a. Let replacement be ? ToString(? Call(replaceValue, undefined, « searchString, 𝔽(position), string »)).
context
.call(
&replace_value,
replace_value,
&JsValue::undefined(),
&[search_str.into(), position.into(), this_str.clone().into()],
)?
@ -846,8 +843,8 @@ impl String {
// 1. Let O be ? RequireObjectCoercible(this value).
let o = this.require_object_coercible(context)?;
let search_value = args.get(0).cloned().unwrap_or_default();
let replace_value = args.get(1).cloned().unwrap_or_default();
let search_value = args.get_or_undefined(0);
let replace_value = args.get_or_undefined(1);
// 2. If searchValue is neither undefined nor null, then
if !search_value.is_null_or_undefined() {
@ -879,7 +876,7 @@ impl String {
// d. If replacer is not undefined, then
if let Some(replacer) = replacer {
// i. Return ? Call(replacer, searchValue, « O, replaceValue »).
return replacer.call(&search_value, &[o.into(), replace_value], context);
return replacer.call(search_value, &[o.into(), replace_value.clone()], context);
}
}
@ -945,7 +942,7 @@ impl String {
// i. Let replacement be ? ToString(? Call(replaceValue, undefined, « searchString, 𝔽(p), string »)).
context
.call(
&replace_value,
replace_value,
&JsValue::undefined(),
&[
search_string.clone().into(),
@ -1109,14 +1106,14 @@ impl String {
let o = this.require_object_coercible(context)?;
// 2. If regexp is neither undefined nor null, then
let regexp = args.get(0).cloned().unwrap_or_default();
let regexp = args.get_or_undefined(0);
if !regexp.is_null_or_undefined() {
// a. Let matcher be ? GetMethod(regexp, @@match).
// b. If matcher is not undefined, then
if let Some(obj) = regexp.as_object() {
if let Some(matcher) = obj.get_method(context, WellKnownSymbols::match_())? {
// i. Return ? Call(matcher, regexp, « O »).
return matcher.call(&regexp, &[o.clone()], context);
return matcher.call(regexp, &[o.clone()], context);
}
}
}
@ -1125,7 +1122,7 @@ impl String {
let s = o.to_string(context)?;
// 4. Let rx be ? RegExpCreate(regexp, undefined).
let rx = RegExp::create(regexp, JsValue::undefined(), context)?;
let rx = RegExp::create(regexp.clone(), JsValue::undefined(), context)?;
// 5. Return ? Invoke(rx, @@match, « S »).
let obj = rx.as_object().expect("RegExpCreate must return Object");
@ -1370,21 +1367,17 @@ impl String {
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(context)?;
// If no args are specified, start is 'undefined', defaults to 0
let start = if args.is_empty() {
0
let start = if let Some(integer) = args.get(0) {
integer.to_integer(context)? as i32
} else {
args.get(0)
.expect("failed to get argument for String method")
.to_integer(context)? as i32
0
};
let length = primitive_val.encode_utf16().count() as i32;
// If less than 2 args specified, end is the length of the this object converted to a String
let end = if args.len() < 2 {
length
let end = if let Some(integer) = args.get(1) {
integer.to_integer(context)? as i32
} else {
args.get(1)
.expect("Could not get argument")
.to_integer(context)? as i32
length
};
// Both start and end args replaced by 0 if they were negative
// or by the length of the String if they were greater
@ -1425,24 +1418,20 @@ impl String {
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(context)?;
// If no args are specified, start is 'undefined', defaults to 0
let mut start = if args.is_empty() {
0
let mut start = if let Some(integer) = args.get(0) {
integer.to_integer(context)? as i32
} else {
args.get(0)
.expect("failed to get argument for String method")
.to_integer(context)? as i32
0
};
let length = primitive_val.chars().count() as i32;
// If less than 2 args specified, end is +infinity, the maximum number value.
// Using i32::max_value() should be safe because the final length used is at most
// the number of code units from start to the end of the string,
// which should always be smaller or equals to both +infinity and i32::max_value
let end = if args.len() < 2 {
i32::MAX
let end = if let Some(integer) = args.get(1) {
integer.to_integer(context)? as i32
} else {
args.get(1)
.expect("Could not get argument")
.to_integer(context)? as i32
i32::MAX
};
// If start is negative it become the number of code units from the end of the string
if start < 0 {
@ -1485,8 +1474,8 @@ impl String {
// 1. Let O be ? RequireObjectCoercible(this value).
let this = this.require_object_coercible(context)?;
let separator = args.get(0).cloned().unwrap_or_default();
let limit = args.get(1).cloned().unwrap_or_default();
let separator = args.get_or_undefined(0);
let limit = args.get_or_undefined(1);
// 2. If separator is neither undefined nor null, then
if !separator.is_null_or_undefined() {
@ -1498,7 +1487,7 @@ impl String {
.get_method(context, WellKnownSymbols::split())?
{
// i. Return ? Call(splitter, separator, « O, limit »).
return splitter.call(&separator, &[this.clone(), limit], context);
return splitter.call(separator, &[this.clone(), limit.clone()], context);
}
}
@ -1661,7 +1650,7 @@ impl String {
let o = this.require_object_coercible(context)?;
// 2. If regexp is neither undefined nor null, then
let regexp = args.get(0).cloned().unwrap_or_default();
let regexp = args.get_or_undefined(0);
if !regexp.is_null_or_undefined() {
// a. Let isRegExp be ? IsRegExp(regexp).
// b. If isRegExp is true, then
@ -1685,7 +1674,7 @@ impl String {
if let Some(obj) = regexp.as_object() {
if let Some(matcher) = obj.get_method(context, WellKnownSymbols::match_all())? {
// i. Return ? Call(matcher, regexp, « O »).
return matcher.call(&regexp, &[o.clone()], context);
return matcher.call(regexp, &[o.clone()], context);
}
}
}
@ -1694,7 +1683,7 @@ impl String {
let s = o.to_string(context)?;
// 4. Let rx be ? RegExpCreate(regexp, "g").
let rx = RegExp::create(regexp, JsValue::new("g"), context)?;
let rx = RegExp::create(regexp.clone(), JsValue::new("g"), context)?;
// 5. Return ? Invoke(rx, @@matchAll, « S »).
let obj = rx.as_object().expect("RegExpCreate must return Object");
@ -1722,7 +1711,7 @@ impl String {
) -> JsResult<JsValue> {
let this = this.require_object_coercible(context)?;
let s = this.to_string(context)?;
let form = args.get(0).cloned().unwrap_or_default();
let form = args.get_or_undefined(0);
let f_str;
@ -1762,14 +1751,14 @@ impl String {
let o = this.require_object_coercible(context)?;
// 2. If regexp is neither undefined nor null, then
let regexp = args.get(0).cloned().unwrap_or_default();
let regexp = args.get_or_undefined(0);
if !regexp.is_null_or_undefined() {
// a. Let searcher be ? GetMethod(regexp, @@search).
// b. If searcher is not undefined, then
if let Some(obj) = regexp.as_object() {
if let Some(searcher) = obj.get_method(context, WellKnownSymbols::search())? {
// i. Return ? Call(searcher, regexp, « O »).
return searcher.call(&regexp, &[o.clone()], context);
return searcher.call(regexp, &[o.clone()], context);
}
}
}
@ -1778,7 +1767,7 @@ impl String {
let string = o.to_string(context)?;
// 4. Let rx be ? RegExpCreate(regexp, undefined).
let rx = RegExp::create(regexp, JsValue::undefined(), context)?;
let rx = RegExp::create(regexp.clone(), JsValue::undefined(), context)?;
// 5. Return ? Invoke(rx, @@search, « string »).
let obj = rx.as_object().expect("RegExpCreate must return Object");

4
boa/src/builtins/symbol/mod.rs

@ -31,6 +31,8 @@ use std::cell::RefCell;
use rustc_hash::FxHashMap;
use super::JsArgs;
thread_local! {
static GLOBAL_SYMBOL_REGISTRY: RefCell<GlobalSymbolRegistry> = RefCell::new(GlobalSymbolRegistry::new());
}
@ -276,7 +278,7 @@ impl Symbol {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let sym = args.get(0).cloned().unwrap_or_default();
let sym = args.get_or_undefined(0);
// 1. If Type(sym) is not Symbol, throw a TypeError exception.
if let Some(sym) = sym.as_symbol() {
// 2. For each element e of the GlobalSymbolRegistry List (see 20.4.2.2), do

3
boa/src/class.rs

@ -7,6 +7,7 @@
//!# class::{Class, ClassBuilder},
//!# gc::{Finalize, Trace},
//!# Context, JsResult, JsValue,
//!# builtins::JsArgs,
//!# };
//!#
//! // This does not have to be an enum it can also be a struct.
@ -27,7 +28,7 @@
//! // This is what is called when we do `new Animal()`
//! fn constructor(_this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<Self> {
//! // This is equivalent to `String(arg)`.
//! let kind = args.get(0).cloned().unwrap_or_default().to_string(context)?;
//! let kind = args.get_or_undefined(0).to_string(context)?;
//!
//! let animal = match kind.as_str() {
//! "cat" => Self::Cat,

Loading…
Cancel
Save