use oxide_compiler::compiler::Compiler; use oxide_types::value::JsValue; use oxide_vm::vm::Vm; fn eval(vm: &mut Vm, source: &str) -> Result { let allocator = oxide_parser::Allocator::default(); let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; let module = Compiler::new() .compile(&program) .map_err(|e| format!("Compile error: {}", e))?; vm.run(&module) } // --- Function Declaration Basics --- #[test] fn fd_return_literal() { let mut vm = Vm::new(); let result = eval(&mut vm, "function f() { return 42; } f()").unwrap(); assert_eq!(result.as_int(), 42); } #[test] fn fd_return_void() { let mut vm = Vm::new(); let result = eval(&mut vm, "function f() { 1; } f()").unwrap(); assert!(result.is_undefined()); } #[test] fn fd_hoisting() { let mut vm = Vm::new(); let result = eval(&mut vm, "foo(); function foo() { return 1; }").unwrap(); assert_eq!(result.as_int(), 1); } #[test] fn fd_with_params() { let mut vm = Vm::new(); let result = eval(&mut vm, "function add(a,b) { return a + b; } add(2, 3)").unwrap(); assert!((result.as_double() - 5.0).abs() < 0.0001); } #[test] fn fd_single_param() { let mut vm = Vm::new(); let result = eval(&mut vm, "function echo(x) { return x; } echo(99)").unwrap(); assert_eq!(result.as_int(), 99); } // --- Function Expression --- #[test] fn fe_basic() { let mut vm = Vm::new(); let result = eval(&mut vm, "var f = function() { return 99; }; f()").unwrap(); assert_eq!(result.as_int(), 99); } #[test] fn fe_with_params() { let mut vm = Vm::new(); let result = eval( &mut vm, "var mul = function(x,y) { return x * y; }; mul(6, 7)", ) .unwrap(); assert!((result.as_double() - 42.0).abs() < 0.0001); } // --- Cross-function calls --- #[test] fn cross_func_call() { let mut vm = Vm::new(); let result = eval( &mut vm, "function a() { return 1; } function b() { return a() + 2; } b()", ) .unwrap(); assert!((result.as_double() - 3.0).abs() < 0.0001); } #[test] fn two_funcs_called_from_global() { let mut vm = Vm::new(); let result = eval( &mut vm, "function a() { return 1; } function b() { return 2; } a() + b()", ) .unwrap(); assert!(result.is_double(), "expected double, got: {:?}", result); assert!((result.as_double() - 3.0).abs() < 0.0001); } // --- Builtins inside functions --- #[test] fn fd_calls_builtin_return() { let mut vm = Vm::new(); let result = eval(&mut vm, "function f() { return Math.abs(-5); } f()").unwrap(); assert!(result.is_double(), "expected double, got: {:?}", result); assert!((result.as_double() - 5.0).abs() < 0.0001); } #[test] fn fd_calls_builtin_with_param() { let mut vm = Vm::new(); let result = eval(&mut vm, "function f(x) { return Math.abs(x); } f(-10)").unwrap(); assert!(result.is_double(), "expected double, got: {:?}", result); assert!((result.as_double() - 10.0).abs() < 0.0001); } #[test] fn fd_calls_builtin_two_args() { let mut vm = Vm::new(); let result = eval(&mut vm, "function f(x) { return Math.pow(x, 2); } f(3)").unwrap(); assert!(result.is_double(), "expected double, got: {:?}", result); assert!((result.as_double() - 9.0).abs() < 0.0001); } // --- Multiple FDs, first FD calls second --- #[test] fn fd_chain_call() { let mut vm = Vm::new(); let result = eval( &mut vm, "function a() { return 10; } function b() { return a(); } b()", ) .unwrap(); assert_eq!(result.as_int(), 10); } // --- new Xxx() in function --- #[test] fn fd_returns_new_object() { let mut vm = Vm::new(); let result = eval(&mut vm, "function f() { return new Object(); } f()").unwrap(); assert!(result.is_object(), "expected object, got: {:?}", result); } #[test] fn fd_returns_new_array() { let mut vm = Vm::new(); let result = eval(&mut vm, "function f() { return new Array(3); } f()").unwrap(); assert!(result.is_object()); }