diff --git a/crates/oxide_bytecode/src/opcode.rs b/crates/oxide_bytecode/src/opcode.rs index 8fa97ada..64fa3368 100644 --- a/crates/oxide_bytecode/src/opcode.rs +++ b/crates/oxide_bytecode/src/opcode.rs @@ -90,6 +90,7 @@ pub enum OpCode { SUPER_STATIC_GET_PROP = 0x46, SET_HOME_OBJECT = 0x47, DEFINE_ACCESSOR = 0x48, + CREATE_CLOSURE = 0x4A, // ── Object Property (0x50-0x5F) ── IC_GET_PROP = 0x50, @@ -238,6 +239,7 @@ impl TryFrom for OpCode { 0x46 => Ok(OpCode::SUPER_STATIC_GET_PROP), 0x47 => Ok(OpCode::SET_HOME_OBJECT), 0x48 => Ok(OpCode::DEFINE_ACCESSOR), + 0x4A => Ok(OpCode::CREATE_CLOSURE), 0x50 => Ok(OpCode::IC_GET_PROP), 0x51 => Ok(OpCode::IC_SET_PROP), 0x52 => Ok(OpCode::GET_PROP), @@ -357,6 +359,7 @@ impl fmt::Display for OpCode { OpCode::SUPER_STATIC_GET_PROP => "SUPER_STATIC_GET_PROP", OpCode::SET_HOME_OBJECT => "SET_HOME_OBJECT", OpCode::DEFINE_ACCESSOR => "DEFINE_ACCESSOR", + OpCode::CREATE_CLOSURE => "CREATE_CLOSURE", OpCode::IC_GET_PROP => "IC_GET_PROP", OpCode::IC_SET_PROP => "IC_SET_PROP", OpCode::GET_PROP => "GET_PROP", diff --git a/crates/oxide_compiler/src/compiler.rs b/crates/oxide_compiler/src/compiler.rs index 2563251b..d1ff1fcf 100644 --- a/crates/oxide_compiler/src/compiler.rs +++ b/crates/oxide_compiler/src/compiler.rs @@ -254,6 +254,11 @@ impl CompileCtx { self.emit(opcode::encode(OpCode::LOAD_CONST, reg, (idx & 0xFF) as u8, ((idx >> 8) & 0xFF) as u8)); } + pub(crate) fn emit_create_closure(&mut self, reg: u8, sub_idx: u32) { + let idx = sub_idx as u16; + self.emit(opcode::encode(OpCode::CREATE_CLOSURE, reg, (idx & 0xFF) as u8, ((idx >> 8) & 0xFF) as u8)); + } + pub(crate) fn count_word(&mut self) { self.projected_pc += 1; } diff --git a/crates/oxide_compiler/src/emitter/expr/function.rs b/crates/oxide_compiler/src/emitter/expr/function.rs index d8773ecd..ecefa87a 100644 --- a/crates/oxide_compiler/src/emitter/expr/function.rs +++ b/crates/oxide_compiler/src/emitter/expr/function.rs @@ -37,9 +37,8 @@ impl Compiler { // 1-indexed: 0 = no sub_module (sentinel) let sub_idx = ctx.sub_modules.len() as u32; - let const_idx = ctx.add_constant(Constant::BytecodeFunc(sub_idx)); let r = ctx.alloc_reg(); - ctx.emit_load_const(r, const_idx); + ctx.emit_create_closure(r, sub_idx); Ok(r) } @@ -72,9 +71,8 @@ impl Compiler { // 1-indexed: 0 = no sub_module (sentinel) let sub_idx = ctx.sub_modules.len() as u32; - let const_idx = ctx.add_constant(Constant::BytecodeFunc(sub_idx)); let r = ctx.alloc_reg(); - ctx.emit_load_const(r, const_idx); + ctx.emit_create_closure(r, sub_idx); Ok(r) } diff --git a/crates/oxide_compiler/src/emitter/mod.rs b/crates/oxide_compiler/src/emitter/mod.rs index 02d200d0..a0af0ec9 100644 --- a/crates/oxide_compiler/src/emitter/mod.rs +++ b/crates/oxide_compiler/src/emitter/mod.rs @@ -811,8 +811,7 @@ impl Compiler { ctor_module.function_name = ctor_name.clone(); ctx.sub_modules.push(ctor_module); - let ctor_idx = ctx.add_constant(Constant::BytecodeFunc(ctx.sub_modules.len() as u32)); - ctx.emit_load_const(ctor_reg, ctor_idx); + ctx.emit_create_closure(ctor_reg, ctx.sub_modules.len() as u32); ctx.emit(opcode::encode(OpCode::NEW_OBJECT, proto_reg, 0, 0)); if let Some(super_reg) = super_reg { @@ -947,9 +946,8 @@ impl Compiler { method_module.needs_home_object = true; ctx.sub_modules.push(method_module); - let method_idx = ctx.add_constant(Constant::BytecodeFunc(ctx.sub_modules.len() as u32)); let method_reg = ctx.alloc_reg(); - ctx.emit_load_const(method_reg, method_idx); + ctx.emit_create_closure(method_reg, ctx.sub_modules.len() as u32); ctx.emit(opcode::encode(OpCode::SET_HOME_OBJECT, method_reg, home_reg, 0)); Ok(method_reg) } diff --git a/crates/oxide_compiler/src/emitter/stmt/declaration.rs b/crates/oxide_compiler/src/emitter/stmt/declaration.rs index 67ae72f3..3b6d1649 100644 --- a/crates/oxide_compiler/src/emitter/stmt/declaration.rs +++ b/crates/oxide_compiler/src/emitter/stmt/declaration.rs @@ -77,8 +77,7 @@ impl Compiler { let var_reg = ctx.lookup(&name)?; ctx.reserve_reg(var_reg); - let const_idx = ctx.add_constant(Constant::BytecodeFunc(sub_idx)); - ctx.emit_load_const(var_reg, const_idx); + ctx.emit_create_closure(var_reg, sub_idx); ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, var_reg, 0)); Ok(None) diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 8918c7ff..fe1ec94f 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -739,6 +739,20 @@ impl Vm { self.dispatch_load_const(rd, instr)?; } + OpCode::CREATE_CLOSURE => { + let sub_idx = opcode::imm16(instr) as u32; + debug_assert!(sub_idx > 0 && (sub_idx as usize) <= self.sub_modules.len()); + let sub = &self.sub_modules[sub_idx as usize - 1]; + let result = self.create_function_object( + sub_idx, + sub.is_arrow, + sub.is_class_constructor, + sub.is_derived_constructor, + sub.needs_home_object, + ); + self.regs[rd] = result; + } + OpCode::ADD => { self.dispatch_add(rd, a, b)?; } diff --git a/crates/oxide_vm/src/vm_runtime.rs b/crates/oxide_vm/src/vm_runtime.rs index 7af1cfcc..8faa7b04 100644 --- a/crates/oxide_vm/src/vm_runtime.rs +++ b/crates/oxide_vm/src/vm_runtime.rs @@ -158,8 +158,7 @@ impl Vm { } } - let global_ptr = self.session.global_object().as_ptr() as *mut JsObject; - self.regs[254] = JsValue::from_js_object(global_ptr); + self.regs[254] = JsValue::undefined(); self.dispatch() } diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index dab0d4be..9a8b47da 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -235,7 +235,7 @@ impl Vm { /// Create a function JsObject for a BytecodeFunc constant. /// When `is_arrow` is true, captures the current `this` (regs[254]) /// for lexical this binding at call time. - fn create_function_object( + pub(crate) fn create_function_object( &mut self, sub_idx: u32, is_arrow: bool, is_class_constructor: bool, is_derived_constructor: bool, needs_home_object: bool, ) -> JsValue {