diff --git a/Cargo.toml b/Cargo.toml index 33f456a9..af42b1df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ members = [ ] [workspace.package] -version = "0.19.1" +version = "0.19.2" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_builtins/src/function.rs b/crates/oxide_builtins/src/function.rs index 812ec841..ad371452 100644 --- a/crates/oxide_builtins/src/function.rs +++ b/crates/oxide_builtins/src/function.rs @@ -89,11 +89,12 @@ fn to_string_error_value(vm: &mut H, msg: &str) -> JsValue { /// 1. this(C)非可调用 → false(非对象或非函数对象,不抛) /// 2. C 是 bound 包装 → 逐层解包 [[BoundTargetFunction]],按最内层 target 判定 /// 3. 实参(O)非对象 → false -/// 4. C.prototype 不是对象 → TypeError +/// 4. C.prototype 读取(传播读,触发 getter);非对象 → TypeError /// 5. 沿 O 原型链与 C.prototype 指针比对(深度上限防环) /// /// # 边界与前提 /// - 唯一抛错点:C 可调用但其 `prototype` 属性非对象(含无 prototype 的 native 方法) +/// 或 prototype getter 抛错(原值经 uncaught 侧通道恢复投递) /// - bound 链解包后 prototype 取最内层 target 的(bound 包装自身无 prototype) /// - 无实参调用(O 缺失)视为 undefined → false pub fn function_symbol_has_instance(vm: &mut H, args: &[u8]) -> NativeResult { @@ -129,13 +130,12 @@ pub fn function_symbol_has_instance(vm: &mut H, args: &[u8]) -> Nativ return NativeResult::Ok(JsValue::bool(false)); } - // prototype 非对象 → TypeError(OrdinaryHasInstance 唯一抛错点)。 + // prototype 非对象 → TypeError(OrdinaryHasInstance 唯一抛错点); + // 传播读触发 getter,getter 抛错时原值经 uncaught 侧通道恢复投递。 let proto_si = vm.perm_intern("prototype"); - let Some(proto_val) = vm.resolve_property(c_obj, proto_si) else { - return NativeResult::Err(crate::error::create_type_error( - vm, - "Function has non-object prototype in instanceof check", - )); + let proto_val = match vm.ordinary_get(c_obj, proto_si, c) { + Ok(v) => v, + Err(e) => return NativeResult::Err(to_string_error_value(vm, &e)), }; if !proto_val.is_object() { return NativeResult::Err(crate::error::create_type_error( diff --git a/crates/oxide_kernel/src/builtin/construct.rs b/crates/oxide_kernel/src/builtin/construct.rs index 0941a938..c09914dc 100644 --- a/crates/oxide_kernel/src/builtin/construct.rs +++ b/crates/oxide_kernel/src/builtin/construct.rs @@ -75,6 +75,16 @@ pub(crate) fn make_named_pair( 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) { + 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 规定其是 /// [[NumberData]] = +0 的 Number object。type_tag 是 Object.prototype.toString /// 品牌表的判据(置位后报 "[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 (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"); + tag_function_proto(&function_proto); let (string_proto, string_constructor) = make_named_pair(string_forge, shape_forge, labels, "String"); tag_string_proto(&string_proto, string_forge, shape_forge); let (number_proto, number_constructor) = make_named_pair(string_forge, shape_forge, labels, "Number"); diff --git a/crates/oxide_kernel/src/builtin/rebuild.rs b/crates/oxide_kernel/src/builtin/rebuild.rs index 72f377dc..b9dd0279 100644 --- a/crates/oxide_kernel/src/builtin/rebuild.rs +++ b/crates/oxide_kernel/src/builtin/rebuild.rs @@ -7,8 +7,9 @@ use oxide_types::object::JsObject; use oxide_types::value::JsValue; use super::construct::{ - builtin_labels, make_error_subtypes, make_named_pair, make_typed_array_family, tag_boolean_proto, tag_number_proto, - tag_string_proto, wire_builtin_world_links, ErrorSubtypeProtos, TypedArrayFamily, + builtin_labels, make_error_subtypes, make_named_pair, make_typed_array_family, tag_boolean_proto, + tag_function_proto, tag_number_proto, tag_string_proto, wire_builtin_world_links, ErrorSubtypeProtos, + TypedArrayFamily, }; use super::{BindEnv, BuiltinWorld}; use crate::kernel::BuiltinDirtySet; @@ -173,7 +174,11 @@ impl BuiltinWorld { (current.array_proto.clone(), current.array_constructor.clone()) }; 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 { (current.function_proto.clone(), current.function_constructor.clone()) }; diff --git a/crates/oxide_kernel/tests/builtin_world_tests.rs b/crates/oxide_kernel/tests/builtin_world_tests.rs index b5a24b2b..b114f5de 100644 --- a/crates/oxide_kernel/tests/builtin_world_tests.rs +++ b/crates/oxide_kernel/tests/builtin_world_tests.rs @@ -46,7 +46,9 @@ fn test_prototypes_are_not_functions() { let w = make_world(); assert!(!w.object_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.number_proto.is_function()); assert!(!w.boolean_proto.is_function()); diff --git a/crates/oxide_vm/src/vm_dispatch_misc.rs b/crates/oxide_vm/src/vm_dispatch_misc.rs index ea6cc2c5..b00dfc06 100644 --- a/crates/oxide_vm/src/vm_dispatch_misc.rs +++ b/crates/oxide_vm/src/vm_dispatch_misc.rs @@ -206,13 +206,11 @@ impl Vm { )?; Ok(true) } else { - let error = oxide_builtins::error::create_from_text( - self, - "NEW_EXPRESSION: bytecode constructors not yet supported", - ); - self.exception_value = Some(error); - self.pending_error_kind = Some(self.thrown_error_kind(error)); - self.unwind().map(|_| true) + // 无函数体的函数对象(如 Function.prototype):IsConstructor 为假, + // 按规范抛 TypeError(此前该分支不可达,报普通 Error 属内部错误)。 + self + .raise_type_error("NEW_EXPRESSION: constructor is not a constructor") + .map(|_| true) } } @@ -411,17 +409,31 @@ impl Vm { 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 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() { - let fn_ptr = has_instance_val.as_js_object_ptr(); - if !fn_ptr.is_null() { - 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(()); - } + if !is_callable(has_instance_val) { + return self.raise_type_error("instanceof: @@hasInstance handler is not callable"); } + 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() { @@ -431,15 +443,18 @@ impl Vm { let rhs_obj = unsafe { &*rhs_val.as_js_object_ptr() }; let proto_si = self.kernel_core.perm_interner().intern("prototype").0; - let ctor_proto = self.resolve_property(rhs_obj, proto_si); - - let ctor_proto_ptr = match ctor_proto { - Some(v) if v.is_object() => v.as_js_object_ptr(), - _ => { - self.regs[rd] = JsValue::bool(false); - return Ok(()); - } + // 传播读:getter 异常原值上抛(深度 0 已就地展开的由 pc 变化感知)。 + let pc_before = self.pc; + let ctor_proto = match self.ordinary_get(rhs_obj, proto_si, rhs_val) { + Ok(v) if self.pc == pc_before => v, + Ok(_) => return Ok(()), + Err(e) => return self.raise_call_error(&e).map(|_| ()), }; + // 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 depth = 0usize; diff --git a/crates/oxide_vm/tests/builtins_symbol_tests.rs b/crates/oxide_vm/tests/builtins_symbol_tests.rs index 96c863a6..811b4a45 100644 --- a/crates/oxide_vm/tests/builtins_symbol_tests.rs +++ b/crates/oxide_vm/tests/builtins_symbol_tests.rs @@ -179,12 +179,16 @@ fn has_instance_overrides_instanceof() { } #[test] -fn has_instance_non_callable_falls_through() { +fn has_instance_non_callable_handler_throws() { let mut vm = Vm::new(); + // handler 在场但不可调用:Call(42) 抛 TypeError(旧行为静默回落 + // prototype 分支,与规范步 4 不符)。 let source = r#" class D {} 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(); assert!(result.as_bool());