diff --git a/crates/oxide_compiler/src/emitter.rs b/crates/oxide_compiler/src/emitter.rs index df187d8..e36e800 100644 --- a/crates/oxide_compiler/src/emitter.rs +++ b/crates/oxide_compiler/src/emitter.rs @@ -1792,17 +1792,6 @@ impl Compiler { let first_arg_reg = if arg_regs.is_empty() { 0u8 } else { arg_regs[0] }; let op = match &call.callee { Expression::Identifier(ident) if ctx.is_builtin(ident.name.as_str()) => OpCode::CALL_NATIVE, - Expression::StaticMemberExpression(m) => { - if let Expression::Identifier(ident) = &m.object { - if ctx.is_builtin(ident.name.as_str()) { - OpCode::CALL_NATIVE - } else { - OpCode::CALL - } - } else { - OpCode::CALL - } - } _ => OpCode::CALL, }; ctx.emit(opcode::encode(op, callee_reg, this_reg, first_arg_reg)); diff --git a/crates/oxide_vm/tests/builtins_object_tests.rs b/crates/oxide_vm/tests/builtins_object_tests.rs index ee9b7ad..a38c104 100644 --- a/crates/oxide_vm/tests/builtins_object_tests.rs +++ b/crates/oxide_vm/tests/builtins_object_tests.rs @@ -26,6 +26,28 @@ fn object_keys_has_own_property() { assert_eq!(obj.prop_count(), 2); } +#[test] +fn object_member_native_call_uses_regular_call_path() { + let (_vm, result) = eval("Object.keys({a:1}).length").unwrap(); + assert_eq!(result.as_int(), 1); +} + +#[test] +fn object_member_call_allows_user_overwrite() { + let (_vm, result) = eval("Object.keys = function() { return 9; }; Object.keys()").unwrap(); + assert_eq!(result.as_int(), 9); +} + +#[test] +fn missing_object_member_call_is_not_call_native_target_error() { + let err = match eval("Object.noSuchMethod()") { + Ok(_) => panic!("missing member call should fail"), + Err(err) => err, + }; + assert!(err.contains("CALL target is not callable"), "unexpected error: {err}"); + assert!(!err.contains("CALL_NATIVE target"), "unexpected CALL_NATIVE path: {err}"); +} + #[test] fn object_create_null_proto() { let (_vm, _result) = eval("Object.create(null)").unwrap();