Browse Source

Implementation of `instanceof` operator (#908)

Co-authored-by: Andrej Onufrák <andrej.onufrak@student.tuke.sk>
pull/920/head
morrien 4 years ago committed by GitHub
parent
commit
f1d676e15b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      boa/src/builtins/symbol/mod.rs
  2. 61
      boa/src/object/gcobject.rs
  3. 3
      boa/src/object/mod.rs
  4. 19
      boa/src/object/tests.rs
  5. 23
      boa/src/syntax/ast/node/operator/bin_op/mod.rs
  6. 34
      boa/src/syntax/ast/node/operator/tests.rs

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

@ -113,7 +113,7 @@ impl WellKnownSymbols {
/// Called by the semantics of the instanceof operator.
#[inline]
pub fn has_instance_symbol(&self) -> RcSymbol {
self.async_iterator.clone()
self.has_instance.clone()
}
/// The `Symbol.isConcatSpreadable` well known symbol.

61
boa/src/object/gcobject.rs

@ -676,6 +676,67 @@ impl GcObject {
pub fn is_native_object(&self) -> bool {
self.borrow().is_native_object()
}
/// 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
#[inline]
pub fn get_method<K>(&self, context: &mut Context, key: K) -> Result<Option<GcObject>>
where
K: Into<PropertyKey>,
{
let key = key.into();
let value = self.get(&key);
if value.is_null_or_undefined() {
return Ok(None);
}
match value.as_object() {
Some(object) if object.is_callable() => Ok(Some(object)),
_ => Err(context
.construct_type_error("value returned for property of object is not a function")),
}
}
/// Determines if `value` inherits from the instance object inheritance path.
///
/// More information:
/// - [EcmaScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinaryhasinstance
#[inline]
pub fn ordinary_has_instance(&self, context: &mut Context, value: &Value) -> Result<bool> {
if !self.is_callable() {
return Ok(false);
}
// TODO: If C has a [[BoundTargetFunction]] internal slot, then
// Let BC be C.[[BoundTargetFunction]].
// Return ? InstanceofOperator(O, BC).
if let Some(object) = value.as_object() {
if let Some(prototype) = self.get(&"prototype".into()).as_object() {
let mut object = object.get_prototype_of();
while let Some(object_prototype) = object.as_object() {
if GcObject::equals(&prototype, &object_prototype) {
return Ok(true);
}
object = object_prototype.get_prototype_of();
}
Ok(false)
} else {
Err(context
.construct_type_error("function has non-object prototype in instanceof check"))
}
} else {
Ok(false)
}
}
}
impl AsRef<GcCell<Object>> for GcObject {

3
boa/src/object/mod.rs

@ -22,6 +22,9 @@ use std::{
ops::{Deref, DerefMut},
};
#[cfg(test)]
mod tests;
mod gcobject;
mod internal_methods;
mod iter;

19
boa/src/object/tests.rs

@ -0,0 +1,19 @@
use crate::exec;
#[test]
fn ordinary_has_instance_nonobject_prototype() {
let scenario = r#"
try {
function C() {}
C.prototype = 1
String instanceof C
} catch (err) {
err.toString()
}
"#;
assert_eq!(
&exec(scenario),
"\"TypeError: function has non-object prototype in instanceof check\""
);
}

23
boa/src/syntax/ast/node/operator/bin_op/mod.rs

@ -125,17 +125,28 @@ impl Executable for BinOp {
interpreter.has_property(&y, &key)
}
CompOp::InstanceOf => {
if !y.is_object() {
if let Some(object) = y.as_object() {
let key = interpreter.well_known_symbols().has_instance_symbol();
match object.get_method(interpreter, key)? {
Some(instance_of_handler) => instance_of_handler
.call(&y, &[x], interpreter)?
.to_boolean(),
None if object.is_callable() => {
object.ordinary_has_instance(interpreter, &x)?
}
None => {
return interpreter.throw_type_error(
"right-hand side of 'instanceof' is not callable",
);
}
}
} else {
return interpreter.throw_type_error(format!(
"right-hand side of 'instanceof' should be an object, got {}",
y.get_type().as_str()
));
}
// TODO: implement the instanceof operator
// spec: https://tc39.es/ecma262/#sec-instanceofoperator
false
}
}))
}

34
boa/src/syntax/ast/node/operator/tests.rs

@ -26,3 +26,37 @@ fn assignmentoperator_rhs_throws_error() {
assert_eq!(&exec(scenario), "\"ReferenceError: b is not defined\"");
}
#[test]
fn instanceofoperator_rhs_not_object() {
let scenario = r#"
try {
let s = new String();
s instanceof 1
} catch (err) {
err.toString()
}
"#;
assert_eq!(
&exec(scenario),
"\"TypeError: right-hand side of 'instanceof' should be an object, got number\""
);
}
#[test]
fn instanceofoperator_rhs_not_callable() {
let scenario = r#"
try {
let s = new String();
s instanceof {}
} catch (err) {
err.toString()
}
"#;
assert_eq!(
&exec(scenario),
"\"TypeError: right-hand side of 'instanceof' is not callable\""
);
}

Loading…
Cancel
Save