From 094675d0bc569632abc80b6b8c9f5227f992eb05 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sun, 7 Jun 2026 11:32:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E5=86=85=E5=BB=BA?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E8=B0=83=E7=94=A8=E5=B9=B6=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E4=B8=8E=E7=BB=91=E5=AE=9A=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_kernel/src/builtin.rs | 206 +++++++++++++----- crates/oxide_vm/src/builtins/function.rs | 152 +++++++++++++ crates/oxide_vm/src/builtins/mod.rs | 1 + crates/oxide_vm/src/vm.rs | 74 ++++--- .../oxide_vm/tests/builtins_function_tests.rs | 34 +++ 5 files changed, 381 insertions(+), 86 deletions(-) create mode 100644 crates/oxide_vm/src/builtins/function.rs create mode 100644 crates/oxide_vm/tests/builtins_function_tests.rs diff --git a/crates/oxide_kernel/src/builtin.rs b/crates/oxide_kernel/src/builtin.rs index d6bb5ae9..9e8b4658 100644 --- a/crates/oxide_kernel/src/builtin.rs +++ b/crates/oxide_kernel/src/builtin.rs @@ -75,6 +75,12 @@ pub struct NumberMethods { pub to_fixed: *const (), } +pub struct FunctionMethods { + pub call: *const (), + pub apply: *const (), + pub bind: *const (), +} + pub struct BuiltinWorld { pub object_proto: P, pub array_proto: P, @@ -136,6 +142,10 @@ fn make_pair( } impl BuiltinWorld { + fn fn_proto_val(&self) -> JsValue { + JsValue::from_js_object(self.function_proto.as_ptr() as *mut JsObject) + } + pub fn new(string_forge: &StringForge, shape_forge: &ShapeForge) -> Self { let si_prototype = intern_label(string_forge, "prototype"); let si_constructor = intern_label(string_forge, "constructor"); @@ -219,6 +229,24 @@ impl BuiltinWorld { let math_object = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); + fn wire_ctor_proto(ctor: &P, proto: &P) { + let ctor_ptr = ctor.as_ptr() as *mut JsObject; + let ctor = unsafe { &mut *ctor_ptr }; + ctor.set_inline_prop(0, JsValue::from_js_object(proto.as_ptr() as *mut JsObject)); + let proto_ptr = proto.as_ptr() as *mut JsObject; + let proto = unsafe { &mut *proto_ptr }; + proto.set_inline_prop(0, JsValue::from_js_object(ctor_ptr)); + } + + wire_ctor_proto(&object_constructor, &object_proto); + wire_ctor_proto(&array_constructor, &array_proto); + wire_ctor_proto(&function_constructor, &function_proto); + wire_ctor_proto(&string_constructor, &string_proto); + wire_ctor_proto(&number_constructor, &number_proto); + wire_ctor_proto(&boolean_constructor, &boolean_proto); + wire_ctor_proto(&error_constructor, &error_proto); + wire_ctor_proto(&symbol_constructor, &symbol_proto); + Self { object_proto, array_proto, @@ -255,10 +283,10 @@ impl BuiltinWorld { let ctor_ptr = P::as_ptr(&self.object_constructor) as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; - let _ = Self::bind_method(ctor, shape_forge, string_forge, "keys", methods.keys, 1); - let _ = Self::bind_method(ctor, shape_forge, string_forge, "create", methods.create, 2); - let _ = Self::bind_method(ctor, shape_forge, string_forge, "assign", methods.assign, 2); - let _ = Self::bind_method( + let _ = self.bind_method(ctor, shape_forge, string_forge, "keys", methods.keys, 1); + let _ = self.bind_method(ctor, shape_forge, string_forge, "create", methods.create, 2); + let _ = self.bind_method(ctor, shape_forge, string_forge, "assign", methods.assign, 2); + let _ = self.bind_method( ctor, shape_forge, string_forge, @@ -266,7 +294,7 @@ impl BuiltinWorld { methods.define_property, 3, ); - let _ = Self::bind_method( + let _ = self.bind_method( ctor, shape_forge, string_forge, @@ -285,10 +313,10 @@ impl BuiltinWorld { let proto_ptr = P::as_ptr(&self.array_proto) as *mut JsObject; let proto = unsafe { &mut *proto_ptr }; - let _ = Self::bind_method(proto, shape_forge, string_forge, "push", methods.push, 1); - let _ = Self::bind_method(proto, shape_forge, string_forge, "pop", methods.pop, 0); - let _ = Self::bind_method(proto, shape_forge, string_forge, "slice", methods.slice, 2); - let _ = Self::bind_method( + let _ = self.bind_method(proto, shape_forge, string_forge, "push", methods.push, 1); + let _ = self.bind_method(proto, shape_forge, string_forge, "pop", methods.pop, 0); + let _ = self.bind_method(proto, shape_forge, string_forge, "slice", methods.slice, 2); + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -296,7 +324,7 @@ impl BuiltinWorld { methods.splice, 2, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -304,8 +332,8 @@ impl BuiltinWorld { methods.concat, 1, ); - let _ = Self::bind_method(proto, shape_forge, string_forge, "join", methods.join, 1); - let _ = Self::bind_method( + let _ = self.bind_method(proto, shape_forge, string_forge, "join", methods.join, 1); + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -313,7 +341,7 @@ impl BuiltinWorld { methods.index_of, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -321,7 +349,7 @@ impl BuiltinWorld { methods.includes, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -329,7 +357,7 @@ impl BuiltinWorld { methods.reverse, 0, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -337,8 +365,8 @@ impl BuiltinWorld { methods.for_each, 1, ); - let _ = Self::bind_method(proto, shape_forge, string_forge, "map", methods.map, 1); - let _ = Self::bind_method( + let _ = self.bind_method(proto, shape_forge, string_forge, "map", methods.map, 1); + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -346,7 +374,7 @@ impl BuiltinWorld { methods.filter, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -354,11 +382,11 @@ impl BuiltinWorld { methods.reduce, 1, ); - let _ = Self::bind_method(proto, shape_forge, string_forge, "find", methods.find, 1); - let _ = Self::bind_method(proto, shape_forge, string_forge, "some", methods.some, 1); - let _ = Self::bind_method(proto, shape_forge, string_forge, "every", methods.every, 1); - let _ = Self::bind_method(proto, shape_forge, string_forge, "flat", methods.flat, 0); - let _ = Self::bind_method( + let _ = self.bind_method(proto, shape_forge, string_forge, "find", methods.find, 1); + let _ = self.bind_method(proto, shape_forge, string_forge, "some", methods.some, 1); + let _ = self.bind_method(proto, shape_forge, string_forge, "every", methods.every, 1); + let _ = self.bind_method(proto, shape_forge, string_forge, "flat", methods.flat, 0); + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -377,8 +405,8 @@ impl BuiltinWorld { let ctor_ptr = P::as_ptr(&self.error_constructor) as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; - let _ = Self::bind_method(ctor, shape_forge, string_forge, "Error", methods.error, 1); - let _ = Self::bind_method( + let _ = self.bind_method(ctor, shape_forge, string_forge, "Error", methods.error, 1); + let _ = self.bind_method( ctor, shape_forge, string_forge, @@ -386,7 +414,7 @@ impl BuiltinWorld { methods.type_error, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( ctor, shape_forge, string_forge, @@ -394,7 +422,7 @@ impl BuiltinWorld { methods.reference_error, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( ctor, shape_forge, string_forge, @@ -402,7 +430,7 @@ impl BuiltinWorld { methods.range_error, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( ctor, shape_forge, string_forge, @@ -410,7 +438,7 @@ impl BuiltinWorld { methods.syntax_error, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( ctor, shape_forge, string_forge, @@ -418,7 +446,7 @@ impl BuiltinWorld { methods.uri_error, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( ctor, shape_forge, string_forge, @@ -437,7 +465,7 @@ impl BuiltinWorld { let proto_ptr = P::as_ptr(&self.string_proto) as *mut JsObject; let proto = unsafe { &mut *proto_ptr }; - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -445,7 +473,7 @@ impl BuiltinWorld { methods.index_of, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -453,7 +481,7 @@ impl BuiltinWorld { methods.includes, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -461,7 +489,7 @@ impl BuiltinWorld { methods.char_at, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -469,7 +497,7 @@ impl BuiltinWorld { methods.char_code_at, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -477,8 +505,8 @@ impl BuiltinWorld { methods.concat, 1, ); - let _ = Self::bind_method(proto, shape_forge, string_forge, "slice", methods.slice, 2); - let _ = Self::bind_method( + let _ = self.bind_method(proto, shape_forge, string_forge, "slice", methods.slice, 2); + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -486,7 +514,7 @@ impl BuiltinWorld { methods.substring, 2, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -494,7 +522,7 @@ impl BuiltinWorld { methods.to_upper_case, 0, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -502,8 +530,8 @@ impl BuiltinWorld { methods.to_lower_case, 0, ); - let _ = Self::bind_method(proto, shape_forge, string_forge, "trim", methods.trim, 0); - let _ = Self::bind_method( + let _ = self.bind_method(proto, shape_forge, string_forge, "trim", methods.trim, 0); + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -511,7 +539,7 @@ impl BuiltinWorld { methods.repeat, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -519,7 +547,7 @@ impl BuiltinWorld { methods.pad_start, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -527,7 +555,7 @@ impl BuiltinWorld { methods.pad_end, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -535,7 +563,7 @@ impl BuiltinWorld { methods.starts_with, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -543,8 +571,8 @@ impl BuiltinWorld { methods.ends_with, 1, ); - let _ = Self::bind_method(proto, shape_forge, string_forge, "split", methods.split, 1); - let _ = Self::bind_method( + let _ = self.bind_method(proto, shape_forge, string_forge, "split", methods.split, 1); + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -552,7 +580,7 @@ impl BuiltinWorld { methods.replace, 2, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -560,7 +588,7 @@ impl BuiltinWorld { methods.match_fn, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -581,8 +609,8 @@ impl BuiltinWorld { let proto_ptr = P::as_ptr(&self.number_proto) as *mut JsObject; let proto = unsafe { &mut *proto_ptr }; - let _ = Self::bind_method(ctor, shape_forge, string_forge, "isNaN", methods.is_nan, 1); - let _ = Self::bind_method( + let _ = self.bind_method(ctor, shape_forge, string_forge, "isNaN", methods.is_nan, 1); + let _ = self.bind_method( ctor, shape_forge, string_forge, @@ -590,7 +618,7 @@ impl BuiltinWorld { methods.is_finite, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( ctor, shape_forge, string_forge, @@ -598,7 +626,7 @@ impl BuiltinWorld { methods.parse_int, 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( ctor, shape_forge, string_forge, @@ -607,7 +635,7 @@ impl BuiltinWorld { 1, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -615,7 +643,7 @@ impl BuiltinWorld { methods.to_string, 0, ); - let _ = Self::bind_method( + let _ = self.bind_method( proto, shape_forge, string_forge, @@ -625,16 +653,84 @@ impl BuiltinWorld { ); } + pub fn bind_function_methods( + &self, + methods: &FunctionMethods, + string_forge: &StringForge, + shape_forge: &ShapeForge, + ) { + let proto_ptr = P::as_ptr(&self.function_proto) as *mut JsObject; + let proto = unsafe { &mut *proto_ptr }; + let fp = self.fn_proto_val(); + + let _ = Self::bind_method_static( + proto, + shape_forge, + string_forge, + "call", + methods.call, + 1, + fp, + ); + let _ = Self::bind_method_static( + proto, + shape_forge, + string_forge, + "apply", + methods.apply, + 2, + fp, + ); + let _ = Self::bind_method_static( + proto, + shape_forge, + string_forge, + "bind", + methods.bind, + 1, + fp, + ); + } + pub fn bind_method( + &self, proto: &mut JsObject, shape_forge: &ShapeForge, string_forge: &StringForge, method_name: &str, native_fn_ptr: *const (), arg_count: u8, + ) -> Result<(), String> { + Self::bind_method_static( + proto, + shape_forge, + string_forge, + method_name, + native_fn_ptr, + arg_count, + self.fn_proto_val(), + ) + } + + pub fn bind_method_static( + proto: &mut JsObject, + shape_forge: &ShapeForge, + string_forge: &StringForge, + method_name: &str, + native_fn_ptr: *const (), + arg_count: u8, + wrapper_proto: JsValue, ) -> Result<(), String> { let si = string_forge.intern(method_name).0; + let wrapper_proto_ptr = if wrapper_proto.is_object() { + wrapper_proto.as_js_object_ptr() + } else { + std::ptr::null_mut() + }; let mut wrapper = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); + if !wrapper_proto_ptr.is_null() { + wrapper.set_proto(wrapper_proto).ok(); + } wrapper.set_function(true); wrapper.set_native_fn(Some(native_fn_ptr)); wrapper.set_native_arg_count(arg_count); diff --git a/crates/oxide_vm/src/builtins/function.rs b/crates/oxide_vm/src/builtins/function.rs new file mode 100644 index 00000000..8b8e61e7 --- /dev/null +++ b/crates/oxide_vm/src/builtins/function.rs @@ -0,0 +1,152 @@ +use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; +use oxide_types::object::JsObject; +use oxide_types::value::JsValue; + +use crate::native::{NativeFn, NativeResult}; +use crate::vm::Vm; + +fn invoke_target( + vm: &mut Vm, + target_val: JsValue, + this_val: JsValue, + arg_regs: &[u8], +) -> NativeResult { + if !target_val.is_object() { + return Err(JsValue::undefined()); + } + let tgt_ptr = target_val.as_js_object_ptr(); + if tgt_ptr.is_null() { + return Err(JsValue::undefined()); + } + let tgt = unsafe { &*tgt_ptr }; + if !tgt.is_function() || tgt.native_fn().is_none() { + return Err(JsValue::undefined()); + } + let func: NativeFn = unsafe { std::mem::transmute(tgt.native_fn().unwrap()) }; + + let base = 230u8; + let n = arg_regs.len(); + let mut args_buf = [0u8; 64]; + args_buf[0] = base; + vm.set_reg(base, this_val); + for (i, ®) in arg_regs.iter().enumerate().take(n.min(63)) { + vm.set_reg(base + 1 + i as u8, vm.reg(reg)); + args_buf[i + 1] = base + 1 + i as u8; + } + + func(vm, &args_buf[..n + 1]) +} + +fn bind_dispatcher(vm: &mut Vm, args: &[u8]) -> NativeResult { + let wrapper_val = vm.reg(254); + let wrapper = unsafe { &*wrapper_val.as_js_object_ptr() }; + let bound_target = wrapper.get_prop(0); + let bound_this = wrapper.get_prop(1); + + let n = args.len().saturating_sub(1); + let base = 230u8; + let mut args_buf = [0u8; 64]; + args_buf[0] = base; + vm.set_reg(base, bound_this); + for (i, ®) in args.iter().skip(1).enumerate().take(n.min(63)) { + vm.set_reg(base + 1 + i as u8, vm.reg(reg)); + args_buf[i + 1] = base + 1 + i as u8; + } + + if !bound_target.is_object() { + return Err(JsValue::undefined()); + } + let tgt_ptr = bound_target.as_js_object_ptr(); + if tgt_ptr.is_null() { + return Err(JsValue::undefined()); + } + let tgt = unsafe { &*tgt_ptr }; + if !tgt.is_function() || tgt.native_fn().is_none() { + return Err(JsValue::undefined()); + } + let func: NativeFn = unsafe { std::mem::transmute(tgt.native_fn().unwrap()) }; + func(vm, &args_buf[..n + 1]) +} + +pub fn function_call(vm: &mut Vm, args: &[u8]) -> NativeResult { + if args.is_empty() { + return Err(JsValue::undefined()); + } + let target_val = vm.reg(args[0]); + let this_val = if args.len() > 1 { + vm.reg(args[1]) + } else { + JsValue::undefined() + }; + let arg_regs: Vec = args.iter().skip(2).copied().collect(); + invoke_target(vm, target_val, this_val, &arg_regs) +} + +pub fn function_apply(vm: &mut Vm, args: &[u8]) -> NativeResult { + if args.is_empty() { + return Err(JsValue::undefined()); + } + let target_val = vm.reg(args[0]); + let this_val = if args.len() > 1 { + vm.reg(args[1]) + } else { + JsValue::undefined() + }; + + let arg_regs: Vec; + if args.len() > 2 { + let arr_val = vm.reg(args[2]); + if arr_val.is_object() { + let arr_ptr = arr_val.as_js_object_ptr(); + if !arr_ptr.is_null() { + let arr = unsafe { &*arr_ptr }; + if arr.is_array() { + let n = arr.prop_count() as usize; + let base = 200u8; + arg_regs = (0..n).map(|i| base + i as u8).collect(); + for i in 0..n { + vm.set_reg(base + i as u8, arr.get_prop(i as u8)); + } + } else { + arg_regs = Vec::new(); + } + } else { + arg_regs = Vec::new(); + } + } else { + arg_regs = Vec::new(); + } + } else { + arg_regs = Vec::new(); + } + invoke_target(vm, target_val, this_val, &arg_regs) +} + +pub fn function_bind(vm: &mut Vm, args: &[u8]) -> NativeResult { + if args.is_empty() { + return Err(JsValue::undefined()); + } + let target_val = vm.reg(args[0]); + if !target_val.is_object() { + return Err(JsValue::undefined()); + } + let bound_this = if args.len() > 1 { + vm.reg(args[1]) + } else { + JsValue::undefined() + }; + + let wrapper = vm + .epoch() + .alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); + unsafe { + (*wrapper).set_function(true); + (*wrapper).set_native_fn(Some(bind_dispatcher as *const ())); + (*wrapper).set_native_arg_count(0); + (*wrapper).set_prop_count(2); + (*wrapper).set_inline_prop(0, target_val); + (*wrapper).set_inline_prop(1, bound_this); + } + + Ok(JsValue::from_js_object(wrapper)) +} diff --git a/crates/oxide_vm/src/builtins/mod.rs b/crates/oxide_vm/src/builtins/mod.rs index fea4dc4e..b487f28e 100644 --- a/crates/oxide_vm/src/builtins/mod.rs +++ b/crates/oxide_vm/src/builtins/mod.rs @@ -1,5 +1,6 @@ pub mod array; pub mod error; +pub mod function; pub mod global; pub mod math; pub mod number; diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 4af509cf..87c34ec3 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -9,7 +9,7 @@ use oxide_compiler::opcode::{self, OpCode}; use crate::coercion; use crate::native::NativeFn; use oxide_kernel::builtin::{ - ArrayMethods, ErrorMethods, NumberMethods, ObjectMethods, StringMethods, + ArrayMethods, ErrorMethods, FunctionMethods, NumberMethods, ObjectMethods, StringMethods, }; use oxide_kernel::kernel::{KernelConfig, OxideKernel}; use oxide_kernel::prop_forge::PropTemplate; @@ -206,7 +206,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { let pi_fn = crate::builtins::number::number_parse_int as *const (); let pf_fn = crate::builtins::number::number_parse_float as *const (); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( global, kernel.shape_forge().as_ref(), kernel.string_forge().as_ref(), @@ -214,7 +214,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { pi_fn, 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( global, kernel.shape_forge().as_ref(), kernel.string_forge().as_ref(), @@ -228,7 +228,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { let sf = kernel.string_forge().as_ref(); let sh = kernel.shape_forge().as_ref(); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -236,7 +236,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_abs as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -244,7 +244,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_acos as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -252,7 +252,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_asin as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -260,7 +260,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_atan as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -268,7 +268,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_atan2 as *const (), 2, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -276,7 +276,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_cbrt as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -284,7 +284,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_ceil as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -292,7 +292,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_cos as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -300,7 +300,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_cosh as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -308,7 +308,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_exp as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -316,7 +316,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_floor as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -324,7 +324,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_hypot as *const (), 2, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -332,7 +332,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_imul as *const (), 2, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -340,7 +340,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_log as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -348,7 +348,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_log10 as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -356,7 +356,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_log2 as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -364,7 +364,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_max as *const (), 2, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -372,7 +372,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_min as *const (), 2, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -380,7 +380,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_pow as *const (), 2, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -388,7 +388,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_random as *const (), 0, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -396,7 +396,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_round as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -404,7 +404,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_sign as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -412,7 +412,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_sin as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -420,7 +420,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_sinh as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -428,7 +428,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_sqrt as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -436,7 +436,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_tan as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -444,7 +444,7 @@ pub fn init_kernel_builtins(kernel: &Arc) { crate::builtins::math::math_tanh as *const (), 1, ); - let _ = oxide_kernel::builtin::BuiltinWorld::bind_method( + let _ = kernel.builtin_world().bind_method( math, sh, sf, @@ -472,6 +472,17 @@ pub fn init_kernel_builtins(kernel: &Arc) { global.set_prop_count(cur_count + 1); global.set_prop_expand_heap(cur_count, m_val); global.bump_generation(); + + let function_methods = FunctionMethods { + call: crate::builtins::function::function_call as *const (), + apply: crate::builtins::function::function_apply as *const (), + bind: crate::builtins::function::function_bind as *const (), + }; + kernel.builtin_world().bind_function_methods( + &function_methods, + kernel.string_forge().as_ref(), + kernel.shape_forge().as_ref(), + ); } impl Vm { @@ -887,6 +898,7 @@ impl Vm { let func: NativeFn = unsafe { std::mem::transmute(obj.native_fn().unwrap()) }; + self.regs[254] = callee; match func(self, args_slice) { Ok(val) => self.regs[0] = val, Err(err_val) => { diff --git a/crates/oxide_vm/tests/builtins_function_tests.rs b/crates/oxide_vm/tests/builtins_function_tests.rs new file mode 100644 index 00000000..6d0ceace --- /dev/null +++ b/crates/oxide_vm/tests/builtins_function_tests.rs @@ -0,0 +1,34 @@ +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) +} + +#[test] +fn function_call_changes_this() { + let mut vm = Vm::new(); + let result = eval(&mut vm, "Math.max.call(null, 10, 5)").unwrap(); + assert!((result.as_double() - 10.0).abs() < 0.0001); +} + +#[test] +fn function_apply_changes_this() { + let mut vm = Vm::new(); + let result = eval(&mut vm, "Math.max.apply(null, [10, 5])").unwrap(); + assert!((result.as_double() - 10.0).abs() < 0.0001); +} + +#[test] +fn function_bind_creates_wrapper() { + let mut vm = Vm::new(); + let result = eval(&mut vm, "var b = Math.max.bind(null, 1); b(5)").unwrap(); + assert!((result.as_double() - 5.0).abs() < 0.0001); +}