Files
OxideJS-New/crates/oxide_builtins/src/boolean.rs
T

124 lines
4.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use num_traits::Zero;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use oxide_runtime_api::{NativeResult, ProtoKind, VmHost};
/// JS `Boolean()` 构造逻辑:把参数按 ToBoolean 语义转换。
/// 以普通函数调用时返回原始 bool;以 new 语义调用时返回 `[[BooleanData]]` 为结果的包装对象。
pub fn boolean_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let bool_val = if args.len() > 1 {
let arg = vm.reg(args[1]);
if arg.is_undefined() || arg.is_null() {
false
} else if arg.is_bool() {
arg.as_bool()
} else if arg.is_string() {
// SAFETY: arg 已确认是字符串值。
!unsafe { (*arg.as_string_ptr()).is_empty() }
} else if arg.is_object() {
// [[IsHTMLDDA]] 宿主对象按 undefined 处理(B.3.4),falsy。
// SAFETY: arg 已确认是对象值,借用即时消费。
!unsafe { (*arg.as_js_object_ptr()).is_html_dda_obj() }
} else if arg.is_int() {
arg.as_int() != 0
} else if arg.is_double() {
// NaN 按规范为 falsy(isNaN 与 != 0.0 的判定顺序即 ToBoolean 语义)。
!arg.as_double().is_nan() && arg.as_double() != 0.0
} else if arg.is_symbol() {
true
} else if arg.is_bigint() {
// SAFETY: arg 已确认是 BigInt 值,其 box 存活至 full_reset。
!unsafe { oxide_runtime_api::bigint_data(arg) }.is_zero()
} else {
false
}
} else {
false
};
let boolean_proto = vm.builtin_proto(ProtoKind::BooleanProto);
let is_ctor = if this_val.is_object() {
let ptr = this_val.as_js_object_ptr();
if ptr.is_null() {
false
} else {
let proto_ptr = unsafe { (*ptr).proto().as_js_object_ptr() };
!proto_ptr.is_null() && std::ptr::eq(proto_ptr, boolean_proto)
}
} else {
false
};
if !is_ctor {
return NativeResult::Ok(JsValue::bool(bool_val));
}
let obj = unsafe { &mut *this_val.as_js_object_ptr() };
obj.type_tag = JsObject::OBJ_TYPE_BOOLEAN_OBJ;
obj.set_boxed_value(JsValue::bool(bool_val));
NativeResult::Ok(this_val)
}
/// `Boolean.prototype.valueOf`:返回包装对象的原始 bool 值。
/// this 为原始 bool 时直接返回;否则必须是 Boolean 包装对象,否则抛 TypeError。
pub fn boolean_prototype_value_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
if this_val.is_bool() {
return NativeResult::Ok(this_val);
}
if !this_val.is_object() {
return NativeResult::Err(crate::error::create_type_error(
vm,
"Boolean.prototype.valueOf called on non-Boolean object",
));
}
let obj = unsafe { &*this_val.as_js_object_ptr() };
if !obj.is_boolean_obj() {
return NativeResult::Err(crate::error::create_type_error(
vm,
"Boolean.prototype.valueOf called on non-Boolean object",
));
}
let val = obj.boxed_value();
NativeResult::Ok(val)
}
/// `Boolean.prototype.toString`:返回 `"true"` 或 `"false"`。
/// 支持原始 bool 与 Boolean 包装对象;其它 this 抛 TypeError。
pub fn boolean_prototype_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
if this_val.is_bool() {
return if this_val.as_bool() {
NativeResult::Ok(vm.new_string("true"))
} else {
NativeResult::Ok(vm.new_string("false"))
};
}
if !this_val.is_object() {
return NativeResult::Err(crate::error::create_type_error(
vm,
"Boolean.prototype.toString called on non-Boolean object",
));
}
let obj = unsafe { &*this_val.as_js_object_ptr() };
if !obj.is_boolean_obj() {
return NativeResult::Err(crate::error::create_type_error(
vm,
"Boolean.prototype.toString called on non-Boolean object",
));
}
let val = obj.boxed_value();
let is_true = if val.is_bool() {
val.as_bool()
} else {
!val.is_null() && !val.is_undefined()
};
if is_true {
NativeResult::Ok(vm.new_string("true"))
} else {
NativeResult::Ok(vm.new_string("false"))
}
}