use crate::{
builtins::{
function::NativeFunctionData,
object::{Object, ObjectKind, PROTOTYPE},
value::{to_value, ResultValue, Value, ValueData},
},
exec::Interpreter,
};
use std::{borrow::Borrow, ops::Deref};
pub fn construct_boolean(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.set_kind(ObjectKind::Boolean);
match args.get(0) {
Some(ref value) => {
this.set_internal_slot("BooleanData", to_boolean(value));
}
None => {
this.set_internal_slot("BooleanData", to_boolean(&to_value(false)));
}
}
Ok(this.clone())
}
pub fn call_boolean(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
match args.get(0) {
Some(ref value) => Ok(to_boolean(value)),
None => Ok(to_boolean(&to_value(false))),
}
}
pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let b = this_boolean_value(this);
Ok(to_value(b.to_string()))
}
pub fn value_of(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(this_boolean_value(this))
}
pub fn create_constructor(global: &Value) -> Value {
let mut boolean = Object::default();
boolean.kind = ObjectKind::Function;
boolean.set_internal_method("construct", construct_boolean);
boolean.set_internal_method("call", call_boolean);
let boolean_prototype = ValueData::new_obj(Some(global));
boolean_prototype.set_internal_slot("BooleanData", to_boolean(&to_value(false)));
make_builtin_fn!(to_string, named "toString", of boolean_prototype);
make_builtin_fn!(value_of, named "valueOf", of boolean_prototype);
let boolean_value = to_value(boolean);
boolean_prototype.set_field_slice("constructor", to_value(boolean_value.clone()));
boolean_value.set_field_slice(PROTOTYPE, boolean_prototype);
boolean_value
}
pub fn to_boolean(value: &Value) -> Value {
match *value.deref().borrow() {
ValueData::Object(_) => to_value(true),
ValueData::String(ref s) if !s.is_empty() => to_value(true),
ValueData::Number(n) if n != 0.0 && !n.is_nan() => to_value(true),
ValueData::Integer(n) if n != 0 => to_value(true),
ValueData::Boolean(v) => to_value(v),
_ => to_value(false),
}
}
pub fn this_boolean_value(value: &Value) -> Value {
match *value.deref().borrow() {
ValueData::Boolean(v) => to_value(v),
ValueData::Object(ref v) => (v).deref().borrow().get_internal_slot("BooleanData"),
_ => to_value(false),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::exec::Executor;
use crate::realm::Realm;
use crate::{builtins::value::same_value, forward, forward_val};
#[test]
fn check_boolean_constructor_is_function() {
let global = ValueData::new_obj(None);
let boolean_constructor = create_constructor(&global);
assert_eq!(boolean_constructor.is_function(), true);
}
#[allow(clippy::result_unwrap_used)]
#[test]
fn construct_and_call() {
let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#"
var one = new Boolean(1);
var zero = Boolean(0);
"#;
forward(&mut engine, init);
let one = forward_val(&mut engine, "one").unwrap();
let zero = forward_val(&mut engine, "zero").unwrap();
assert_eq!(one.is_object(), true);
assert_eq!(zero.is_boolean(), true);
}
#[test]
fn constructor_gives_true_instance() {
let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#"
var trueVal = new Boolean(true);
var trueNum = new Boolean(1);
var trueString = new Boolean("true");
var trueBool = new Boolean(trueVal);
"#;
forward(&mut engine, init);
let true_val = forward_val(&mut engine, "trueVal").expect("value expected");
let true_num = forward_val(&mut engine, "trueNum").expect("value expected");
let true_string = forward_val(&mut engine, "trueString").expect("value expected");
let true_bool = forward_val(&mut engine, "trueBool").expect("value expected");
assert_eq!(true_val.is_object(), true);
assert_eq!(true_num.is_object(), true);
assert_eq!(true_string.is_object(), true);
assert_eq!(true_bool.is_object(), true);
assert_eq!(true_val.is_true(), true);
assert_eq!(true_num.is_true(), true);
assert_eq!(true_string.is_true(), true);
assert_eq!(true_bool.is_true(), true);
}
#[test]
fn instances_have_correct_proto_set() {
let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#"
var boolInstance = new Boolean(true);
var boolProto = Boolean.prototype;
"#;
forward(&mut engine, init);
let bool_instance = forward_val(&mut engine, "boolInstance").expect("value expected");
let bool_prototype = forward_val(&mut engine, "boolProto").expect("value expected");
assert!(same_value(
&bool_instance.get_internal_slot("__proto__"),
&bool_prototype,
true
));
}
}