mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: bind 的 length/name 改自身门与传播读,getter 抛错原值上抛
This commit is contained in:
@@ -254,6 +254,10 @@ pub fn function_apply<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
|
||||
/// `Function.prototype.bind(thisArg, ...args)`:返回绑定 this 与前置实参的新包装函数,
|
||||
/// 调用时通过 `bind_dispatcher` 把绑定实参拼到调用实参前转发到原目标。非函数目标抛 TypeError。
|
||||
///
|
||||
/// length = max(0, target.length - 绑定实参数):仅当 target 自有 length 且为 Number
|
||||
/// 时读(传播读,getter 抛错原值传播),否则 0。name = "bound " + target.name
|
||||
/// (传播读,非字符串 → 空串)。
|
||||
pub fn function_bind<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if args.is_empty() {
|
||||
return NativeResult::Err(crate::error::create_type_error(
|
||||
@@ -298,12 +302,20 @@ pub fn function_bind<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
// 全部 shape 属性(length/name)须先于状态数据定义完成,保证 shape 槽位
|
||||
// 与 dense 下标对齐,再 push [target, thisArg, ...boundArgs]。
|
||||
unsafe {
|
||||
let wrapper_ref = &mut *wrapper;
|
||||
// length:HasOwnProperty 门(非自身 → 0)+ 传播 Get(getter 抛错原值
|
||||
// 传播)+ Number 类型门(非 Number → 0)。
|
||||
// SAFETY: target_val 上方已校验为非空函数对象指针;读取即时消费,不跨 GC/reset。
|
||||
let target_obj = &*target_val.as_js_object_ptr();
|
||||
let target_length = vm
|
||||
.resolve_property(target_obj, length_si)
|
||||
.map(to_integer_or_infinity)
|
||||
.unwrap_or(0.0);
|
||||
let mut target_length = 0.0;
|
||||
if vm.get_own_property_slot(target_obj, length_si).is_some() {
|
||||
let own_length = match vm.ordinary_get(target_obj, length_si, target_val) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return NativeResult::Err(to_string_error_value(vm, &e)),
|
||||
};
|
||||
if own_length.js_type().is_number() {
|
||||
target_length = to_integer_or_infinity(own_length);
|
||||
}
|
||||
}
|
||||
// length = max(0, target.length - boundArgs);target.length 为 +∞ 时保持 +∞。
|
||||
let length = if target_length == f64::INFINITY {
|
||||
f64::INFINITY
|
||||
@@ -316,14 +328,20 @@ pub fn function_bind<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
} else {
|
||||
JsValue::float(length)
|
||||
};
|
||||
// 传播读后重取包装器指针。
|
||||
let wrapper_ref = &mut *wrapper;
|
||||
if let Err(e) = vm.define_data_property(wrapper_ref, length_si, length_val, attrs) {
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, &e));
|
||||
}
|
||||
let target_name = vm
|
||||
.resolve_property(target_obj, name_si)
|
||||
.and_then(|v| vm.lookup_str(v))
|
||||
.unwrap_or_default();
|
||||
// name:传播 Get(getter 抛错原值传播),非字符串 → 空串。
|
||||
let target_obj = &*target_val.as_js_object_ptr();
|
||||
let target_name = match vm.ordinary_get(target_obj, name_si, target_val) {
|
||||
Ok(v) => vm.lookup_str(v).unwrap_or_default(),
|
||||
Err(e) => return NativeResult::Err(to_string_error_value(vm, &e)),
|
||||
};
|
||||
let name_val = vm.new_string(&format!("bound {target_name}"));
|
||||
// 传播读后重取包装器指针。
|
||||
let wrapper_ref = &mut *wrapper;
|
||||
if let Err(e) = vm.define_data_property(wrapper_ref, name_si, name_val, attrs) {
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, &e));
|
||||
}
|
||||
|
||||
@@ -342,3 +342,45 @@ fn bound_function_newtarget_pins() {
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bound_function_length_name_pins() {
|
||||
let mut vm = Vm::new();
|
||||
// length:非自身 length(原型链 42 泄漏形态)→ 0;自身非 Number → 0;
|
||||
// 自身 Number 经 ToIntegerOrInfinity 减绑定实参数。
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"function bar() {} Object.setPrototypeOf(bar, {length: 42}); delete bar.length; Function.prototype.bind.call(bar, null, 1).length",
|
||||
)
|
||||
.unwrap();
|
||||
assert_num(result, 0.0);
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"function t() {} Object.defineProperty(t, 'length', {value: '5'}); t.bind(null, 1).length",
|
||||
)
|
||||
.unwrap();
|
||||
assert_num(result, 0.0);
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"function t() {} Object.defineProperty(t, 'length', {value: 3.66}); t.bind().length",
|
||||
)
|
||||
.unwrap();
|
||||
assert_num(result, 3.0);
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"function t() {} Object.defineProperty(t, 'length', {value: Infinity}); t.bind().length",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_double() == f64::INFINITY);
|
||||
// name:getter 抛错须原值传播;chained 读 bound 包装自身 name。
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var threw = false; try { Object.defineProperty(function(){}, 'name', {get: function() { throw new Error('x'); }}).bind(); } catch (e) { threw = e.message === 'x'; } threw",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
let result = eval(&mut vm, "function target() {} target.bind().name").unwrap();
|
||||
assert_eq!(vm.lookup_str(result).unwrap_or_default(), "bound target");
|
||||
let result = eval(&mut vm, "function target() {} target.bind().bind().name").unwrap();
|
||||
assert_eq!(vm.lookup_str(result).unwrap_or_default(), "bound bound target");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user