From 2404d208f5f9e09db52889390e71bba7a88f7645 Mon Sep 17 00:00:00 2001 From: Romain Marcadier Date: Wed, 23 Feb 2022 06:23:28 +0100 Subject: [PATCH] feat: add bindings for String::kMaxLength and TypedArray::kMaxLength (#904) --- src/binding.cc | 4 ++++ src/string.rs | 9 +++++++++ src/typed_array.rs | 14 +++++++++++++ tests/test_api.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+) diff --git a/src/binding.cc b/src/binding.cc index fa094d2d..fef9e287 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -457,6 +457,8 @@ bool v8__Data__IsFunctionTemplate(const v8::Data& self) { return self.IsFunctionTemplate(); } +size_t v8__TypedArray__kMaxLength() { return v8::TypedArray::kMaxLength; } + bool v8__Value__IsUndefined(const v8::Value& self) { return self.IsUndefined(); } @@ -849,6 +851,8 @@ int v8__Name__GetIdentityHash(const v8::Name& self) { return ptr_to_local(&self)->GetIdentityHash(); } +size_t v8__String__kMaxLength() { return v8::String::kMaxLength; } + const v8::String* v8__String__Empty(v8::Isolate* isolate) { return local_to_ptr(v8::String::Empty(isolate)); } diff --git a/src/string.rs b/src/string.rs index 19a3f0ac..1a3a58b8 100644 --- a/src/string.rs +++ b/src/string.rs @@ -11,6 +11,8 @@ use crate::Local; use crate::String; extern "C" { + fn v8__String__kMaxLength() -> libc::size_t; + fn v8__String__Empty(isolate: *mut Isolate) -> *const String; fn v8__String__NewFromUtf8( @@ -115,6 +117,13 @@ bitflags! { } impl String { + /// The maximum length (in bytes) of a buffer that a v8::String can be built + /// from. Attempting to create a v8::String from a larger buffer will result + /// in None being returned. + pub fn max_length() -> usize { + unsafe { v8__String__kMaxLength() } + } + pub fn empty<'s>(scope: &mut HandleScope<'s, ()>) -> Local<'s, String> { // FIXME(bnoordhuis) v8__String__Empty() is infallible so there // is no need to box up the result, only to unwrap it again. diff --git a/src/typed_array.rs b/src/typed_array.rs index b265dbde..45b4424a 100644 --- a/src/typed_array.rs +++ b/src/typed_array.rs @@ -2,6 +2,20 @@ use crate::ArrayBuffer; use crate::HandleScope; use crate::Local; +use crate::TypedArray; + +extern "C" { + fn v8__TypedArray__kMaxLength() -> libc::size_t; +} + +impl TypedArray { + /// The maximum length (in bytes) of the buffer backing a v8::TypedArray + /// instance. Attempting to create a v8::ArrayBuffer from a larger buffer will + /// result in a fatal error. + pub fn max_length() -> usize { + unsafe { v8__TypedArray__kMaxLength() } + } +} macro_rules! typed_array { ($name:ident, $func:ident) => { diff --git a/tests/test_api.rs b/tests/test_api.rs index a5c3e405..15c3a230 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -236,6 +236,31 @@ fn test_string() { assert_eq!(4, local.utf8_length(scope)); assert_eq!("🦕", local.to_rust_string_lossy(scope)); } + { + let scope = &mut v8::HandleScope::new(isolate); + let buffer = (0..v8::String::max_length() / 4) + .map(|_| '\u{10348}') // UTF8: 0xF0 0x90 0x8D 0x88 + .collect::(); + let local = v8::String::new_from_utf8( + scope, + buffer.as_bytes(), + v8::NewStringType::Normal, + ) + .unwrap(); + // U+10348 is 2 UTF-16 code units, which is the unit of v8::String.length(). + assert_eq!(v8::String::max_length() / 2, local.length()); + assert_eq!(buffer, local.to_rust_string_lossy(scope)); + + let too_long = (0..(v8::String::max_length() / 4) + 1) + .map(|_| '\u{10348}') // UTF8: 0xF0 0x90 0x8D 0x88 + .collect::(); + let none = v8::String::new_from_utf8( + scope, + too_long.as_bytes(), + v8::NewStringType::Normal, + ); + assert!(none.is_none()); + } } #[test] @@ -3113,6 +3138,31 @@ fn typed_array_constructors() { let t = v8::BigInt64Array::new(scope, ab, 0, 0).unwrap(); assert!(t.is_big_int64_array()); + + // TypedArray::max_length() ought to be >= 2^30 < 2^32 + assert!(((2 << 30)..(2 << 32)).contains(&v8::TypedArray::max_length())); + + // v8::ArrayBuffer::new raises a fatal if the length is > kMaxLength, so we test this behavior + // through the JS side of things, where a non-fatal RangeError is thrown in such cases. + { + let scope = &mut v8::TryCatch::new(scope); + let _ = eval( + scope, + &format!("new Uint8Array({})", v8::TypedArray::max_length()), + ) + .unwrap(); + assert!(!scope.has_caught()); + } + + { + let scope = &mut v8::TryCatch::new(scope); + eval( + scope, + &format!("new Uint8Array({})", v8::TypedArray::max_length() + 1), + ); + // Array is too big (> max_length) - expecting this threw a RangeError + assert!(scope.has_caught()); + } } #[test]