Skip to content

Commit

Permalink
Implement JsValue::Invoke
Browse files Browse the repository at this point in the history
  • Loading branch information
jedel1043 committed Sep 18, 2021
1 parent 3e9e75c commit 33d900f
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 74 deletions.
8 changes: 4 additions & 4 deletions boa/src/builtins/iterable/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,11 @@ impl JsValue {
// a. If hint is async, then
if hint == IteratorHint::Async {
// i. Set method to ? GetMethod(obj, @@asyncIterator).
let method = self.get_method(context, WellKnownSymbols::async_iterator())?;
let method = self.get_method(WellKnownSymbols::async_iterator(), context)?;
// ii. If method is undefined, then
if method.is_undefined() {
// 1. Let syncMethod be ? GetMethod(obj, @@iterator).
let sync_method = self.get_method(context, WellKnownSymbols::iterator())?;
let sync_method = self.get_method(WellKnownSymbols::iterator(), context)?;
// 2. Let syncIteratorRecord be ? GetIterator(obj, sync, syncMethod).
let _sync_iterator_record =
self.get_iterator(context, Some(IteratorHint::Sync), Some(sync_method));
Expand All @@ -145,7 +145,7 @@ impl JsValue {
method
} else {
// b. Otherwise, set method to ? GetMethod(obj, @@iterator).
self.get_method(context, WellKnownSymbols::iterator())?
self.get_method(WellKnownSymbols::iterator(), context)?
}
};

Expand All @@ -158,7 +158,7 @@ impl JsValue {
}

// 5. Let nextMethod be ? GetV(iterator, "next").
let next_method = iterator.get_v(context, "next")?;
let next_method = iterator.get_v("next", context)?;

// 6. Let iteratorRecord be the Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
// 7. Return iteratorRecord.
Expand Down
23 changes: 9 additions & 14 deletions boa/src/builtins/string/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,7 @@ impl String {
// 2. If searchValue is neither undefined nor null, then
if !search_value.is_null_or_undefined() {
// a. Let replacer be ? GetMethod(searchValue, @@replace).
let replacer = search_value.get_method(context, WellKnownSymbols::replace())?;
let replacer = search_value.get_method(WellKnownSymbols::replace(), context)?;

// b. If replacer is not undefined, then
if !replacer.is_undefined() {
Expand Down Expand Up @@ -859,7 +859,7 @@ impl String {
}

// c. Let replacer be ? GetMethod(searchValue, @@replace).
let replacer = search_value.get_method(context, WellKnownSymbols::replace())?;
let replacer = search_value.get_method(WellKnownSymbols::replace(), context)?;

// d. If replacer is not undefined, then
if !replacer.is_undefined() {
Expand Down Expand Up @@ -1097,7 +1097,7 @@ impl String {
let regexp = args.get_or_undefined(0);
if !regexp.is_null_or_undefined() {
// a. Let matcher be ? GetMethod(regexp, @@match).
let matcher = regexp.get_method(context, WellKnownSymbols::r#match())?;
let matcher = regexp.get_method(WellKnownSymbols::r#match(), context)?;
// b. If matcher is not undefined, then
if !matcher.is_undefined() {
// i. Return ? Call(matcher, regexp, « O »).
Expand All @@ -1112,9 +1112,7 @@ impl String {
let rx = RegExp::create(regexp.clone(), JsValue::undefined(), context)?;

// 5. Return ? Invoke(rx, @@match, « S »).
let obj = rx.to_object(context)?;
let func = obj.get(WellKnownSymbols::r#match(), context)?;
context.call(&func, &obj.into(), &[JsValue::new(s)])
rx.invoke(WellKnownSymbols::r#match(), &[JsValue::new(s)], context)
}

/// Abstract method `StringPad`.
Expand Down Expand Up @@ -1464,7 +1462,7 @@ impl String {
// 2. If separator is neither undefined nor null, then
if !separator.is_null_or_undefined() {
// a. Let splitter be ? GetMethod(separator, @@split).
let splitter = separator.get_method(context, WellKnownSymbols::split())?;
let splitter = separator.get_method(WellKnownSymbols::split(), context)?;
// b. If splitter is not undefined, then
if !splitter.is_undefined() {
// i. Return ? Call(splitter, separator, « O, limit »).
Expand Down Expand Up @@ -1651,7 +1649,7 @@ impl String {
}

// c. Let matcher be ? GetMethod(regexp, @@matchAll).
let matcher = regexp.get_method(context, WellKnownSymbols::match_all())?;
let matcher = regexp.get_method(WellKnownSymbols::match_all(), context)?;
// d. If matcher is not undefined, then
if !matcher.is_undefined() {
// i. Return ? Call(matcher, regexp, « O »).
Expand All @@ -1666,9 +1664,7 @@ impl String {
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");
let func = obj.get(WellKnownSymbols::match_all(), context)?;
obj.call(&func, &[JsValue::new(s)], context)
rx.invoke(WellKnownSymbols::match_all(), &[JsValue::new(s)], context)
}

/// `String.prototype.normalize( [ form ] )`
Expand Down Expand Up @@ -1731,7 +1727,7 @@ impl String {
let regexp = args.get_or_undefined(0);
if !regexp.is_null_or_undefined() {
// a. Let searcher be ? GetMethod(regexp, @@search).
let searcher = regexp.get_method(context, WellKnownSymbols::search())?;
let searcher = regexp.get_method(WellKnownSymbols::search(), context)?;
// b. If searcher is not undefined, then
if !searcher.is_undefined() {
// i. Return ? Call(searcher, regexp, « O »).
Expand All @@ -1746,8 +1742,7 @@ impl String {
let rx = RegExp::create(regexp.clone(), JsValue::undefined(), context)?;

// 5. Return ? Invoke(rx, @@search, « string »).
let func = rx.get_v(context, WellKnownSymbols::search())?;
context.call(&func, &rx, &[JsValue::new(string)])
rx.invoke(WellKnownSymbols::search(), &[JsValue::new(string)], context)
}

pub(crate) fn iterator(
Expand Down
2 changes: 1 addition & 1 deletion boa/src/builtins/typed_array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ macro_rules! typed_array {
// 2. Let usingIterator be ? GetMethod(firstArgument, @@iterator).
let first_argument = JsValue::from(first_argument);
let using_iterator =
first_argument.get_method(context, WellKnownSymbols::replace())?;
first_argument.get_method(WellKnownSymbols::replace(), context)?;

// 3. If usingIterator is not undefined, then
if !using_iterator.is_undefined() {
Expand Down
78 changes: 76 additions & 2 deletions boa/src/object/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,9 +544,58 @@ impl JsObject {
}

impl JsValue {
// todo: GetV
/// Abstract operation `GetV ( V, P )`.
///
/// Retrieves the value of a specific property of an ECMAScript language value. If the value is
/// not an object, the property lookup is performed using a wrapper object appropriate for the
/// type of the value.
///
/// More information:
/// - [EcmaScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-getmethod
#[inline]
pub(crate) fn get_v<K>(&self, key: K, context: &mut Context) -> JsResult<JsValue>
where
K: Into<PropertyKey>,
{
// 1. Let O be ? ToObject(V).
let o = self.to_object(context)?;

// 2. Return ? O.[[Get]](P, V).
o.__get__(&key.into(), self.clone(), context)
}

/// Abstract operation `GetMethod ( V, P )`
///
/// Retrieves the value of a specific property, when the value of the property is expected to be a function.
///
/// More information:
/// - [EcmaScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-getmethod
pub(crate) fn get_method<K>(&self, key: K, context: &mut Context) -> JsResult<JsValue>
where
K: Into<PropertyKey>,
{
// 1. Assert: IsPropertyKey(P) is true.
// 2. Let func be ? GetV(V, P).
let func = self.get_v(key, context)?;

// todo: GetMethod
// 3. If func is either undefined or null, return undefined.
if func.is_null_or_undefined() {
return Ok(JsValue::undefined());
}

// 4. If IsCallable(func) is false, throw a TypeError exception.
if !func.is_callable() {
Err(context
.construct_type_error("value returned for property of object is not a function"))
} else {
// 5. Return func.
Ok(func)
}
}

/// It is used to create List value whose elements are provided by the indexed properties of
/// self.
Expand Down Expand Up @@ -605,4 +654,29 @@ impl JsValue {
// 7. Return list.
Ok(list)
}

/// Abstract operation `( V, P [ , argumentsList ] )
///
/// Calls a method property of an ECMAScript language value.
///
/// More information:
/// - [EcmaScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-invoke
pub(crate) fn invoke<K>(
&self,
key: K,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue>
where
K: Into<PropertyKey>,
{
// 1. If argumentsList is not present, set argumentsList to a new empty List.
// 2. Let func be ? GetV(V, P).
let func = self.get_v(key, context)?;

// 3. Return ? Call(func, V, argumentsList)
context.call(&func, self, args)
}
}
53 changes: 1 addition & 52 deletions boa/src/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ impl JsValue {
// 2. If Type(input) is Object, then
if self.is_object() {
// a. Let exoticToPrim be ? GetMethod(input, @@toPrimitive).
let exotic_to_prim = self.get_method(context, WellKnownSymbols::to_primitive())?;
let exotic_to_prim = self.get_method(WellKnownSymbols::to_primitive(), context)?;

// b. If exoticToPrim is not undefined, then
if !exotic_to_prim.is_undefined() {
Expand Down Expand Up @@ -1038,57 +1038,6 @@ impl JsValue {
}
}

/// Retrieves value of specific property, when the value of the property is expected to be a function.
///
/// More information:
/// - [EcmaScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-getmethod
pub(crate) fn get_method<K>(&self, context: &mut Context, key: K) -> JsResult<JsValue>
where
K: Into<PropertyKey>,
{
// 1. Assert: IsPropertyKey(P) is true.
// 2. Let func be ? GetV(V, P).
let func = self.get_v(context, key)?;

// 3. If func is either undefined or null, return undefined.
if func.is_null_or_undefined() {
return Ok(JsValue::undefined());
}

// 4. If IsCallable(func) is false, throw a TypeError exception.
if !func.is_callable() {
Err(context
.construct_type_error("value returned for property of object is not a function"))
} else {
// 5. Return func.
Ok(func)
}
}

/// The `GetV ( V, P )` abstract operation
///
/// Retrieves the value of a specific property of an ECMAScript language value. If the value is
/// not an object, the property lookup is performed using a wrapper object appropriate for the
/// type of the value.
///
/// More information:
/// - [EcmaScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-getmethod
#[inline]
pub(crate) fn get_v<K>(&self, context: &mut Context, key: K) -> JsResult<JsValue>
where
K: Into<PropertyKey>,
{
// 1. Let O be ? ToObject(V).
let o = self.to_object(context)?;

// 2. Return ? O.[[Get]](P, V).
o.__get__(&key.into(), self.clone(), context)
}

/// It determines if the value is a callable function with a `[[Call]]` internal method.
///
/// More information:
Expand Down
2 changes: 1 addition & 1 deletion boa/src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ impl<'a> Vm<'a> {
target.type_of()
)));
};
let handler = target.get_method(self.context, WellKnownSymbols::has_instance())?;
let handler = target.get_method(WellKnownSymbols::has_instance(), self.context)?;
if !handler.is_undefined() {
let value = self
.context
Expand Down

0 comments on commit 33d900f

Please sign in to comment.