feat: 支持对象进行完整 ES 属性解析

This commit is contained in:
爱特每羊队
2026-06-05 19:10:44 +08:00
parent 82db4f27a3
commit 3e5a2161e5
3 changed files with 71 additions and 21 deletions
+23 -2
View File
@@ -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<JsValue, &'static str> {
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)
}
+20 -1
View File
@@ -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 {
+28 -18
View File
@@ -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<JsValue> {
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<JsValue, String> {
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 => {