Files
OxideJS-New/crates/oxide_vm/src/dispatch/property.rs
T

227 lines
9.7 KiB
Rust

use oxide_compiler::opcode::OpCode;
use oxide_kernel::prop_forge::PropTemplate;
use oxide_types::object::JsObject;
use crate::vm::Vm;
impl Vm {
pub(crate) fn dispatch_property_op(&mut self, op: OpCode, rd: usize, a: usize, b: usize) -> Result<(), String> {
match op {
OpCode::IC_GET_PROP => self.dispatch_ic_get_prop(a, b),
OpCode::IC_SET_PROP => self.dispatch_ic_set_prop(rd, a, b),
OpCode::GET_PROP => self.dispatch_get_prop(rd, a, b),
OpCode::SET_PROP => self.dispatch_set_prop(rd, a, b),
OpCode::GET_PROP_DYNAMIC => self.dispatch_get_prop_dynamic(rd, a, b),
OpCode::SET_PROP_DYNAMIC => self.dispatch_set_prop_dynamic(rd, a, b),
OpCode::SET_ELEM => self.dispatch_set_elem(rd, a, b),
_ => unreachable!("non-property opcode passed to dispatch_property_op"),
}
}
fn dispatch_ic_get_prop(&mut self, a: usize, b: usize) -> Result<(), String> {
let val = self.regs[a];
let obj_ptr = val.as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() {
if val.is_string() {
let proto_ptr = self.kernel.builtin_world().string_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &*proto_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
let resolved = self.ordinary_get(proto, prop_name_si, val)?;
if !resolved.is_undefined() {
self.regs[a] = resolved;
self.pc += 3;
return Ok(());
}
}
if val.is_int() || val.is_double() {
let proto_ptr = self.kernel.builtin_world().number_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &*proto_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
let resolved = self.ordinary_get(proto, prop_name_si, val)?;
if !resolved.is_undefined() {
self.regs[a] = resolved;
self.pc += 3;
return Ok(());
}
}
self.raise_type_error("IC_GET_PROP on non-object")?;
return Ok(());
}
let addr = obj_ptr as usize;
if addr < 0x10000 || addr % std::mem::align_of::<JsObject>() != 0 {
self.raise_type_error("IC_GET_PROP on non-object")?;
return Ok(());
}
let obj = unsafe { &*obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
let ext0 = self.bytecode[self.pc];
let ext1 = self.bytecode[self.pc + 1];
let _ext2 = self.bytecode[self.pc + 2];
self.pc += 3;
if obj.has_prop_meta() {
let val = self.ordinary_get_with_target(obj, prop_name_si, val, a as u8)?;
if self.accessor_frame_target_reg.take().is_none() {
self.regs[a] = val;
}
return Ok(());
}
let cached_shape_id = ext0 & 0x00FF_FFFF;
let cached_slot = ext1;
if cached_shape_id != 0 && cached_shape_id == obj.shape_id() && cached_slot < obj.prop_vec_len() as u32 {
self.regs[a] = obj.get_prop_at(cached_slot);
} else if let Some(template) = self.kernel.prop_forge().get_template(obj.shape_id()) {
if template.prop_name == prop_name_si {
if template.position < obj.prop_vec_len() as u32 {
self.write_ic_back(obj.shape_id(), template.position);
self.regs[a] = obj.get_prop_at(template.position);
} else {
self.regs[a] = self.ordinary_get(obj, prop_name_si, val)?;
}
} else {
self.regs[a] = self.ordinary_get(obj, prop_name_si, val)?;
}
} else {
let resolved = self.ordinary_get(obj, prop_name_si, val)?;
if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
self.write_ic_back(obj.shape_id(), pos);
}
self.regs[a] = resolved;
}
Ok(())
}
fn dispatch_ic_set_prop(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "IC_SET_PROP on non-object")? else {
return Ok(());
};
let prop_name_si = self.property_key_si(self.regs[b]);
if self.kernel.string_forge().lookup(prop_name_si).as_deref() == Some("__proto__") {
let obj = unsafe { &mut *obj_ptr };
if self.is_object_prototype(obj_ptr) && !self.regs[a].is_null() {
self.raise_type_error("Object.prototype.__proto__ is immutable")?;
self.pc += 3;
return Ok(());
}
obj.set_proto(self.regs[a]).map_err(|e| e.to_string())?;
self.pc += 3;
return Ok(());
}
let obj = unsafe { &mut *obj_ptr };
let ext0 = self.bytecode[self.pc];
let ext1 = self.bytecode[self.pc + 1];
let _ext2 = self.bytecode[self.pc + 2];
self.pc += 3;
let value = self.regs[a];
let receiver = self.regs[rd];
if obj.has_prop_meta() {
self.ordinary_set_dispatch(obj, prop_name_si, value, receiver)?;
return Ok(());
}
let cached_shape_id = ext0 & 0x00FF_FFFF;
let cached_slot = ext1;
if cached_shape_id != 0 && cached_shape_id == obj.shape_id() && cached_slot < obj.prop_vec_len() as u32 {
obj.set_prop_at(cached_slot, value);
} else if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
obj.set_prop_at(pos, value);
self.write_ic_back(obj.shape_id(), pos);
} else {
let old_shape = obj.shape_id();
self.ordinary_set_dispatch(obj, prop_name_si, value, receiver)?;
if old_shape != obj.shape_id() {
if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
self.write_ic_back(obj.shape_id(), pos);
self.kernel.prop_forge().upsert(
obj.shape_id(),
PropTemplate {
shape_id: obj.shape_id(),
prop_name: prop_name_si,
position: pos,
generation: obj.generation(),
},
);
}
}
}
Ok(())
}
fn dispatch_get_prop(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "GET_PROP on non-object")? else {
return Ok(());
};
let obj = unsafe { &*obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
let val = self.ordinary_get_with_target(obj, prop_name_si, self.regs[rd], a as u8)?;
if self.accessor_frame_target_reg.take().is_none() {
self.regs[a] = val;
}
Ok(())
}
fn dispatch_set_prop(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "SET_PROP on non-object")? else {
return Ok(());
};
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]);
if self.kernel.string_forge().lookup(prop_name_si).as_deref() == Some("__proto__") {
if self.is_object_prototype(obj_ptr) && !self.regs[a].is_null() {
self.raise_type_error("Object.prototype.__proto__ is immutable")?;
return Ok(());
}
obj.set_proto(self.regs[a]).map_err(|e| e.to_string())?;
return Ok(());
}
self.ordinary_set_dispatch(obj, prop_name_si, self.regs[a], self.regs[rd])?;
Ok(())
}
fn dispatch_get_prop_dynamic(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "GET_PROP_DYNAMIC on non-object")? else {
return Ok(());
};
let obj = unsafe { &*obj_ptr };
let prop_name_si = self.property_key_si(self.regs[a]);
let val = self.ordinary_get_with_target(obj, prop_name_si, self.regs[rd], b as u8)?;
if self.accessor_frame_target_reg.take().is_none() {
self.regs[b] = val;
}
Ok(())
}
fn dispatch_set_prop_dynamic(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "SET_PROP_DYNAMIC on non-object")? else {
return Ok(());
};
let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[a]);
if self.kernel.string_forge().lookup(prop_name_si).as_deref() == Some("__proto__") {
if self.is_object_prototype(obj_ptr) && !self.regs[b].is_null() {
self.raise_type_error("Object.prototype.__proto__ is immutable")?;
return Ok(());
}
obj.set_proto(self.regs[b]).map_err(|e| e.to_string())?;
return Ok(());
}
self.ordinary_set_dispatch(obj, prop_name_si, self.regs[b], self.regs[rd])?;
Ok(())
}
fn dispatch_set_elem(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "SET_ELEM on non-object")? else {
return Ok(());
};
let idx = self.regs[a].as_int().max(0) as u32;
let obj = unsafe { &mut *obj_ptr };
obj.set_prop_at(idx, self.regs[b]);
Ok(())
}
}