diff --git a/src/binding.cc b/src/binding.cc index 2ae30fcf..2ba9f300 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -1979,9 +1979,10 @@ v8::CTypeInfo* v8__CTypeInfo__New__From__Slice(unsigned int len, v8::CFunctionInfo* v8__CFunctionInfo__New(const v8::CTypeInfo& return_info, unsigned int args_len, - v8::CTypeInfo* args_info) { + v8::CTypeInfo* args_info, + v8::CFunctionInfo::Int64Representation repr) { std::unique_ptr info = std::make_unique( - v8::CFunctionInfo(return_info, args_len, args_info)); + v8::CFunctionInfo(return_info, args_len, args_info, repr)); return info.release(); } diff --git a/src/fast_api.rs b/src/fast_api.rs index 3611c360..6cf47961 100644 --- a/src/fast_api.rs +++ b/src/fast_api.rs @@ -17,6 +17,7 @@ extern "C" { return_info: *const CTypeInfo, args_len: usize, args_info: *const CTypeInfo, + repr: Int64Representation, ) -> *mut CFunctionInfo; } @@ -34,8 +35,14 @@ impl CFunctionInfo { args: *const CTypeInfo, args_len: usize, return_type: *const CTypeInfo, + repr: Int64Representation, ) -> NonNull { - NonNull::new_unchecked(v8__CFunctionInfo__New(return_type, args_len, args)) + NonNull::new_unchecked(v8__CFunctionInfo__New( + return_type, + args_len, + args, + repr, + )) } } @@ -246,8 +253,9 @@ impl FastApiTypedArray { pub struct FastFunction { pub args: &'static [Type], - pub return_type: CType, pub function: *const c_void, + pub repr: Int64Representation, + pub return_type: CType, } impl FastFunction { @@ -259,8 +267,29 @@ impl FastFunction { ) -> Self { Self { args, - return_type, function, + repr: Int64Representation::Number, + return_type, + } + } + + pub const fn new_with_bigint( + args: &'static [Type], + return_type: CType, + function: *const c_void, + ) -> Self { + Self { + args, + function, + repr: Int64Representation::BigInt, + return_type, } } } + +#[derive(Copy, Clone, Debug)] +#[repr(u8)] +pub enum Int64Representation { + Number = 0, + BigInt = 1, +} diff --git a/src/template.rs b/src/template.rs index 82a5be85..2bffb995 100644 --- a/src/template.rs +++ b/src/template.rs @@ -578,7 +578,12 @@ impl<'s> FunctionBuilder<'s, FunctionTemplate> { let args = CTypeInfo::new_from_slice(overload1.args); let ret = CTypeInfo::new(overload1.return_type); let fn_info = unsafe { - CFunctionInfo::new(args.as_ptr(), overload1.args.len(), ret.as_ptr()) + CFunctionInfo::new( + args.as_ptr(), + overload1.args.len(), + ret.as_ptr(), + overload1.repr, + ) }; fn_info.as_ptr() }; @@ -590,7 +595,12 @@ impl<'s> FunctionBuilder<'s, FunctionTemplate> { let args = CTypeInfo::new_from_slice(overload2.args); let ret = CTypeInfo::new(overload2.return_type); let fn_info = unsafe { - CFunctionInfo::new(args.as_ptr(), overload2.args.len(), ret.as_ptr()) + CFunctionInfo::new( + args.as_ptr(), + overload2.args.len(), + ret.as_ptr(), + overload2.repr, + ) }; fn_info.as_ptr() } diff --git a/tests/test_api.rs b/tests/test_api.rs index 7a437a1e..52e4dbdd 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -9873,6 +9873,113 @@ fn test_fast_calls_onebytestring() { assert_eq!("fast", unsafe { WHO }); } +#[test] +fn test_fast_calls_i64representation() { + static mut FAST_CALL_COUNT: u32 = 0; + static mut SLOW_CALL_COUNT: u32 = 0; + fn fast_fn(_recv: v8::Local, a: u64, b: u64) -> u64 { + unsafe { FAST_CALL_COUNT += 1 }; + assert_eq!(9007199254740991, a); + assert_eq!(646, b); + a * b + } + + const FAST_TEST_NUMBER: fast_api::FastFunction = fast_api::FastFunction::new( + &[V8Value, Uint64, Uint64], + CType::Uint64, + fast_fn as _, + ); + + const FAST_TEST_BIGINT: fast_api::FastFunction = + fast_api::FastFunction::new_with_bigint( + &[V8Value, Uint64, Uint64], + CType::Uint64, + fast_fn as _, + ); + + fn slow_fn( + _: &mut v8::HandleScope, + _: v8::FunctionCallbackArguments, + _: v8::ReturnValue, + ) { + unsafe { SLOW_CALL_COUNT += 1 }; + } + + let _setup_guard = setup::parallel_test(); + let isolate = &mut v8::Isolate::new(Default::default()); + let scope = &mut v8::HandleScope::new(isolate); + let context = v8::Context::new(scope); + let scope = &mut v8::ContextScope::new(scope, context); + + let global = context.global(scope); + + let template_number = v8::FunctionTemplate::builder(slow_fn).build_fast( + scope, + &FAST_TEST_NUMBER, + None, + None, + None, + ); + let template_bigint = v8::FunctionTemplate::builder(slow_fn).build_fast( + scope, + &FAST_TEST_BIGINT, + None, + None, + None, + ); + + let name_number = v8::String::new(scope, "func_number").unwrap(); + let name_bigint = v8::String::new(scope, "func_bigint").unwrap(); + let value_number = template_number.get_function(scope).unwrap(); + let value_bigint = template_bigint.get_function(scope).unwrap(); + global + .set(scope, name_number.into(), value_number.into()) + .unwrap(); + global + .set(scope, name_bigint.into(), value_bigint.into()) + .unwrap(); + let source = r#" + function f(a, b) { return func_number(a, b); } + %PrepareFunctionForOptimization(f); + f(1, 2); +"#; + eval(scope, source).unwrap(); + assert_eq!(1, unsafe { SLOW_CALL_COUNT }); + + let source = r#" + %OptimizeFunctionOnNextCall(f); + { + const result = f(Number.MAX_SAFE_INTEGER, 646); + // Correct answer is 5818650718562680186: data is lost. + if (result != 5818650718562680000) { + throw new Error(`wrong number result: ${result}`); + } + } + "#; + eval(scope, source).unwrap(); + assert_eq!(1, unsafe { FAST_CALL_COUNT }); + + let source = r#" + function g(a, b) { return func_bigint(a, b); } + %PrepareFunctionForOptimization(g); + g(1n, 2n); +"#; + eval(scope, source).unwrap(); + assert_eq!(2, unsafe { SLOW_CALL_COUNT }); + + let source = r#" + %OptimizeFunctionOnNextCall(g); + { + const result = g(BigInt(Number.MAX_SAFE_INTEGER), 646n); + if (result != 5818650718562680186n) { + throw new Error(`wrong bigint result: ${result}`); + } + } + "#; + eval(scope, source).unwrap(); + assert_eq!(2, unsafe { FAST_CALL_COUNT }); +} + #[test] fn gc_callbacks() { let _setup_guard = setup::parallel_test();