diff --git a/crates/oxide_compiler/src/compiler.rs b/crates/oxide_compiler/src/compiler.rs index 6d8ca5f3..d5c8f897 100644 --- a/crates/oxide_compiler/src/compiler.rs +++ b/crates/oxide_compiler/src/compiler.rs @@ -347,6 +347,33 @@ impl Compiler { )); Ok(r) } + Expression::ObjectExpression(obj) => { + let obj_reg = ctx.alloc_reg(); + ctx.emit(opcode::encode(OpCode::NEW_OBJECT, obj_reg, 0, 0)); + for prop in &obj.properties { + let oxide_parser::ObjectPropertyKind::ObjectProperty(p) = prop else { + return Err("spread properties not yet supported".into()); + }; + let prop_name = match &p.key { + oxide_parser::PropertyKey::StaticIdentifier(ident) => { + ident.name.as_str().to_string() + } + oxide_parser::PropertyKey::StringLiteral(s) => s.value.to_string(), + _ => return Err("unsupported object property key type".into()), + }; + let idx = ctx.add_constant(Constant::String(prop_name.to_string())); + let key_reg = ctx.alloc_reg(); + ctx.emit(opcode::encode( + OpCode::LOAD_CONST, + key_reg, + (idx & 0xFF) as u8, + ((idx >> 8) & 0xFF) as u8, + )); + let val_reg = self.emit_expression(&p.value, ctx)?; + ctx.emit(opcode::encode(OpCode::SET_PROP, obj_reg, val_reg, key_reg)); + } + Ok(obj_reg) + } _ => Err(format!( "unsupported expression type: {:?}", std::mem::discriminant(expr) diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index fb505c4a..a2fba9c7 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -313,8 +313,7 @@ impl Vm { return Err("GET_PROP on non-object".into()); } let obj = unsafe { &*obj_ptr }; - let prop_val = self.constants[opcode::imm16(instr) as usize]; - let prop_name_si = prop_val.as_string_index(); + 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 { @@ -322,6 +321,25 @@ impl Vm { } } + OpCode::SET_PROP => { + let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject; + if obj_ptr.is_null() { + return Err("SET_PROP on non-object".into()); + } + let obj = unsafe { &mut *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) { + obj.set_prop(offset, self.regs[a]); + } else { + let new_offset = obj.prop_count(); + let new_shape_id = shape::make_shape(obj.shape_id(), prop_name_si); + obj.set_shape_id(new_shape_id); + obj.set_prop_count(new_offset + 1); + obj.set_prop(new_offset, self.regs[a]); + obj.bump_generation(); + } + } + OpCode::GET_PROP_DYNAMIC => { let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject; if obj_ptr.is_null() { @@ -340,26 +358,6 @@ impl Vm { } } - OpCode::SET_PROP => { - let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject; - if obj_ptr.is_null() { - return Err("SET_PROP on non-object".into()); - } - let obj = unsafe { &mut *obj_ptr }; - let prop_val = self.constants[opcode::imm16(instr) as usize]; - let prop_name_si = prop_val.as_string_index(); - if let Some(offset) = shape::lookup_offset(obj.shape_id(), prop_name_si) { - obj.set_prop(offset, self.regs[a]); - } else { - let new_offset = obj.prop_count(); - let new_shape_id = shape::make_shape(obj.shape_id(), prop_name_si); - obj.set_shape_id(new_shape_id); - obj.set_prop_count(new_offset + 1); - obj.set_prop(new_offset, self.regs[a]); - obj.bump_generation(); - } - } - OpCode::SET_PROP_DYNAMIC => { let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject; if obj_ptr.is_null() {