diff --git a/crates/oxide_compiler/src/counter.rs b/crates/oxide_compiler/src/counter.rs index 6a91dbb..4f27592 100644 --- a/crates/oxide_compiler/src/counter.rs +++ b/crates/oxide_compiler/src/counter.rs @@ -440,6 +440,10 @@ impl Compiler { Expression::ParenthesizedExpression(p) => { self.count_expression(&p.expression, ctx); } + Expression::ThisExpression(_) => { + ctx.alloc_reg(); + ctx.projected_pc += 1; // LOAD_VAR from reg 254 + } Expression::UpdateExpression(update) => match &update.argument { SimpleAssignmentTarget::AssignmentTargetIdentifier(_) => { ctx.alloc_reg(); diff --git a/crates/oxide_compiler/src/emitter.rs b/crates/oxide_compiler/src/emitter.rs index 92844fd..38c2009 100644 --- a/crates/oxide_compiler/src/emitter.rs +++ b/crates/oxide_compiler/src/emitter.rs @@ -1036,6 +1036,11 @@ impl Compiler { ctx.emit(opcode::encode(OpCode::LOAD_VAR, result_reg, 0, 0)); Ok(result_reg) } + Expression::ThisExpression(_) => { + let r = ctx.alloc_reg(); + ctx.emit(opcode::encode(OpCode::LOAD_VAR, r, 254, 0)); + Ok(r) + } _ => Err(format!("unsupported expression type: {:?}", expr)), } } diff --git a/crates/oxide_compiler/tests/compile_expr_tests.rs b/crates/oxide_compiler/tests/compile_expr_tests.rs index c2611fb..cdd70d2 100644 --- a/crates/oxide_compiler/tests/compile_expr_tests.rs +++ b/crates/oxide_compiler/tests/compile_expr_tests.rs @@ -358,3 +358,21 @@ fn compile_compound_member_exp_ok() { }); assert!(result.is_ok(), "obj.x**=2 should compile without error"); } + +#[test] +fn compile_this_in_function_ok() { + let module = compile_source("function f() { return this; }"); + assert!(!module.bytecode.is_empty()); +} + +#[test] +fn compile_this_member_assign_ok() { + let module = compile_source("function f() { this.x = 1; }"); + assert!(!module.bytecode.is_empty()); +} + +#[test] +fn compile_this_member_read_ok() { + let module = compile_source("function f() { return this.x; }"); + assert!(!module.bytecode.is_empty()); +} diff --git a/crates/oxide_vm/tests/function_tests.rs b/crates/oxide_vm/tests/function_tests.rs index 78ce240..4ba077a 100644 --- a/crates/oxide_vm/tests/function_tests.rs +++ b/crates/oxide_vm/tests/function_tests.rs @@ -148,3 +148,41 @@ fn fd_returns_new_array() { let result = eval(&mut vm, "function f() { return new Array(3); } f()").unwrap(); assert!(result.is_object()); } + +// --- this expression --- + +#[test] +fn this_in_function_reads_value() { + let mut vm = Vm::new(); + let result = eval(&mut vm, "function f() { return this; } f()").unwrap(); + assert!( + result.is_object(), + "expected object (function as this), got: {:?}", + result + ); +} + +#[test] +fn this_in_function_assign_member() { + let mut vm = Vm::new(); + let result = eval(&mut vm, "function f(m) { this.message = m; } f('hello'); 1").unwrap(); + assert_eq!(result.as_int(), 1); +} + +#[test] +fn this_member_access() { + let mut vm = Vm::new(); + let result = eval(&mut vm, "function f() { this.x = 42; return this.x; } f()").unwrap(); + assert_eq!(result.as_int(), 42); +} + +#[test] +fn this_in_constructor_sets_proto() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "function Ctor(m) { this.message = m; } Ctor.prototype = new Object(); 1", + ) + .unwrap(); + assert_eq!(result.as_int(), 1); +}