Skip to content

Commit

Permalink
feat: Bring back deprecated v8::String API (#1694)
Browse files Browse the repository at this point in the history
Brings back String APIs removed in #1692
and renamed the new APIs to have _v2 suffix.
  • Loading branch information
bartlomieju authored Feb 7, 2025
1 parent 45edd18 commit 79c94a9
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 23 deletions.
36 changes: 28 additions & 8 deletions src/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@

using namespace support;

// TODO(bartlomieju): ideally we could ignore only some of the deprecated APIs
#pragma clang diagnostic ignored "-Wdeprecated-declarations"

template <typename T>
constexpr size_t align_to(size_t size) {
return (size + sizeof(T) - 1) & ~(sizeof(T) - 1);
Expand Down Expand Up @@ -1082,20 +1085,37 @@ int v8__String__Utf8Length(const v8::String& self, v8::Isolate* isolate) {
return self.Utf8LengthV2(isolate);
}

void v8__String__Write(const v8::String& self, v8::Isolate* isolate,
uint32_t offset, uint32_t length, uint16_t* buffer,
int flags) {
int v8__String__Write(const v8::String& self, v8::Isolate* isolate,
uint16_t* buffer, int start, int length, int options) {
return self.Write(isolate, buffer, start, length, options);
}

void v8__String__Write_v2(const v8::String& self, v8::Isolate* isolate,
uint32_t offset, uint32_t length, uint16_t* buffer,
int flags) {
return self.WriteV2(isolate, offset, length, buffer, flags);
}

void v8__String__WriteOneByte(const v8::String& self, v8::Isolate* isolate,
uint32_t offset, uint32_t length, uint8_t* buffer,
int flags) {
int v8__String__WriteOneByte(const v8::String& self, v8::Isolate* isolate,
uint8_t* buffer, int start, int length,
int options) {
return self.WriteOneByte(isolate, buffer, start, length, options);
}

void v8__String__WriteOneByte_v2(const v8::String& self, v8::Isolate* isolate,
uint32_t offset, uint32_t length,
uint8_t* buffer, int flags) {
return self.WriteOneByteV2(isolate, offset, length, buffer, flags);
}

size_t v8__String__WriteUtf8(const v8::String& self, v8::Isolate* isolate,
char* buffer, size_t capacity, int flags) {
int v8__String__WriteUtf8(const v8::String& self, v8::Isolate* isolate,
char* buffer, int length, int* nchars_ref,
int options) {
return self.WriteUtf8(isolate, buffer, length, nchars_ref, options);
}

size_t v8__String__WriteUtf8_v2(const v8::String& self, v8::Isolate* isolate,
char* buffer, size_t capacity, int flags) {
return self.WriteUtf8V2(isolate, buffer, capacity, flags);
}

Expand Down
188 changes: 176 additions & 12 deletions src/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ extern "C" {
fn v8__String__Utf8Length(this: *const String, isolate: *mut Isolate) -> int;

fn v8__String__Write(
this: *const String,
isolate: *mut Isolate,
buffer: *mut u16,
start: int,
length: int,
options: WriteOptions,
) -> int;

fn v8__String__Write_v2(
this: *const String,
isolate: *mut Isolate,
offset: u32,
Expand All @@ -54,6 +63,15 @@ extern "C" {
);

fn v8__String__WriteOneByte(
this: *const String,
isolate: *mut Isolate,
buffer: *mut u8,
start: int,
length: int,
options: WriteOptions,
) -> int;

fn v8__String__WriteOneByte_v2(
this: *const String,
isolate: *mut Isolate,
offset: u32,
Expand All @@ -63,6 +81,15 @@ extern "C" {
);

fn v8__String__WriteUtf8(
this: *const String,
isolate: *mut Isolate,
buffer: *mut char,
length: int,
nchars_ref: *mut int,
options: WriteOptions,
) -> int;

fn v8__String__WriteUtf8_v2(
this: *const String,
isolate: *mut Isolate,
buffer: *mut char,
Expand Down Expand Up @@ -325,6 +352,21 @@ pub enum NewStringType {
Internalized,
}

bitflags! {
#[derive(Clone, Copy, Default)]
#[repr(transparent)]
pub struct WriteOptions: int {
const NO_OPTIONS = 0;
const HINT_MANY_WRITES_EXPECTED = 1;
const NO_NULL_TERMINATION = 2;
const PRESERVE_ONE_BYTE_NULL = 4;
// Used by WriteUtf8 to replace orphan surrogate code units with the
// unicode replacement character. Needs to be set to guarantee valid UTF-8
// output.
const REPLACE_INVALID_UTF8 = 8;
}
}

bitflags! {
#[derive(Clone, Copy, Default)]
#[repr(transparent)]
Expand Down Expand Up @@ -430,15 +472,38 @@ impl String {
/// Writes the contents of the string to an external buffer, as 16-bit
/// (UTF-16) character codes.
#[inline(always)]
#[deprecated = "Use `v8::String::write_v2` instead"]
pub fn write(
&self,
scope: &mut Isolate,
buffer: &mut [u16],
start: usize,
options: WriteOptions,
) -> usize {
unsafe {
v8__String__Write(
self,
scope,
buffer.as_mut_ptr(),
start.try_into().unwrap_or(int::MAX),
buffer.len().try_into().unwrap_or(int::MAX),
options,
) as usize
}
}

/// Writes the contents of the string to an external buffer, as 16-bit
/// (UTF-16) character codes.
#[inline(always)]
pub fn write_v2(
&self,
scope: &mut Isolate,
offset: u32,
buffer: &mut [u16],
flags: WriteFlags,
) {
unsafe {
v8__String__Write(
v8__String__Write_v2(
self,
scope,
offset,
Expand All @@ -452,15 +517,38 @@ impl String {
/// Writes the contents of the string to an external buffer, as one-byte
/// (Latin-1) characters.
#[inline(always)]
#[deprecated = "Use `v8::String::write_one_byte_v2` instead."]
pub fn write_one_byte(
&self,
scope: &mut Isolate,
buffer: &mut [u8],
start: usize,
options: WriteOptions,
) -> usize {
unsafe {
v8__String__WriteOneByte(
self,
scope,
buffer.as_mut_ptr(),
start.try_into().unwrap_or(int::MAX),
buffer.len().try_into().unwrap_or(int::MAX),
options,
) as usize
}
}

/// Writes the contents of the string to an external buffer, as one-byte
/// (Latin-1) characters.
#[inline(always)]
pub fn write_one_byte_v2(
&self,
scope: &mut Isolate,
offset: u32,
buffer: &mut [u8],
flags: WriteFlags,
) {
unsafe {
v8__String__WriteOneByte(
v8__String__WriteOneByte_v2(
self,
scope,
offset,
Expand All @@ -474,15 +562,38 @@ impl String {
/// Writes the contents of the string to an external [`MaybeUninit`] buffer, as one-byte
/// (Latin-1) characters.
#[inline(always)]
#[deprecated = "Use `v8::String::write_one_byte_uninit_v2` instead."]
pub fn write_one_byte_uninit(
&self,
scope: &mut Isolate,
buffer: &mut [MaybeUninit<u8>],
start: usize,
options: WriteOptions,
) -> usize {
unsafe {
v8__String__WriteOneByte(
self,
scope,
buffer.as_mut_ptr() as *mut u8,
start.try_into().unwrap_or(int::MAX),
buffer.len().try_into().unwrap_or(int::MAX),
options,
) as usize
}
}

/// Writes the contents of the string to an external [`MaybeUninit`] buffer, as one-byte
/// (Latin-1) characters.
#[inline(always)]
pub fn write_one_byte_uninit_v2(
&self,
scope: &mut Isolate,
offset: u32,
buffer: &mut [MaybeUninit<u8>],
flags: WriteFlags,
) {
unsafe {
v8__String__WriteOneByte(
v8__String__WriteOneByte_v2(
self,
scope,
offset,
Expand All @@ -495,7 +606,31 @@ impl String {

/// Writes the contents of the string to an external buffer, as UTF-8.
#[inline(always)]
#[deprecated = "Use `v8::String::write_utf8_v2` instead."]
pub fn write_utf8(
&self,
scope: &mut Isolate,
buffer: &mut [u8],
nchars_ref: Option<&mut usize>,
options: WriteOptions,
) -> usize {
unsafe {
// SAFETY:
// We assume that v8 will overwrite the buffer without de-initializing any byte in it.
// So the type casting of the buffer is safe.
let buffer = {
let len = buffer.len();
let data = buffer.as_mut_ptr().cast();
slice::from_raw_parts_mut(data, len)
};
#[allow(deprecated)]
self.write_utf8_uninit(scope, buffer, nchars_ref, options)
}
}

/// Writes the contents of the string to an external buffer, as UTF-8.
#[inline(always)]
pub fn write_utf8_v2(
&self,
scope: &mut Isolate,
buffer: &mut [u8],
Expand All @@ -511,19 +646,45 @@ impl String {
let data = buffer.as_mut_ptr().cast();
slice::from_raw_parts_mut(data, len)
};
self.write_utf8_uninit(scope, buffer, flags)
self.write_utf8_uninit_v2(scope, buffer, flags)
}
}

/// Writes the contents of the string to an external [`MaybeUninit`] buffer, as UTF-8.
#[deprecated = "Use `v8::String::write_utf8_uninit_v2` instead."]
pub fn write_utf8_uninit(
&self,
scope: &mut Isolate,
buffer: &mut [MaybeUninit<u8>],
flags: WriteFlags,
nchars_ref: Option<&mut usize>,
options: WriteOptions,
) -> usize {
let mut nchars_ref_int: int = 0;
let bytes = unsafe {
v8__String__WriteUtf8(
self,
scope,
buffer.as_mut_ptr() as *mut char,
buffer.len().try_into().unwrap_or(int::MAX),
&mut nchars_ref_int,
options,
)
};
if let Some(r) = nchars_ref {
*r = nchars_ref_int as usize;
}
bytes as usize
}

/// Writes the contents of the string to an external [`MaybeUninit`] buffer, as UTF-8.
pub fn write_utf8_uninit_v2(
&self,
scope: &mut Isolate,
buffer: &mut [MaybeUninit<u8>],
flags: WriteFlags,
) -> usize {
let bytes = unsafe {
v8__String__WriteUtf8_v2(
self,
scope,
buffer.as_mut_ptr() as _,
Expand Down Expand Up @@ -791,7 +952,7 @@ impl String {
let buffer = std::ptr::slice_from_raw_parts_mut(data, len_utf16);

// Write to this MaybeUninit buffer, assuming we're going to fill this entire buffer
self.write_one_byte_uninit(
self.write_one_byte_uninit_v2(
scope,
0,
&mut *buffer,
Expand All @@ -816,7 +977,7 @@ impl String {
let buffer = std::ptr::slice_from_raw_parts_mut(data, len_utf8);

// Write to this MaybeUninit buffer, assuming we're going to fill this entire buffer
let length = self.write_utf8_uninit(
let length = self.write_utf8_uninit_v2(
scope,
&mut *buffer,
WriteFlags::kReplaceInvalidUtf8,
Expand Down Expand Up @@ -850,7 +1011,7 @@ impl String {
// string is 100% 7-bit ASCII.
if self.is_onebyte() && len_utf8 == len_utf16 {
if len_utf16 <= N {
self.write_one_byte_uninit(scope, 0, buffer, WriteFlags::empty());
self.write_one_byte_uninit_v2(scope, 0, buffer, WriteFlags::empty());
unsafe {
// Get a slice of &[u8] of what we know is initialized now
let buffer = &mut buffer[..len_utf16];
Expand All @@ -869,7 +1030,7 @@ impl String {
let buffer = std::ptr::slice_from_raw_parts_mut(data, len_utf16);

// Write to this MaybeUninit buffer, assuming we're going to fill this entire buffer
self.write_one_byte_uninit(
self.write_one_byte_uninit_v2(
scope,
0,
&mut *buffer,
Expand All @@ -886,8 +1047,11 @@ impl String {

if len_utf8 <= N {
// No malloc path
let length =
self.write_utf8_uninit(scope, buffer, WriteFlags::kReplaceInvalidUtf8);
let length = self.write_utf8_uninit_v2(
scope,
buffer,
WriteFlags::kReplaceInvalidUtf8,
);
debug_assert!(length == len_utf8);

// SAFETY: We know that we wrote `length` UTF-8 bytes. See `slice_assume_init_mut` for additional guarantee information.
Expand All @@ -911,7 +1075,7 @@ impl String {
let buffer = std::ptr::slice_from_raw_parts_mut(data, len_utf8);

// Write to this MaybeUninit buffer, assuming we're going to fill this entire buffer
let length = self.write_utf8_uninit(
let length = self.write_utf8_uninit_v2(
scope,
&mut *buffer,
WriteFlags::kReplaceInvalidUtf8,
Expand Down
Loading

0 comments on commit 79c94a9

Please sign in to comment.