mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-26 19:51:11 +08:00
fix: instanceof 检查右侧可调用性并投递原生异常
This commit is contained in:
+1
-1
@@ -25,7 +25,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.19.1"
|
version = "0.19.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
rust-version = "1.80"
|
rust-version = "1.80"
|
||||||
|
|||||||
@@ -89,11 +89,12 @@ fn to_string_error_value<H: VmHost>(vm: &mut H, msg: &str) -> JsValue {
|
|||||||
/// 1. this(C)非可调用 → false(非对象或非函数对象,不抛)
|
/// 1. this(C)非可调用 → false(非对象或非函数对象,不抛)
|
||||||
/// 2. C 是 bound 包装 → 逐层解包 [[BoundTargetFunction]],按最内层 target 判定
|
/// 2. C 是 bound 包装 → 逐层解包 [[BoundTargetFunction]],按最内层 target 判定
|
||||||
/// 3. 实参(O)非对象 → false
|
/// 3. 实参(O)非对象 → false
|
||||||
/// 4. C.prototype 不是对象 → TypeError
|
/// 4. C.prototype 读取(传播读,触发 getter);非对象 → TypeError
|
||||||
/// 5. 沿 O 原型链与 C.prototype 指针比对(深度上限防环)
|
/// 5. 沿 O 原型链与 C.prototype 指针比对(深度上限防环)
|
||||||
///
|
///
|
||||||
/// # 边界与前提
|
/// # 边界与前提
|
||||||
/// - 唯一抛错点:C 可调用但其 `prototype` 属性非对象(含无 prototype 的 native 方法)
|
/// - 唯一抛错点:C 可调用但其 `prototype` 属性非对象(含无 prototype 的 native 方法)
|
||||||
|
/// 或 prototype getter 抛错(原值经 uncaught 侧通道恢复投递)
|
||||||
/// - bound 链解包后 prototype 取最内层 target 的(bound 包装自身无 prototype)
|
/// - bound 链解包后 prototype 取最内层 target 的(bound 包装自身无 prototype)
|
||||||
/// - 无实参调用(O 缺失)视为 undefined → false
|
/// - 无实参调用(O 缺失)视为 undefined → false
|
||||||
pub fn function_symbol_has_instance<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
pub fn function_symbol_has_instance<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
@@ -129,13 +130,12 @@ pub fn function_symbol_has_instance<H: VmHost>(vm: &mut H, args: &[u8]) -> Nativ
|
|||||||
return NativeResult::Ok(JsValue::bool(false));
|
return NativeResult::Ok(JsValue::bool(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
// prototype 非对象 → TypeError(OrdinaryHasInstance 唯一抛错点)。
|
// prototype 非对象 → TypeError(OrdinaryHasInstance 唯一抛错点);
|
||||||
|
// 传播读触发 getter,getter 抛错时原值经 uncaught 侧通道恢复投递。
|
||||||
let proto_si = vm.perm_intern("prototype");
|
let proto_si = vm.perm_intern("prototype");
|
||||||
let Some(proto_val) = vm.resolve_property(c_obj, proto_si) else {
|
let proto_val = match vm.ordinary_get(c_obj, proto_si, c) {
|
||||||
return NativeResult::Err(crate::error::create_type_error(
|
Ok(v) => v,
|
||||||
vm,
|
Err(e) => return NativeResult::Err(to_string_error_value(vm, &e)),
|
||||||
"Function has non-object prototype in instanceof check",
|
|
||||||
));
|
|
||||||
};
|
};
|
||||||
if !proto_val.is_object() {
|
if !proto_val.is_object() {
|
||||||
return NativeResult::Err(crate::error::create_type_error(
|
return NativeResult::Err(crate::error::create_type_error(
|
||||||
|
|||||||
@@ -75,6 +75,16 @@ pub(crate) fn make_named_pair(
|
|||||||
make_pair(string_forge, shape_forge, name, labels.prototype, labels.constructor, labels.name)
|
make_pair(string_forge, shape_forge, name, labels.prototype, labels.constructor, labels.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 把 Function.prototype 标成函数对象:规范规定其是 function object
|
||||||
|
/// (IsCallable 为真),typeof 报 "function"、Object.prototype.toString
|
||||||
|
/// 品牌表报 "[object Function]";调用/构造仍因无函数体抛 TypeError。
|
||||||
|
pub(crate) fn tag_function_proto(proto: &P<JsObject>) {
|
||||||
|
let ptr = proto.as_ptr() as *mut JsObject;
|
||||||
|
// SAFETY: proto 是 make_named_pair 刚建的本进程对象,P 引用与裸指针同址。
|
||||||
|
let obj = unsafe { &mut *ptr };
|
||||||
|
obj.set_function(true);
|
||||||
|
}
|
||||||
|
|
||||||
/// 把 Number.prototype 标成 Number 对象本体:规范 21.7.3 规定其是
|
/// 把 Number.prototype 标成 Number 对象本体:规范 21.7.3 规定其是
|
||||||
/// [[NumberData]] = +0 的 Number object。type_tag 是 Object.prototype.toString
|
/// [[NumberData]] = +0 的 Number object。type_tag 是 Object.prototype.toString
|
||||||
/// 品牌表的判据(置位后报 "[object Number]"),boxed 值供 thisNumberValue/
|
/// 品牌表的判据(置位后报 "[object Number]"),boxed 值供 thisNumberValue/
|
||||||
@@ -483,6 +493,7 @@ impl BuiltinWorld {
|
|||||||
let (object_proto, object_constructor) = make_named_pair(string_forge, shape_forge, labels, "Object");
|
let (object_proto, object_constructor) = make_named_pair(string_forge, shape_forge, labels, "Object");
|
||||||
let (array_proto, array_constructor) = make_named_pair(string_forge, shape_forge, labels, "Array");
|
let (array_proto, array_constructor) = make_named_pair(string_forge, shape_forge, labels, "Array");
|
||||||
let (function_proto, function_constructor) = make_named_pair(string_forge, shape_forge, labels, "Function");
|
let (function_proto, function_constructor) = make_named_pair(string_forge, shape_forge, labels, "Function");
|
||||||
|
tag_function_proto(&function_proto);
|
||||||
let (string_proto, string_constructor) = make_named_pair(string_forge, shape_forge, labels, "String");
|
let (string_proto, string_constructor) = make_named_pair(string_forge, shape_forge, labels, "String");
|
||||||
tag_string_proto(&string_proto, string_forge, shape_forge);
|
tag_string_proto(&string_proto, string_forge, shape_forge);
|
||||||
let (number_proto, number_constructor) = make_named_pair(string_forge, shape_forge, labels, "Number");
|
let (number_proto, number_constructor) = make_named_pair(string_forge, shape_forge, labels, "Number");
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ use oxide_types::object::JsObject;
|
|||||||
use oxide_types::value::JsValue;
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
use super::construct::{
|
use super::construct::{
|
||||||
builtin_labels, make_error_subtypes, make_named_pair, make_typed_array_family, tag_boolean_proto, tag_number_proto,
|
builtin_labels, make_error_subtypes, make_named_pair, make_typed_array_family, tag_boolean_proto,
|
||||||
tag_string_proto, wire_builtin_world_links, ErrorSubtypeProtos, TypedArrayFamily,
|
tag_function_proto, tag_number_proto, tag_string_proto, wire_builtin_world_links, ErrorSubtypeProtos,
|
||||||
|
TypedArrayFamily,
|
||||||
};
|
};
|
||||||
use super::{BindEnv, BuiltinWorld};
|
use super::{BindEnv, BuiltinWorld};
|
||||||
use crate::kernel::BuiltinDirtySet;
|
use crate::kernel::BuiltinDirtySet;
|
||||||
@@ -173,7 +174,11 @@ impl BuiltinWorld {
|
|||||||
(current.array_proto.clone(), current.array_constructor.clone())
|
(current.array_proto.clone(), current.array_constructor.clone())
|
||||||
};
|
};
|
||||||
let (function_proto, function_constructor) = if dirty.function {
|
let (function_proto, function_constructor) = if dirty.function {
|
||||||
make_named_pair(string_forge, shape_forge, labels, "Function")
|
// 脏重建与全量构造同形:原型本体须带函数对象标志,
|
||||||
|
// 漏此分支 full_reset 后 IsCallable(Function.prototype) 翻回假。
|
||||||
|
let (proto, ctor) = make_named_pair(string_forge, shape_forge, labels, "Function");
|
||||||
|
tag_function_proto(&proto);
|
||||||
|
(proto, ctor)
|
||||||
} else {
|
} else {
|
||||||
(current.function_proto.clone(), current.function_constructor.clone())
|
(current.function_proto.clone(), current.function_constructor.clone())
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -46,7 +46,9 @@ fn test_prototypes_are_not_functions() {
|
|||||||
let w = make_world();
|
let w = make_world();
|
||||||
assert!(!w.object_proto.is_function());
|
assert!(!w.object_proto.is_function());
|
||||||
assert!(!w.array_proto.is_function());
|
assert!(!w.array_proto.is_function());
|
||||||
assert!(!w.function_proto.is_function());
|
// Function.prototype 是函数对象(规范规定其 IsCallable 为真),
|
||||||
|
// 其余原型均非函数。
|
||||||
|
assert!(w.function_proto.is_function());
|
||||||
assert!(!w.string_proto.is_function());
|
assert!(!w.string_proto.is_function());
|
||||||
assert!(!w.number_proto.is_function());
|
assert!(!w.number_proto.is_function());
|
||||||
assert!(!w.boolean_proto.is_function());
|
assert!(!w.boolean_proto.is_function());
|
||||||
|
|||||||
@@ -206,13 +206,11 @@ impl Vm {
|
|||||||
)?;
|
)?;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
} else {
|
} else {
|
||||||
let error = oxide_builtins::error::create_from_text(
|
// 无函数体的函数对象(如 Function.prototype):IsConstructor 为假,
|
||||||
self,
|
// 按规范抛 TypeError(此前该分支不可达,报普通 Error 属内部错误)。
|
||||||
"NEW_EXPRESSION: bytecode constructors not yet supported",
|
self
|
||||||
);
|
.raise_type_error("NEW_EXPRESSION: constructor is not a constructor")
|
||||||
self.exception_value = Some(error);
|
.map(|_| true)
|
||||||
self.pending_error_kind = Some(self.thrown_error_kind(error));
|
|
||||||
self.unwind().map(|_| true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,17 +409,31 @@ impl Vm {
|
|||||||
let has_instance_si = encode_symbol_key(self.realm_id(), WELL_KNOWN_SYMBOL_HAS_INSTANCE);
|
let has_instance_si = encode_symbol_key(self.realm_id(), WELL_KNOWN_SYMBOL_HAS_INSTANCE);
|
||||||
|
|
||||||
let ctor_obj = unsafe { &*rhs_val.as_js_object_ptr() };
|
let ctor_obj = unsafe { &*rhs_val.as_js_object_ptr() };
|
||||||
let has_instance_val = self.ordinary_get(ctor_obj, has_instance_si, rhs_val)?;
|
// 传播读触发 getter;getter 抛错且外围 catch 接手时 ordinary_get 返回
|
||||||
|
// catch 参数值(pc 已改写),按 pc 变化感知并停流,异常由展开送达。
|
||||||
|
let pc_before = self.pc;
|
||||||
|
let has_instance_val = match self.ordinary_get(ctor_obj, has_instance_si, rhs_val) {
|
||||||
|
Ok(v) if self.pc == pc_before => v,
|
||||||
|
Ok(_) => return Ok(()),
|
||||||
|
Err(e) => return self.raise_call_error(&e).map(|_| ()),
|
||||||
|
};
|
||||||
|
// GetMethod 语义:null/undefined 均视为无 handler。handler 在场须可调用,
|
||||||
|
// 否则 Call(null) 抛 TypeError;调用异常原值上抛(外围 try/catch 可捕获)。
|
||||||
if !has_instance_val.is_undefined() && !has_instance_val.is_null() {
|
if !has_instance_val.is_undefined() && !has_instance_val.is_null() {
|
||||||
let fn_ptr = has_instance_val.as_js_object_ptr();
|
if !is_callable(has_instance_val) {
|
||||||
if !fn_ptr.is_null() {
|
return self.raise_type_error("instanceof: @@hasInstance handler is not callable");
|
||||||
let fn_obj = unsafe { &*fn_ptr };
|
|
||||||
if fn_obj.is_function() {
|
|
||||||
let result = self.call_function_sync(has_instance_val, rhs_val, &[lhs_val])?;
|
|
||||||
self.regs[rd] = JsValue::bool(to_boolean(result));
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let result = match self.call_function_sync(has_instance_val, rhs_val, &[lhs_val]) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => return self.raise_call_error(&e).map(|_| ()),
|
||||||
|
};
|
||||||
|
self.regs[rd] = JsValue::bool(to_boolean(result));
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无 handler 时 RHS 不可调用即 TypeError(先于左操作数类型检查)。
|
||||||
|
if !is_callable(rhs_val) {
|
||||||
|
return self.raise_type_error("INSTANCEOF right-hand side is not callable");
|
||||||
}
|
}
|
||||||
|
|
||||||
if !lhs_val.is_object() {
|
if !lhs_val.is_object() {
|
||||||
@@ -431,15 +443,18 @@ impl Vm {
|
|||||||
|
|
||||||
let rhs_obj = unsafe { &*rhs_val.as_js_object_ptr() };
|
let rhs_obj = unsafe { &*rhs_val.as_js_object_ptr() };
|
||||||
let proto_si = self.kernel_core.perm_interner().intern("prototype").0;
|
let proto_si = self.kernel_core.perm_interner().intern("prototype").0;
|
||||||
let ctor_proto = self.resolve_property(rhs_obj, proto_si);
|
// 传播读:getter 异常原值上抛(深度 0 已就地展开的由 pc 变化感知)。
|
||||||
|
let pc_before = self.pc;
|
||||||
let ctor_proto_ptr = match ctor_proto {
|
let ctor_proto = match self.ordinary_get(rhs_obj, proto_si, rhs_val) {
|
||||||
Some(v) if v.is_object() => v.as_js_object_ptr(),
|
Ok(v) if self.pc == pc_before => v,
|
||||||
_ => {
|
Ok(_) => return Ok(()),
|
||||||
self.regs[rd] = JsValue::bool(false);
|
Err(e) => return self.raise_call_error(&e).map(|_| ()),
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
// prototype 非对象 → TypeError。
|
||||||
|
if !ctor_proto.is_object() {
|
||||||
|
return self.raise_type_error("instanceof: prototype is not an object");
|
||||||
|
}
|
||||||
|
let ctor_proto_ptr = ctor_proto.as_js_object_ptr();
|
||||||
|
|
||||||
let mut proto = unsafe { &*lhs_val.as_js_object_ptr() }.proto();
|
let mut proto = unsafe { &*lhs_val.as_js_object_ptr() }.proto();
|
||||||
let mut depth = 0usize;
|
let mut depth = 0usize;
|
||||||
|
|||||||
@@ -179,12 +179,16 @@ fn has_instance_overrides_instanceof() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn has_instance_non_callable_falls_through() {
|
fn has_instance_non_callable_handler_throws() {
|
||||||
let mut vm = Vm::new();
|
let mut vm = Vm::new();
|
||||||
|
// handler 在场但不可调用:Call(42) 抛 TypeError(旧行为静默回落
|
||||||
|
// prototype 分支,与规范步 4 不符)。
|
||||||
let source = r#"
|
let source = r#"
|
||||||
class D {}
|
class D {}
|
||||||
D[Symbol.hasInstance] = 42;
|
D[Symbol.hasInstance] = 42;
|
||||||
({} instanceof D) === false
|
var threw = false;
|
||||||
|
try { ({} ) instanceof D; } catch (e) { threw = e instanceof TypeError; }
|
||||||
|
threw
|
||||||
"#;
|
"#;
|
||||||
let result = eval(&mut vm, source).unwrap();
|
let result = eval(&mut vm, source).unwrap();
|
||||||
assert!(result.as_bool());
|
assert!(result.as_bool());
|
||||||
|
|||||||
Reference in New Issue
Block a user