diff --git a/src/bigint.rs b/src/bigint.rs new file mode 100644 index 00000000..a68838f5 --- /dev/null +++ b/src/bigint.rs @@ -0,0 +1,135 @@ +use crate::support::int; +use crate::BigInt; +use crate::Context; +use crate::HandleScope; +use crate::Isolate; +use crate::Local; + +use std::mem::MaybeUninit; + +extern "C" { + fn v8__BigInt__New(isolate: *mut Isolate, value: i64) -> *const BigInt; + fn v8__BigInt__NewFromUnsigned( + isolate: *mut Isolate, + value: u64, + ) -> *const BigInt; + fn v8__BigInt__NewFromWords( + context: *const Context, + sign_bit: int, + word_count: int, + words: *const u64, + ) -> *const BigInt; + fn v8__BigInt__Uint64Value(this: *const BigInt, lossless: *mut bool) -> u64; + fn v8__BigInt__Int64Value(this: *const BigInt, lossless: *mut bool) -> i64; + fn v8__BigInt__WordCount(this: *const BigInt) -> int; + fn v8__BigInt__ToWordsArray( + this: *const BigInt, + sign_bit: *mut int, + word_count: *mut int, + words: *mut u64, + ); +} + +impl BigInt { + pub fn new_from_i64<'s>( + scope: &mut HandleScope<'s>, + value: i64, + ) -> Local<'s, BigInt> { + unsafe { + scope.cast_local(|sd| v8__BigInt__New(sd.get_isolate_ptr(), value)) + } + .unwrap() + } + + pub fn new_from_u64<'s>( + scope: &mut HandleScope<'s>, + value: u64, + ) -> Local<'s, BigInt> { + unsafe { + scope.cast_local(|sd| { + v8__BigInt__NewFromUnsigned(sd.get_isolate_ptr(), value) + }) + } + .unwrap() + } + + /// Creates a new BigInt object using a specified sign bit and a + /// specified list of digits/words. + /// The resulting number is calculated as: + /// + /// (-1)^sign_bit * (words[0] * (2^64)^0 + words[1] * (2^64)^1 + ...) + pub fn new_from_words<'s>( + scope: &mut HandleScope<'s>, + sign_bit: bool, + words: &[u64], + ) -> Option> { + unsafe { + scope.cast_local(|sd| { + v8__BigInt__NewFromWords( + sd.get_current_context(), + sign_bit as int, + words.len() as int, + words.as_ptr(), + ) + }) + } + } + + /// Returns the value of this BigInt as an unsigned 64-bit integer, and a + /// `bool` indicating whether the return value was truncated was truncated or + /// wrapped around. In particular, it will be `false` if this BigInt is + /// negative. + pub fn u64_value(&self) -> (u64, bool) { + let mut lossless = MaybeUninit::uninit(); + let v = unsafe { v8__BigInt__Uint64Value(&*self, lossless.as_mut_ptr()) }; + let lossless = unsafe { lossless.assume_init() }; + (v, lossless) + } + + /// Returns the value of this BigInt as a signed 64-bit integer, and a `bool` + /// indicating whether this BigInt was truncated or not. + pub fn i64_value(&self) -> (i64, bool) { + let mut lossless = MaybeUninit::uninit(); + let v = unsafe { v8__BigInt__Int64Value(&*self, lossless.as_mut_ptr()) }; + let lossless = unsafe { lossless.assume_init() }; + (v, lossless) + } + + /// Returns the number of 64-bit words needed to store the result of + /// `to_words_array`. + pub fn word_count(&self) -> usize { + unsafe { v8__BigInt__WordCount(&*self) as usize } + } + + /// Converts this BigInt to a (sign_bit, words) pair. `sign_bit` will be true + /// if this BigInt is negative. If `words` has too few elements, the result will + /// be truncated to fit. + pub fn to_words_array<'a>( + &self, + words: &'a mut [u64], + ) -> (bool, &'a mut [u64]) { + let mut sign_bit = MaybeUninit::uninit(); + let mut word_count = words.len() as int; + unsafe { + v8__BigInt__ToWordsArray( + &*self, + sign_bit.as_mut_ptr(), + &mut word_count, + words.as_mut_ptr(), + ) + } + + let sign_bit = unsafe { sign_bit.assume_init() }; + debug_assert!(sign_bit == 0 || sign_bit == 1); + let word_count = word_count as usize; + + ( + sign_bit == 1, + if word_count < words.len() { + &mut words[..word_count] + } else { + words + }, + ) + } +} diff --git a/src/binding.cc b/src/binding.cc index f8d3dac5..cd78429c 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -717,6 +717,57 @@ int v8__String__WriteUtf8(const v8::String& self, v8::Isolate* isolate, return self.WriteUtf8(isolate, buffer, length, nchars_ref, options); } +const v8::Symbol* v8__Symbol__New(v8::Isolate* isolate, + const v8::String* description) { + return local_to_ptr(v8::Symbol::New(isolate, ptr_to_local(description))); +} + +const v8::Symbol* v8__Symbol__For(v8::Isolate* isolate, + const v8::String* description) { + return local_to_ptr(v8::Symbol::For(isolate, ptr_to_local(description))); +} + +const v8::Symbol* v8__Symbol__ForApi(v8::Isolate* isolate, + const v8::String* description) { + return local_to_ptr(v8::Symbol::ForApi(isolate, ptr_to_local(description))); +} + +#define V(NAME) \ + const v8::Symbol* v8__Symbol__Get##NAME(v8::Isolate* isolate) { \ + return local_to_ptr(v8::Symbol::Get##NAME(isolate)); \ + } + +V(AsyncIterator) +V(HasInstance) +V(IsConcatSpreadable) +V(Iterator) +V(Match) +V(Replace) +V(Search) +V(Split) +V(ToPrimitive) +V(ToStringTag) +V(Unscopables) +#undef V + +const v8::Value* v8__Symbol__Description(const v8::Symbol& self) { + return local_to_ptr(ptr_to_local(&self)->Description()); +} + +const v8::Private* v8__Private__New(v8::Isolate* isolate, + const v8::String* name) { + return local_to_ptr(v8::Private::New(isolate, ptr_to_local(name))); +} + +const v8::Private* v8__Private__ForApi(v8::Isolate* isolate, + const v8::String* name) { + return local_to_ptr(v8::Private::ForApi(isolate, ptr_to_local(name))); +} + +const v8::Value* v8__Private__Name(const v8::Private& self) { + return local_to_ptr(ptr_to_local(&self)->Name()); +} + void v8__Template__Set(const v8::Template& self, const v8::Name& key, const v8::Data& value, v8::PropertyAttribute attr) { ptr_to_local(&self)->Set(ptr_to_local(&key), ptr_to_local(&value), attr); @@ -948,6 +999,39 @@ const v8::Integer* v8__Integer__NewFromUnsigned(v8::Isolate* isolate, int64_t v8__Integer__Value(const v8::Integer& self) { return self.Value(); } +const v8::BigInt* v8__BigInt__New(v8::Isolate* isolate, int64_t value) { + return local_to_ptr(v8::BigInt::New(isolate, value)); +} + +const v8::BigInt* v8__BigInt__NewFromUnsigned(v8::Isolate* isolate, + uint64_t value) { + return local_to_ptr(v8::BigInt::NewFromUnsigned(isolate, value)); +} + +const v8::BigInt* v8__BigInt__NewFromWords(const v8::Context& context, + int sign_bit, int word_count, + const uint64_t* words) { + return maybe_local_to_ptr(v8::BigInt::NewFromWords( + ptr_to_local(&context), sign_bit, word_count, words)); +} + +uint64_t v8__BigInt__Uint64Value(const v8::BigInt& self, bool* lossless) { + return ptr_to_local(&self)->Uint64Value(lossless); +} + +int64_t v8__BigInt__Int64Value(const v8::BigInt& self, bool* lossless) { + return ptr_to_local(&self)->Int64Value(lossless); +} + +int v8__BigInt__WordCount(const v8::BigInt& self) { + return ptr_to_local(&self)->WordCount(); +} + +void v8__BigInt__ToWordsArray(const v8::BigInt& self, int* sign_bit, + int* word_count, uint64_t* words) { + ptr_to_local(&self)->ToWordsArray(sign_bit, word_count, words); +} + const v8::ArrayBuffer* v8__ArrayBufferView__Buffer( const v8::ArrayBufferView& self) { return local_to_ptr(ptr_to_local(&self)->Buffer()); diff --git a/src/lib.rs b/src/lib.rs index 7a92b3cd..726f03fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,6 +35,7 @@ extern crate libc; mod array_buffer; mod array_buffer_view; +mod bigint; mod context; mod data; mod date; @@ -52,6 +53,7 @@ mod object; mod platform; mod primitive_array; mod primitives; +mod private; mod promise; mod property_attribute; mod proxy; @@ -62,6 +64,7 @@ mod shared_array_buffer; mod snapshot; mod string; mod support; +mod symbol; mod template; mod uint8_array; mod value; @@ -75,6 +78,7 @@ pub mod script_compiler; pub mod V8; pub use array_buffer::*; +pub use bigint::*; pub use data::*; pub use exception::*; pub use external_references::ExternalReference; @@ -103,6 +107,7 @@ pub use platform::Task; pub use platform::TaskBase; pub use platform::TaskImpl; pub use primitives::*; +pub use private::*; pub use promise::{PromiseRejectEvent, PromiseRejectMessage, PromiseState}; pub use property_attribute::*; pub use proxy::*; @@ -120,6 +125,7 @@ pub use support::SharedPtr; pub use support::SharedRef; pub use support::UniquePtr; pub use support::UniqueRef; +pub use symbol::*; pub use template::*; // TODO(piscisaureus): Ideally this trait would not be exported. diff --git a/src/private.rs b/src/private.rs new file mode 100644 index 00000000..affb7910 --- /dev/null +++ b/src/private.rs @@ -0,0 +1,63 @@ +use crate::HandleScope; +use crate::Isolate; +use crate::Local; +use crate::Private; +use crate::String; +use crate::Value; + +extern "C" { + fn v8__Private__New( + isolate: *mut Isolate, + name: *const String, + ) -> *const Private; + fn v8__Private__ForApi( + isolate: *mut Isolate, + name: *const String, + ) -> *const Private; + fn v8__Private__Name(this: *const Private) -> *const Value; +} + +impl Private { + /// Create a private symbol. If name is not empty, it will be the description. + pub fn new<'s>( + scope: &mut HandleScope<'s>, + name: Option>, + ) -> Local<'s, Private> { + unsafe { + scope.cast_local(|sd| { + v8__Private__New( + sd.get_isolate_ptr(), + name.map_or_else(std::ptr::null, |v| &*v), + ) + }) + } + .unwrap() + } + + /// Retrieve a global private symbol. If a symbol with this name has not + /// been retrieved in the same isolate before, it is created. + /// Note that private symbols created this way are never collected, so + /// they should only be used for statically fixed properties. + /// Also, there is only one global name space for the names used as keys. + /// To minimize the potential for clashes, use qualified names as keys, + /// e.g., "Class#property". + pub fn for_api<'s>( + scope: &mut HandleScope<'s>, + name: Option>, + ) -> Local<'s, Private> { + unsafe { + scope.cast_local(|sd| { + v8__Private__ForApi( + sd.get_isolate_ptr(), + name.map_or_else(std::ptr::null, |v| &*v), + ) + }) + } + .unwrap() + } + + /// Returns the print name string of the private symbol, or undefined if none. + pub fn name<'s>(&self, scope: &mut HandleScope<'s>) -> Local<'s, Value> { + unsafe { scope.cast_local(|_| v8__Private__Name(&*self)) }.unwrap() + } +} diff --git a/src/symbol.rs b/src/symbol.rs new file mode 100644 index 00000000..dc1692d2 --- /dev/null +++ b/src/symbol.rs @@ -0,0 +1,87 @@ +use crate::HandleScope; +use crate::Isolate; +use crate::Local; +use crate::String; +use crate::Symbol; +use crate::Value; + +extern "C" { + fn v8__Symbol__New( + isolate: *mut Isolate, + description: *const String, + ) -> *const Symbol; + fn v8__Symbol__ForApi( + isolate: *mut Isolate, + description: *const String, + ) -> *const Symbol; + fn v8__Symbol__Description(this: *const Symbol) -> *const Value; +} + +macro_rules! well_known { + ($name:ident, $binding:ident) => { + pub fn $name<'s>(scope: &mut HandleScope<'s>) -> Local<'s, Symbol> { + extern "C" { + fn $binding(isolate: *mut Isolate) -> *const Symbol; + } + unsafe { scope.cast_local(|sd| $binding(sd.get_isolate_ptr())) }.unwrap() + } + }; +} + +impl Symbol { + /// Create a symbol. If description is not empty, it will be used as the + /// description. + pub fn new<'s>( + scope: &mut HandleScope<'s>, + description: Option>, + ) -> Local<'s, Symbol> { + unsafe { + scope.cast_local(|sd| { + v8__Symbol__New( + sd.get_isolate_ptr(), + description.map_or_else(std::ptr::null, |v| &*v), + ) + }) + } + .unwrap() + } + + /// Access global symbol registry. + /// Note that symbols created this way are never collected, so + /// they should only be used for statically fixed properties. + /// Also, there is only one global description space for the descriptions used as + /// keys. + /// To minimize the potential for clashes, use qualified descriptions as keys. + /// Corresponds to v8::Symbol::For() in C++. + pub fn for_global<'s>( + scope: &mut HandleScope<'s>, + description: Local, + ) -> Local<'s, Symbol> { + unsafe { + scope.cast_local(|sd| { + v8__Symbol__ForApi(sd.get_isolate_ptr(), &*description) + }) + } + .unwrap() + } + + /// Returns the description string of the symbol, or undefined if none. + pub fn description<'s>( + &self, + scope: &mut HandleScope<'s>, + ) -> Local<'s, Value> { + unsafe { scope.cast_local(|_| v8__Symbol__Description(&*self)) }.unwrap() + } + + well_known!(get_async_iterator, v8__Symbol__GetAsyncIterator); + well_known!(get_has_instance, v8__Symbol__GetHasInstance); + well_known!(get_is_concat_spreadable, v8__Symbol__GetIsConcatSpreadable); + well_known!(get_iterator, v8__Symbol__GetIterator); + well_known!(get_match, v8__Symbol__GetMatch); + well_known!(get_replace, v8__Symbol__GetReplace); + well_known!(get_search, v8__Symbol__GetSearch); + well_known!(get_split, v8__Symbol__GetSplit); + well_known!(get_to_primitive, v8__Symbol__GetToPrimitive); + well_known!(get_to_string_tag, v8__Symbol__GetToStringTag); + well_known!(get_unscopables, v8__Symbol__GetUnscopables); +} diff --git a/tests/test_api.rs b/tests/test_api.rs index b396b235..b2f61495 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -2810,16 +2810,22 @@ fn try_from_data() { v8::Local::::try_from(d).unwrap() == object_template ); - // There is currently no way to construct instances of `v8::Private`, - // therefore we don't have a test where `is_private()` succeeds. + let p: v8::Local = v8::Private::new(scope, None).into(); + assert!(!p.is_function_template()); + assert!(!p.is_module()); + assert!(!p.is_object_template()); + assert!(p.is_private()); + assert!(!p.is_value()); let values: &[v8::Local] = &[ v8::null(scope).into(), v8::undefined(scope).into(), + v8::BigInt::new_from_u64(scope, 1337).into(), v8::Boolean::new(scope, true).into(), v8::Function::new(scope, function_callback).unwrap().into(), v8::Number::new(scope, 42.0).into(), v8::Object::new(scope).into(), + v8::Symbol::new(scope, None).into(), v8::String::new(scope, "hello").unwrap().into(), ]; for &v in values { @@ -3644,3 +3650,97 @@ fn date() { assert_eq!(date.value_of(), 3.0); assert_eq!(date.number_value(scope).unwrap(), 3.0); } + +#[test] +fn symbol() { + let _setup_guard = setup(); + 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 desc = v8::String::new(scope, "a description").unwrap(); + + let s = v8::Symbol::new(scope, None); + assert!(s.description(scope) == v8::undefined(scope)); + + let s = v8::Symbol::new(scope, Some(desc)); + assert!(s.description(scope) == desc); + + let s_pub = v8::Symbol::for_global(scope, desc); + assert!(s_pub.description(scope) == desc); + assert!(s_pub != s); + + let s_pub2 = v8::Symbol::for_global(scope, desc); + assert!(s_pub2 != s); + assert!(s_pub == s_pub2); + + let s = eval(scope, "Symbol.asyncIterator").unwrap(); + assert!(s == v8::Symbol::get_async_iterator(scope)); +} + +#[test] +fn private() { + let _setup_guard = setup(); + 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 p = v8::Private::new(scope, None); + assert!(p.name(scope) == v8::undefined(scope)); + + let name = v8::String::new(scope, "a name").unwrap(); + let p = v8::Private::new(scope, Some(name)); + assert!(p.name(scope) == name); + + let p_api = v8::Private::for_api(scope, Some(name)); + assert!(p_api.name(scope) == name); + assert!(p_api != p); + + let p_api2 = v8::Private::for_api(scope, Some(name)); + assert!(p_api2 != p); + assert!(p_api == p_api2); +} + +#[test] +fn bigint() { + let _setup_guard = setup(); + 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 b = v8::BigInt::new_from_u64(scope, 1337); + assert_eq!(b.u64_value(), (1337, true)); + + let b = v8::BigInt::new_from_i64(scope, -1337); + assert_eq!(b.i64_value(), (-1337, true)); + + let words = vec![10, 10]; + let b = v8::BigInt::new_from_words(scope, false, &words).unwrap(); + assert_eq!(b.i64_value(), (10, false)); + + let raw_b = eval(scope, "184467440737095516170n").unwrap(); + assert!(b == raw_b); + + let b = v8::BigInt::new_from_words(scope, true, &words).unwrap(); + assert_eq!(b.i64_value(), (-10, false)); + + let raw_b = eval(scope, "-184467440737095516170n").unwrap(); + assert!(b == raw_b); + + let raw_b = v8::Local::::try_from(raw_b).unwrap(); + + let mut vec = Vec::new(); + vec.resize(raw_b.word_count(), 0); + assert_eq!(raw_b.to_words_array(&mut vec), (true, &mut [10, 10][..])); + + let mut vec = Vec::new(); + vec.resize(1, 0); + assert_eq!(raw_b.to_words_array(&mut vec), (true, &mut [10][..])); + + let mut vec = Vec::new(); + vec.resize(20, 1337); + assert_eq!(raw_b.to_words_array(&mut vec), (true, &mut [10, 10][..])); +}