From 3e5a2161e56d7bf99bad1bf10b23b9f2445b92ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=88=B1=E7=89=B9=E6=AF=8F=E7=BE=8A=E9=98=9F?= Date: Fri, 5 Jun 2026 19:10:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E5=AE=8C=E6=95=B4=20ES=20=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_vm/src/coercion.rs | 25 ++++++++++++++++-- crates/oxide_vm/src/object.rs | 21 ++++++++++++++- crates/oxide_vm/src/vm.rs | 46 ++++++++++++++++++++------------- 3 files changed, 71 insertions(+), 21 deletions(-) diff --git a/crates/oxide_vm/src/coercion.rs b/crates/oxide_vm/src/coercion.rs index ad3be71..babe3a2 100644 --- a/crates/oxide_vm/src/coercion.rs +++ b/crates/oxide_vm/src/coercion.rs @@ -1,3 +1,5 @@ +use crate::object::JsObject; +use crate::shape::EMPTY_SHAPE_ID; use crate::value::JsValue; use crate::vm::Vm; @@ -184,6 +186,25 @@ pub fn string_concat(lhs: &str, rhs: &str) -> String { s } -fn panic_to_object() { - panic!("ToObject not yet implemented (Phase 6)") +fn panic_to_object() -> ! { + panic!("ToObject failed: null or undefined cannot be converted to object") +} + +pub fn to_object(val: JsValue, vm: &mut Vm) -> Result { + if val.is_object() { + return Ok(val); + } + if val.is_null() || val.is_undefined() { + return Err("TypeError: Cannot convert null or undefined to object"); + } + let proto_ptr = &*vm.object_prototype as *const JsObject as *mut JsObject; + let proto_val = JsValue::from_js_object(proto_ptr); + let obj = vm + .epoch + .alloc(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)); + let obj_val = JsValue::from_js_object(obj); + let obj_ref = unsafe { &mut *obj }; + obj_ref.set_inline_prop(0, val); + obj_ref.set_prop_count(1); + Ok(obj_val) } diff --git a/crates/oxide_vm/src/object.rs b/crates/oxide_vm/src/object.rs index 0a6762d..53b3327 100644 --- a/crates/oxide_vm/src/object.rs +++ b/crates/oxide_vm/src/object.rs @@ -142,9 +142,28 @@ impl JsObject { self.proto } - pub fn set_proto(&mut self, proto: JsValue) { + pub fn set_proto(&mut self, proto: JsValue) -> Result<(), &'static str> { + if proto.is_null() { + self.proto = proto; + self.generation = self.generation.wrapping_add(1); + return Ok(()); + } + if !proto.is_object() { + return Err("__proto__ must be an object or null"); + } + let mut cursor = proto; + let self_ptr = self as *const JsObject; + while cursor.is_object() { + let cursor_ptr = cursor.as_js_object_ptr(); + if cursor_ptr == self_ptr as *mut JsObject { + return Err("cyclic __proto__ value"); + } + let obj = unsafe { &*cursor_ptr }; + cursor = obj.proto; + } self.proto = proto; self.generation = self.generation.wrapping_add(1); + Ok(()) } pub fn generation(&self) -> u32 { diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 6c6b9b2..cf8364b 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -61,6 +61,21 @@ impl Vm { self.string_reverse.get(idx).map(|s| s.as_str()) } + fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option { + if let Some(offset) = shape::lookup_offset(obj.shape_id(), prop_name_si) { + return Some(obj.get_prop(offset)); + } + let mut proto = obj.proto(); + while proto.is_object() { + let proto_obj = unsafe { &*proto.as_js_object_ptr() }; + if let Some(offset) = shape::lookup_offset(proto_obj.shape_id(), prop_name_si) { + return Some(proto_obj.get_prop(offset)); + } + proto = proto_obj.proto(); + } + None + } + pub fn run(&mut self, module: &CompiledModule) -> Result { self.string_table.clear(); self.string_reverse.clear(); @@ -265,14 +280,14 @@ impl Vm { if cached_shape_id != 0 && cached_shape_id == obj.shape_id() { self.regs[a] = obj.get_prop(cached_offset); + } else if let Some(val) = self.resolve_property(obj, prop_name_si) { + let offset = + shape::lookup_offset(obj.shape_id(), prop_name_si).unwrap_or(0); + let new_ext = (obj.shape_id() & 0x00FF_FFFF) | ((offset as u32) << 24); + self.bytecode[self.pc - 1] = new_ext; + self.regs[a] = val; } else { - if let Some(offset) = shape::lookup_offset(obj.shape_id(), prop_name_si) { - let new_ext = (obj.shape_id() & 0x00FF_FFFF) | ((offset as u32) << 24); - self.bytecode[self.pc - 1] = new_ext; - self.regs[a] = obj.get_prop(offset); - } else { - self.regs[a] = JsValue::undefined(); - } + self.regs[a] = JsValue::undefined(); } } @@ -316,13 +331,10 @@ impl Vm { } let obj = unsafe { &*obj_ptr }; let prop_name_si = self.regs[b].as_string_index(); - if let Some(offset) = shape::lookup_offset(obj.shape_id(), prop_name_si) { - self.regs[a] = obj.get_prop(offset); - } else { - self.regs[a] = JsValue::undefined(); - } + self.regs[a] = self + .resolve_property(obj, prop_name_si) + .unwrap_or(JsValue::undefined()); } - OpCode::SET_PROP => { let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject; if obj_ptr.is_null() { @@ -353,11 +365,9 @@ impl Vm { return Err("GET_PROP_DYNAMIC key not a string".into()); } let prop_name_si = key_val.as_string_index(); - if let Some(offset) = shape::lookup_offset(obj.shape_id(), prop_name_si) { - self.regs[b] = obj.get_prop(offset); - } else { - self.regs[b] = JsValue::undefined(); - } + self.regs[b] = self + .resolve_property(obj, prop_name_si) + .unwrap_or(JsValue::undefined()); } OpCode::SET_PROP_DYNAMIC => {