mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 09:31:22 -05:00
refactor(ops): op2 supports strings in argument and return position (#19613)
Support strings (&str, String, and Cow) in the argument position and String in the return position. Avoids copies where possible, though this is not always something we can do.
This commit is contained in:
parent
0f719aa79c
commit
6afdcf59b8
17 changed files with 710 additions and 20 deletions
|
@ -144,6 +144,9 @@ pub mod _ops {
|
|||
pub use super::runtime::ops::queue_async_op;
|
||||
pub use super::runtime::ops::queue_fast_async_op;
|
||||
pub use super::runtime::ops::to_i32;
|
||||
pub use super::runtime::ops::to_str;
|
||||
pub use super::runtime::ops::to_str_ptr;
|
||||
pub use super::runtime::ops::to_string_ptr;
|
||||
pub use super::runtime::ops::to_u32;
|
||||
pub use super::runtime::V8_WRAPPER_OBJECT_INDEX;
|
||||
pub use super::runtime::V8_WRAPPER_TYPE_INDEX;
|
||||
|
|
|
@ -7,8 +7,10 @@ use futures::future::Either;
|
|||
use futures::future::Future;
|
||||
use futures::future::FutureExt;
|
||||
use futures::task::noop_waker_ref;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::future::ready;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::option::Option;
|
||||
use std::task::Context;
|
||||
use std::task::Poll;
|
||||
|
@ -197,6 +199,104 @@ pub fn to_i64(number: &v8::Value) -> i32 {
|
|||
0
|
||||
}
|
||||
|
||||
/// Expands `inbuf` to `outbuf`, assuming that `outbuf` has at least 2x `input_length`.
|
||||
#[inline(always)]
|
||||
unsafe fn latin1_to_utf8(
|
||||
input_length: usize,
|
||||
inbuf: *const u8,
|
||||
outbuf: *mut u8,
|
||||
) -> usize {
|
||||
let mut output = 0;
|
||||
let mut input = 0;
|
||||
while input < input_length {
|
||||
let char = *(inbuf.add(input));
|
||||
if char < 0x80 {
|
||||
*(outbuf.add(output)) = char;
|
||||
output += 1;
|
||||
} else {
|
||||
// Top two bits
|
||||
*(outbuf.add(output)) = (char >> 6) | 0b1100_0000;
|
||||
// Bottom six bits
|
||||
*(outbuf.add(output + 1)) = (char & 0b0011_1111) | 0b1000_0000;
|
||||
output += 2;
|
||||
}
|
||||
input += 1;
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
/// Converts a [`v8::fast_api::FastApiOneByteString`] to either an owned string, or a borrowed string, depending on whether it fits into the
|
||||
/// provided buffer.
|
||||
pub fn to_str_ptr<'a, const N: usize>(
|
||||
string: &mut v8::fast_api::FastApiOneByteString,
|
||||
buffer: &'a mut [MaybeUninit<u8>; N],
|
||||
) -> Cow<'a, str> {
|
||||
let input_buf = string.as_bytes();
|
||||
let input_len = input_buf.len();
|
||||
let output_len = buffer.len();
|
||||
|
||||
// We know that this string is full of either one or two-byte UTF-8 chars, so if it's < 1/2 of N we
|
||||
// can skip the ASCII check and just start copying.
|
||||
if input_len < N / 2 {
|
||||
debug_assert!(output_len >= input_len * 2);
|
||||
let buffer = buffer.as_mut_ptr() as *mut u8;
|
||||
|
||||
let written =
|
||||
// SAFETY: We checked that buffer is at least 2x the size of input_buf
|
||||
unsafe { latin1_to_utf8(input_buf.len(), input_buf.as_ptr(), buffer) };
|
||||
|
||||
debug_assert!(written <= output_len);
|
||||
|
||||
let slice = std::ptr::slice_from_raw_parts(buffer, written);
|
||||
// SAFETY: We know it's valid UTF-8, so make a string
|
||||
Cow::Borrowed(unsafe { std::str::from_utf8_unchecked(&*slice) })
|
||||
} else {
|
||||
// TODO(mmastrac): We could be smarter here about not allocating
|
||||
Cow::Owned(to_string_ptr(string))
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a [`v8::fast_api::FastApiOneByteString`] to an owned string. May over-allocate to avoid
|
||||
/// re-allocation.
|
||||
pub fn to_string_ptr(
|
||||
string: &mut v8::fast_api::FastApiOneByteString,
|
||||
) -> String {
|
||||
let input_buf = string.as_bytes();
|
||||
let capacity = input_buf.len() * 2;
|
||||
|
||||
// SAFETY: We're allocating a buffer of 2x the input size, writing valid UTF-8, then turning that into a string
|
||||
unsafe {
|
||||
// Create an uninitialized buffer of `capacity` bytes. We need to be careful here to avoid
|
||||
// accidentally creating a slice of u8 which would be invalid.
|
||||
let layout = std::alloc::Layout::from_size_align(capacity, 1).unwrap();
|
||||
let out = std::alloc::alloc(layout);
|
||||
|
||||
let written = latin1_to_utf8(input_buf.len(), input_buf.as_ptr(), out);
|
||||
|
||||
debug_assert!(written <= capacity);
|
||||
// We know it's valid UTF-8, so make a string
|
||||
String::from_raw_parts(out, written, capacity)
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a [`v8::String`] to either an owned string, or a borrowed string, depending on whether it fits into the
|
||||
/// provided buffer.
|
||||
#[inline(always)]
|
||||
pub fn to_str<'a, const N: usize>(
|
||||
scope: &mut v8::Isolate,
|
||||
string: &v8::Value,
|
||||
buffer: &'a mut [MaybeUninit<u8>; N],
|
||||
) -> Cow<'a, str> {
|
||||
if !string.is_string() {
|
||||
return Cow::Borrowed("");
|
||||
}
|
||||
|
||||
// SAFETY: We checked is_string above
|
||||
let string: &v8::String = unsafe { std::mem::transmute(string) };
|
||||
|
||||
string.to_rust_cow_lossy(scope, buffer)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::error::generic_error;
|
||||
|
@ -206,6 +306,7 @@ mod tests {
|
|||
use crate::JsRuntime;
|
||||
use crate::RuntimeOptions;
|
||||
use deno_ops::op2;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::Cell;
|
||||
|
||||
crate::extension!(
|
||||
|
@ -219,6 +320,13 @@ mod tests {
|
|||
op_test_result_void_err,
|
||||
op_test_result_primitive_ok,
|
||||
op_test_result_primitive_err,
|
||||
op_test_string_owned,
|
||||
op_test_string_ref,
|
||||
op_test_string_cow,
|
||||
op_test_string_roundtrip_char,
|
||||
op_test_string_return,
|
||||
op_test_string_option_return,
|
||||
op_test_string_roundtrip,
|
||||
op_test_generics<String>,
|
||||
]
|
||||
);
|
||||
|
@ -229,18 +337,11 @@ mod tests {
|
|||
|
||||
#[op2(core, fast)]
|
||||
pub fn op_test_fail() {
|
||||
FAIL.with(|b| {
|
||||
println!("fail");
|
||||
b.set(true)
|
||||
})
|
||||
FAIL.with(|b| b.set(true))
|
||||
}
|
||||
|
||||
/// Run a test for a single op.
|
||||
fn run_test2(
|
||||
repeat: usize,
|
||||
op: &'static str,
|
||||
test: &'static str,
|
||||
) -> Result<(), AnyError> {
|
||||
fn run_test2(repeat: usize, op: &str, test: &str) -> Result<(), AnyError> {
|
||||
let mut runtime = JsRuntime::new(RuntimeOptions {
|
||||
extensions: vec![testing::init_ops_and_esm()],
|
||||
..Default::default()
|
||||
|
@ -278,7 +379,7 @@ mod tests {
|
|||
),
|
||||
)?;
|
||||
if FAIL.with(|b| b.get()) {
|
||||
Err(generic_error("test failed"))
|
||||
Err(generic_error(format!("{op} test failed ({test})")))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
@ -406,6 +507,127 @@ mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[op2(core, fast)]
|
||||
pub fn op_test_string_owned(#[string] s: String) -> u32 {
|
||||
s.len() as _
|
||||
}
|
||||
|
||||
#[op2(core, fast)]
|
||||
pub fn op_test_string_ref(#[string] s: &str) -> u32 {
|
||||
s.len() as _
|
||||
}
|
||||
|
||||
#[op2(core, fast)]
|
||||
pub fn op_test_string_cow(#[string] s: Cow<str>) -> u32 {
|
||||
s.len() as _
|
||||
}
|
||||
|
||||
#[op2(core, fast)]
|
||||
pub fn op_test_string_roundtrip_char(#[string] s: Cow<str>) -> u32 {
|
||||
s.chars().next().unwrap() as u32
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
pub async fn test_op_strings() -> Result<(), Box<dyn std::error::Error>> {
|
||||
for op in [
|
||||
"op_test_string_owned",
|
||||
"op_test_string_cow",
|
||||
"op_test_string_ref",
|
||||
] {
|
||||
for (len, str) in [
|
||||
// ASCII
|
||||
(3, "'abc'"),
|
||||
// Latin-1 (one byte but two UTF-8 chars)
|
||||
(2, "'\\u00a0'"),
|
||||
// ASCII
|
||||
(10000, "'a'.repeat(10000)"),
|
||||
// Latin-1
|
||||
(20000, "'\\u00a0'.repeat(10000)"),
|
||||
// 4-byte UTF-8 emoji (1F995 = 🦕)
|
||||
(40000, "'\\u{1F995}'.repeat(10000)"),
|
||||
] {
|
||||
let test = format!("assert({op}({str}) == {len})");
|
||||
run_test2(10000, op, &test)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that we're correctly encoding UTF-8
|
||||
run_test2(
|
||||
10000,
|
||||
"op_test_string_roundtrip_char",
|
||||
"assert(op_test_string_roundtrip_char('\\u00a0') == 0xa0)",
|
||||
)?;
|
||||
run_test2(
|
||||
10000,
|
||||
"op_test_string_roundtrip_char",
|
||||
"assert(op_test_string_roundtrip_char('\\u00ff') == 0xff)",
|
||||
)?;
|
||||
run_test2(
|
||||
10000,
|
||||
"op_test_string_roundtrip_char",
|
||||
"assert(op_test_string_roundtrip_char('\\u0080') == 0x80)",
|
||||
)?;
|
||||
run_test2(
|
||||
10000,
|
||||
"op_test_string_roundtrip_char",
|
||||
"assert(op_test_string_roundtrip_char('\\u0100') == 0x100)",
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[op2(core)]
|
||||
#[string]
|
||||
pub fn op_test_string_return(
|
||||
#[string] a: Cow<str>,
|
||||
#[string] b: Cow<str>,
|
||||
) -> String {
|
||||
(a + b).to_string()
|
||||
}
|
||||
|
||||
#[op2(core)]
|
||||
#[string]
|
||||
pub fn op_test_string_option_return(
|
||||
#[string] a: Cow<str>,
|
||||
#[string] b: Cow<str>,
|
||||
) -> Option<String> {
|
||||
if a == "none" {
|
||||
return None;
|
||||
}
|
||||
Some((a + b).to_string())
|
||||
}
|
||||
|
||||
#[op2(core)]
|
||||
#[string]
|
||||
pub fn op_test_string_roundtrip(#[string] s: String) -> String {
|
||||
s
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
pub async fn test_op_string_returns() -> Result<(), Box<dyn std::error::Error>>
|
||||
{
|
||||
run_test2(
|
||||
1,
|
||||
"op_test_string_return",
|
||||
"assert(op_test_string_return('a', 'b') == 'ab')",
|
||||
)?;
|
||||
run_test2(
|
||||
1,
|
||||
"op_test_string_option_return",
|
||||
"assert(op_test_string_option_return('a', 'b') == 'ab')",
|
||||
)?;
|
||||
run_test2(
|
||||
1,
|
||||
"op_test_string_option_return",
|
||||
"assert(op_test_string_option_return('none', 'b') == null)",
|
||||
)?;
|
||||
run_test2(
|
||||
1,
|
||||
"op_test_string_roundtrip",
|
||||
"assert(op_test_string_roundtrip('\\u0080\\u00a0\\u00ff') == '\\u0080\\u00a0\\u00ff')",
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// We don't actually test this one -- we just want it to compile
|
||||
#[op2(core, fast)]
|
||||
pub fn op_test_generics<T: Clone>() {}
|
||||
|
|
|
@ -4,10 +4,13 @@ use super::signature::Arg;
|
|||
use super::signature::NumericArg;
|
||||
use super::signature::ParsedSignature;
|
||||
use super::signature::RetVal;
|
||||
use super::signature::Special;
|
||||
use super::V8MappingError;
|
||||
use proc_macro2::Ident;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::format_ident;
|
||||
use quote::quote;
|
||||
use std::iter::zip;
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
|
@ -49,10 +52,12 @@ impl V8FastCallType {
|
|||
V8FastCallType::CallbackOptions => {
|
||||
quote!(*mut #deno_core::v8::fast_api::FastApiCallbackOptions)
|
||||
}
|
||||
V8FastCallType::SeqOneByteString => {
|
||||
quote!(*mut #deno_core::v8::fast_api::FastApiOneByteString)
|
||||
}
|
||||
V8FastCallType::Uint8Array
|
||||
| V8FastCallType::Uint32Array
|
||||
| V8FastCallType::Float64Array
|
||||
| V8FastCallType::SeqOneByteString => unreachable!(),
|
||||
| V8FastCallType::Float64Array => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,7 +195,10 @@ pub fn generate_dispatch_fast(
|
|||
.map(|rv| rv.quote_rust_type(deno_core))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let call_args = names.clone();
|
||||
let call_idents = names.clone();
|
||||
let call_args = zip(names.iter(), signature.args.iter())
|
||||
.map(|(name, arg)| map_v8_fastcall_arg_to_arg(deno_core, name, arg))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let with_fast_api_callback_options = if *needs_fast_api_callback_options {
|
||||
types.push(V8FastCallType::CallbackOptions.quote_rust_type(deno_core));
|
||||
|
@ -219,7 +227,8 @@ pub fn generate_dispatch_fast(
|
|||
) -> #output_type {
|
||||
#with_fast_api_callback_options
|
||||
#with_opctx
|
||||
let #result = Self::call(#(#call_args as _),*);
|
||||
#(#call_args)*
|
||||
let #result = Self::call(#(#call_idents),*);
|
||||
#handle_error
|
||||
#result
|
||||
}
|
||||
|
@ -228,6 +237,32 @@ pub fn generate_dispatch_fast(
|
|||
Ok(Some((fast_definition, fast_fn)))
|
||||
}
|
||||
|
||||
fn map_v8_fastcall_arg_to_arg(
|
||||
deno_core: &TokenStream,
|
||||
arg_ident: &Ident,
|
||||
arg: &Arg,
|
||||
) -> TokenStream {
|
||||
let arg_temp = format_ident!("{}_temp", arg_ident);
|
||||
match arg {
|
||||
Arg::Special(Special::RefStr) => {
|
||||
quote! {
|
||||
let mut #arg_temp: [::std::mem::MaybeUninit<u8>; 1024] = [::std::mem::MaybeUninit::uninit(); 1024];
|
||||
let #arg_ident = &#deno_core::_ops::to_str_ptr(unsafe { &mut *#arg_ident }, &mut #arg_temp);
|
||||
}
|
||||
}
|
||||
Arg::Special(Special::String) => {
|
||||
quote!(let #arg_ident = #deno_core::_ops::to_string_ptr(unsafe { &mut *#arg_ident });)
|
||||
}
|
||||
Arg::Special(Special::CowStr) => {
|
||||
quote! {
|
||||
let mut #arg_temp: [::std::mem::MaybeUninit<u8>; 1024] = [::std::mem::MaybeUninit::uninit(); 1024];
|
||||
let #arg_ident = #deno_core::_ops::to_str_ptr(unsafe { &mut *#arg_ident }, &mut #arg_temp);
|
||||
}
|
||||
}
|
||||
_ => quote!(let #arg_ident = #arg_ident as _;),
|
||||
}
|
||||
}
|
||||
|
||||
fn map_arg_to_v8_fastcall_type(
|
||||
arg: &Arg,
|
||||
) -> Result<Option<V8FastCallType>, V8MappingError> {
|
||||
|
@ -247,6 +282,13 @@ fn map_arg_to_v8_fastcall_type(
|
|||
Arg::Numeric(NumericArg::i64) | Arg::Numeric(NumericArg::isize) => {
|
||||
V8FastCallType::I64
|
||||
}
|
||||
// Ref strings that are one byte internally may be passed as a SeqOneByteString,
|
||||
// which gives us a FastApiOneByteString.
|
||||
Arg::Special(Special::RefStr) => V8FastCallType::SeqOneByteString,
|
||||
// Owned strings can be fast, but we'll have to copy them.
|
||||
Arg::Special(Special::String) => V8FastCallType::SeqOneByteString,
|
||||
// Cow strings can be fast, but may require copying
|
||||
Arg::Special(Special::CowStr) => V8FastCallType::SeqOneByteString,
|
||||
_ => return Err(V8MappingError::NoMapping("a fast argument", arg.clone())),
|
||||
};
|
||||
Ok(Some(rv))
|
||||
|
@ -271,6 +313,8 @@ fn map_retval_to_v8_fastcall_type(
|
|||
Arg::Numeric(NumericArg::i64) | Arg::Numeric(NumericArg::isize) => {
|
||||
V8FastCallType::I64
|
||||
}
|
||||
// We don't return special return types
|
||||
Arg::Option(_) => return Ok(None),
|
||||
Arg::Special(_) => return Ok(None),
|
||||
_ => {
|
||||
return Err(V8MappingError::NoMapping(
|
||||
|
|
|
@ -8,6 +8,7 @@ use super::signature::Special;
|
|||
use super::MacroConfig;
|
||||
use super::V8MappingError;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::format_ident;
|
||||
use quote::quote;
|
||||
|
||||
pub(crate) fn generate_dispatch_slow(
|
||||
|
@ -151,10 +152,14 @@ pub fn from_arg(
|
|||
arg: &Arg,
|
||||
) -> Result<TokenStream, V8MappingError> {
|
||||
let GeneratorState {
|
||||
deno_core, args, ..
|
||||
deno_core,
|
||||
args,
|
||||
scope,
|
||||
needs_scope,
|
||||
..
|
||||
} = &mut generator_state;
|
||||
let arg_ident = args.get_mut(index).expect("Argument at index was missing");
|
||||
|
||||
let arg_temp = format_ident!("{}_temp", arg_ident);
|
||||
let res = match arg {
|
||||
Arg::Numeric(NumericArg::bool) => quote! {
|
||||
let #arg_ident = #arg_ident.is_true();
|
||||
|
@ -198,13 +203,31 @@ pub fn from_arg(
|
|||
}
|
||||
}
|
||||
Arg::Option(Special::String) => {
|
||||
*needs_scope = true;
|
||||
quote! {
|
||||
let #arg_ident = #arg_ident.to_rust_string_lossy();
|
||||
let #arg_ident = #arg_ident.to_rust_string_lossy(#scope);
|
||||
}
|
||||
}
|
||||
Arg::Special(Special::String) => {
|
||||
*needs_scope = true;
|
||||
quote! {
|
||||
let #arg_ident = #arg_ident.to_rust_string_lossy(#scope);
|
||||
}
|
||||
}
|
||||
Arg::Special(Special::RefStr) => {
|
||||
*needs_scope = true;
|
||||
quote! {
|
||||
let #arg_ident = #arg_ident.to_rust_string_lossy();
|
||||
// Trade 1024 bytes of stack space for potentially non-allocating strings
|
||||
let mut #arg_temp: [::std::mem::MaybeUninit<u8>; 1024] = [::std::mem::MaybeUninit::uninit(); 1024];
|
||||
let #arg_ident = &#deno_core::_ops::to_str(#scope, &#arg_ident, &mut #arg_temp);
|
||||
}
|
||||
}
|
||||
Arg::Special(Special::CowStr) => {
|
||||
*needs_scope = true;
|
||||
quote! {
|
||||
// Trade 1024 bytes of stack space for potentially non-allocating strings
|
||||
let mut #arg_temp: [::std::mem::MaybeUninit<u8>; 1024] = [::std::mem::MaybeUninit::uninit(); 1024];
|
||||
let #arg_ident = #deno_core::_ops::to_str(#scope, &#arg_ident, &mut #arg_temp);
|
||||
}
|
||||
}
|
||||
_ => return Err(V8MappingError::NoMapping("a slow argument", arg.clone())),
|
||||
|
@ -243,9 +266,12 @@ pub fn return_value_infallible(
|
|||
ret_type: &Arg,
|
||||
) -> Result<TokenStream, V8MappingError> {
|
||||
let GeneratorState {
|
||||
deno_core,
|
||||
scope,
|
||||
result,
|
||||
retval,
|
||||
needs_retval,
|
||||
needs_scope,
|
||||
..
|
||||
} = generator_state;
|
||||
|
||||
|
@ -265,6 +291,38 @@ pub fn return_value_infallible(
|
|||
*needs_retval = true;
|
||||
quote!(#retval.set_int32(#result as i32);)
|
||||
}
|
||||
Arg::Special(Special::String) => {
|
||||
*needs_retval = true;
|
||||
*needs_scope = true;
|
||||
quote! {
|
||||
if #result.is_empty() {
|
||||
#retval.set_empty_string();
|
||||
} else {
|
||||
// This should not fail in normal cases
|
||||
// TODO(mmastrac): This has extra allocations that we need to get rid of, especially if the string
|
||||
// is ASCII. We could make an "external Rust String" string in V8 from these and re-use the allocation.
|
||||
let temp = #deno_core::v8::String::new(#scope, &#result).unwrap();
|
||||
#retval.set(temp.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
Arg::Option(Special::String) => {
|
||||
*needs_retval = true;
|
||||
*needs_scope = true;
|
||||
// End the generator_state borrow
|
||||
let (result, retval) = (result.clone(), retval.clone());
|
||||
let some = return_value_infallible(
|
||||
generator_state,
|
||||
&Arg::Special(Special::String),
|
||||
)?;
|
||||
quote! {
|
||||
if let Some(#result) = #result {
|
||||
#some
|
||||
} else {
|
||||
#retval.set_null();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(V8MappingError::NoMapping(
|
||||
"a slow return value",
|
||||
|
|
|
@ -106,6 +106,7 @@ pub enum Special {
|
|||
HandleScope,
|
||||
OpState,
|
||||
String,
|
||||
CowStr,
|
||||
RefStr,
|
||||
FastApiCallbackOptions,
|
||||
}
|
||||
|
@ -431,6 +432,17 @@ fn parse_type_path(attrs: Attributes, tp: &TypePath) -> Result<Arg, ArgError> {
|
|||
Err(ArgError::MissingStringAttribute)
|
||||
}
|
||||
}
|
||||
( $( std :: str :: )? str ) => {
|
||||
// We should not hit this path with a #[string] argument
|
||||
Err(ArgError::MissingStringAttribute)
|
||||
}
|
||||
( $( std :: borrow :: )? Cow < str > ) => {
|
||||
if attrs.primary == Some(AttributeModifier::String) {
|
||||
Ok(Arg::Special(Special::CowStr))
|
||||
} else {
|
||||
Err(ArgError::MissingStringAttribute)
|
||||
}
|
||||
}
|
||||
( $( std :: ffi :: )? c_void ) => Ok(Arg::Numeric(NumericArg::__VOID__)),
|
||||
( OpState ) => Ok(Arg::Special(Special::OpState)),
|
||||
( v8 :: HandleScope ) => Ok(Arg::Special(Special::HandleScope)),
|
||||
|
|
|
@ -52,7 +52,9 @@ impl op_add {
|
|||
arg0: u32,
|
||||
arg1: u32,
|
||||
) -> u32 {
|
||||
let result = Self::call(arg0 as _, arg1 as _);
|
||||
let arg0 = arg0 as _;
|
||||
let arg1 = arg1 as _;
|
||||
let result = Self::call(arg0, arg1);
|
||||
result
|
||||
}
|
||||
extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) {
|
||||
|
|
|
@ -52,7 +52,9 @@ impl op_add {
|
|||
arg0: i32,
|
||||
arg1: u32,
|
||||
) -> u32 {
|
||||
let result = Self::call(arg0 as _, arg1 as _);
|
||||
let arg0 = arg0 as _;
|
||||
let arg1 = arg1 as _;
|
||||
let result = Self::call(arg0, arg1);
|
||||
result
|
||||
}
|
||||
extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) {
|
||||
|
|
75
ops/op2/test_cases/sync/string_cow.out
Normal file
75
ops/op2/test_cases/sync/string_cow.out
Normal file
|
@ -0,0 +1,75 @@
|
|||
#[allow(non_camel_case_types)]
|
||||
struct op_string_cow {
|
||||
_unconstructable: ::std::marker::PhantomData<()>,
|
||||
}
|
||||
impl deno_core::_ops::Op for op_string_cow {
|
||||
const NAME: &'static str = stringify!(op_string_cow);
|
||||
const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl {
|
||||
name: stringify!(op_string_cow),
|
||||
v8_fn_ptr: Self::v8_fn_ptr as _,
|
||||
enabled: true,
|
||||
fast_fn: Some({
|
||||
use deno_core::v8::fast_api::Type;
|
||||
use deno_core::v8::fast_api::CType;
|
||||
deno_core::v8::fast_api::FastFunction::new(
|
||||
&[Type::V8Value, Type::SeqOneByteString],
|
||||
CType::Uint32,
|
||||
Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
|
||||
)
|
||||
}),
|
||||
is_async: false,
|
||||
is_unstable: false,
|
||||
is_v8: false,
|
||||
arg_count: 1usize as u8,
|
||||
};
|
||||
}
|
||||
impl op_string_cow {
|
||||
pub const fn name() -> &'static str {
|
||||
stringify!(op_string_cow)
|
||||
}
|
||||
pub const fn decl() -> deno_core::_ops::OpDecl {
|
||||
deno_core::_ops::OpDecl {
|
||||
name: stringify!(op_string_cow),
|
||||
v8_fn_ptr: Self::v8_fn_ptr as _,
|
||||
enabled: true,
|
||||
fast_fn: Some({
|
||||
use deno_core::v8::fast_api::Type;
|
||||
use deno_core::v8::fast_api::CType;
|
||||
deno_core::v8::fast_api::FastFunction::new(
|
||||
&[Type::V8Value, Type::SeqOneByteString],
|
||||
CType::Uint32,
|
||||
Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
|
||||
)
|
||||
}),
|
||||
is_async: false,
|
||||
is_unstable: false,
|
||||
is_v8: false,
|
||||
arg_count: 1usize as u8,
|
||||
}
|
||||
}
|
||||
fn v8_fn_ptr_fast(
|
||||
_: deno_core::v8::Local<deno_core::v8::Object>,
|
||||
arg0: *mut deno_core::v8::fast_api::FastApiOneByteString,
|
||||
) -> u32 {
|
||||
let mut arg0_temp: [::std::mem::MaybeUninit<u8>; 1024] = [::std::mem::MaybeUninit::uninit(); 1024];
|
||||
let arg0 = deno_core::_ops::to_str_ptr(unsafe { &mut *arg0 }, &mut arg0_temp);
|
||||
let result = Self::call(arg0);
|
||||
result
|
||||
}
|
||||
extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) {
|
||||
let scope = &mut unsafe { deno_core::v8::CallbackScope::new(&*info) };
|
||||
let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
|
||||
&*info
|
||||
});
|
||||
let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
|
||||
&*info
|
||||
});
|
||||
let arg0 = args.get(0usize as i32);
|
||||
let mut arg0_temp: [::std::mem::MaybeUninit<u8>; 1024] = [::std::mem::MaybeUninit::uninit(); 1024];
|
||||
let arg0 = deno_core::_ops::to_str(scope, &arg0, &mut arg0_temp);
|
||||
let result = Self::call(arg0);
|
||||
rv.set_uint32(result as u32);
|
||||
}
|
||||
#[inline(always)]
|
||||
fn call(s: Cow<str>) -> u32 {}
|
||||
}
|
4
ops/op2/test_cases/sync/string_cow.rs
Normal file
4
ops/op2/test_cases/sync/string_cow.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
#[op2(fast)]
|
||||
fn op_string_cow(#[string] s: Cow<str>) -> u32 {}
|
53
ops/op2/test_cases/sync/string_option_return.out
Normal file
53
ops/op2/test_cases/sync/string_option_return.out
Normal file
|
@ -0,0 +1,53 @@
|
|||
#[allow(non_camel_case_types)]
|
||||
pub struct op_string_return {
|
||||
_unconstructable: ::std::marker::PhantomData<()>,
|
||||
}
|
||||
impl deno_core::_ops::Op for op_string_return {
|
||||
const NAME: &'static str = stringify!(op_string_return);
|
||||
const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl {
|
||||
name: stringify!(op_string_return),
|
||||
v8_fn_ptr: Self::v8_fn_ptr as _,
|
||||
enabled: true,
|
||||
fast_fn: None,
|
||||
is_async: false,
|
||||
is_unstable: false,
|
||||
is_v8: false,
|
||||
arg_count: 0usize as u8,
|
||||
};
|
||||
}
|
||||
impl op_string_return {
|
||||
pub const fn name() -> &'static str {
|
||||
stringify!(op_string_return)
|
||||
}
|
||||
pub const fn decl() -> deno_core::_ops::OpDecl {
|
||||
deno_core::_ops::OpDecl {
|
||||
name: stringify!(op_string_return),
|
||||
v8_fn_ptr: Self::v8_fn_ptr as _,
|
||||
enabled: true,
|
||||
fast_fn: None,
|
||||
is_async: false,
|
||||
is_unstable: false,
|
||||
is_v8: false,
|
||||
arg_count: 0usize as u8,
|
||||
}
|
||||
}
|
||||
extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) {
|
||||
let scope = &mut unsafe { deno_core::v8::CallbackScope::new(&*info) };
|
||||
let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
|
||||
&*info
|
||||
});
|
||||
let result = Self::call();
|
||||
if let Some(result) = result {
|
||||
if result.is_empty() {
|
||||
rv.set_empty_string();
|
||||
} else {
|
||||
let temp = deno_core::v8::String::new(scope, &result).unwrap();
|
||||
rv.set(temp.into());
|
||||
}
|
||||
} else {
|
||||
rv.set_null();
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn call() -> Option<String> {}
|
||||
}
|
5
ops/op2/test_cases/sync/string_option_return.rs
Normal file
5
ops/op2/test_cases/sync/string_option_return.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
#[op2]
|
||||
#[string]
|
||||
pub fn op_string_return() -> Option<String> {}
|
73
ops/op2/test_cases/sync/string_owned.out
Normal file
73
ops/op2/test_cases/sync/string_owned.out
Normal file
|
@ -0,0 +1,73 @@
|
|||
#[allow(non_camel_case_types)]
|
||||
struct op_string_owned {
|
||||
_unconstructable: ::std::marker::PhantomData<()>,
|
||||
}
|
||||
impl deno_core::_ops::Op for op_string_owned {
|
||||
const NAME: &'static str = stringify!(op_string_owned);
|
||||
const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl {
|
||||
name: stringify!(op_string_owned),
|
||||
v8_fn_ptr: Self::v8_fn_ptr as _,
|
||||
enabled: true,
|
||||
fast_fn: Some({
|
||||
use deno_core::v8::fast_api::Type;
|
||||
use deno_core::v8::fast_api::CType;
|
||||
deno_core::v8::fast_api::FastFunction::new(
|
||||
&[Type::V8Value, Type::SeqOneByteString],
|
||||
CType::Uint32,
|
||||
Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
|
||||
)
|
||||
}),
|
||||
is_async: false,
|
||||
is_unstable: false,
|
||||
is_v8: false,
|
||||
arg_count: 1usize as u8,
|
||||
};
|
||||
}
|
||||
impl op_string_owned {
|
||||
pub const fn name() -> &'static str {
|
||||
stringify!(op_string_owned)
|
||||
}
|
||||
pub const fn decl() -> deno_core::_ops::OpDecl {
|
||||
deno_core::_ops::OpDecl {
|
||||
name: stringify!(op_string_owned),
|
||||
v8_fn_ptr: Self::v8_fn_ptr as _,
|
||||
enabled: true,
|
||||
fast_fn: Some({
|
||||
use deno_core::v8::fast_api::Type;
|
||||
use deno_core::v8::fast_api::CType;
|
||||
deno_core::v8::fast_api::FastFunction::new(
|
||||
&[Type::V8Value, Type::SeqOneByteString],
|
||||
CType::Uint32,
|
||||
Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
|
||||
)
|
||||
}),
|
||||
is_async: false,
|
||||
is_unstable: false,
|
||||
is_v8: false,
|
||||
arg_count: 1usize as u8,
|
||||
}
|
||||
}
|
||||
fn v8_fn_ptr_fast(
|
||||
_: deno_core::v8::Local<deno_core::v8::Object>,
|
||||
arg0: *mut deno_core::v8::fast_api::FastApiOneByteString,
|
||||
) -> u32 {
|
||||
let arg0 = deno_core::_ops::to_string_ptr(unsafe { &mut *arg0 });
|
||||
let result = Self::call(arg0);
|
||||
result
|
||||
}
|
||||
extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) {
|
||||
let scope = &mut unsafe { deno_core::v8::CallbackScope::new(&*info) };
|
||||
let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
|
||||
&*info
|
||||
});
|
||||
let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
|
||||
&*info
|
||||
});
|
||||
let arg0 = args.get(0usize as i32);
|
||||
let arg0 = arg0.to_rust_string_lossy(scope);
|
||||
let result = Self::call(arg0);
|
||||
rv.set_uint32(result as u32);
|
||||
}
|
||||
#[inline(always)]
|
||||
fn call(s: String) -> u32 {}
|
||||
}
|
4
ops/op2/test_cases/sync/string_owned.rs
Normal file
4
ops/op2/test_cases/sync/string_owned.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
#[op2(fast)]
|
||||
fn op_string_owned(#[string] s: String) -> u32 {}
|
75
ops/op2/test_cases/sync/string_ref.out
Normal file
75
ops/op2/test_cases/sync/string_ref.out
Normal file
|
@ -0,0 +1,75 @@
|
|||
#[allow(non_camel_case_types)]
|
||||
struct op_string_owned {
|
||||
_unconstructable: ::std::marker::PhantomData<()>,
|
||||
}
|
||||
impl deno_core::_ops::Op for op_string_owned {
|
||||
const NAME: &'static str = stringify!(op_string_owned);
|
||||
const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl {
|
||||
name: stringify!(op_string_owned),
|
||||
v8_fn_ptr: Self::v8_fn_ptr as _,
|
||||
enabled: true,
|
||||
fast_fn: Some({
|
||||
use deno_core::v8::fast_api::Type;
|
||||
use deno_core::v8::fast_api::CType;
|
||||
deno_core::v8::fast_api::FastFunction::new(
|
||||
&[Type::V8Value, Type::SeqOneByteString],
|
||||
CType::Uint32,
|
||||
Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
|
||||
)
|
||||
}),
|
||||
is_async: false,
|
||||
is_unstable: false,
|
||||
is_v8: false,
|
||||
arg_count: 1usize as u8,
|
||||
};
|
||||
}
|
||||
impl op_string_owned {
|
||||
pub const fn name() -> &'static str {
|
||||
stringify!(op_string_owned)
|
||||
}
|
||||
pub const fn decl() -> deno_core::_ops::OpDecl {
|
||||
deno_core::_ops::OpDecl {
|
||||
name: stringify!(op_string_owned),
|
||||
v8_fn_ptr: Self::v8_fn_ptr as _,
|
||||
enabled: true,
|
||||
fast_fn: Some({
|
||||
use deno_core::v8::fast_api::Type;
|
||||
use deno_core::v8::fast_api::CType;
|
||||
deno_core::v8::fast_api::FastFunction::new(
|
||||
&[Type::V8Value, Type::SeqOneByteString],
|
||||
CType::Uint32,
|
||||
Self::v8_fn_ptr_fast as *const ::std::ffi::c_void,
|
||||
)
|
||||
}),
|
||||
is_async: false,
|
||||
is_unstable: false,
|
||||
is_v8: false,
|
||||
arg_count: 1usize as u8,
|
||||
}
|
||||
}
|
||||
fn v8_fn_ptr_fast(
|
||||
_: deno_core::v8::Local<deno_core::v8::Object>,
|
||||
arg0: *mut deno_core::v8::fast_api::FastApiOneByteString,
|
||||
) -> u32 {
|
||||
let mut arg0_temp: [::std::mem::MaybeUninit<u8>; 1024] = [::std::mem::MaybeUninit::uninit(); 1024];
|
||||
let arg0 = &deno_core::_ops::to_str_ptr(unsafe { &mut *arg0 }, &mut arg0_temp);
|
||||
let result = Self::call(arg0);
|
||||
result
|
||||
}
|
||||
extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) {
|
||||
let scope = &mut unsafe { deno_core::v8::CallbackScope::new(&*info) };
|
||||
let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
|
||||
&*info
|
||||
});
|
||||
let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
|
||||
&*info
|
||||
});
|
||||
let arg0 = args.get(0usize as i32);
|
||||
let mut arg0_temp: [::std::mem::MaybeUninit<u8>; 1024] = [::std::mem::MaybeUninit::uninit(); 1024];
|
||||
let arg0 = &deno_core::_ops::to_str(scope, &arg0, &mut arg0_temp);
|
||||
let result = Self::call(arg0);
|
||||
rv.set_uint32(result as u32);
|
||||
}
|
||||
#[inline(always)]
|
||||
fn call(s: &str) -> u32 {}
|
||||
}
|
4
ops/op2/test_cases/sync/string_ref.rs
Normal file
4
ops/op2/test_cases/sync/string_ref.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
#[op2(fast)]
|
||||
fn op_string_owned(#[string] s: &str) -> u32 {}
|
49
ops/op2/test_cases/sync/string_return.out
Normal file
49
ops/op2/test_cases/sync/string_return.out
Normal file
|
@ -0,0 +1,49 @@
|
|||
#[allow(non_camel_case_types)]
|
||||
pub struct op_string_return {
|
||||
_unconstructable: ::std::marker::PhantomData<()>,
|
||||
}
|
||||
impl deno_core::_ops::Op for op_string_return {
|
||||
const NAME: &'static str = stringify!(op_string_return);
|
||||
const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl {
|
||||
name: stringify!(op_string_return),
|
||||
v8_fn_ptr: Self::v8_fn_ptr as _,
|
||||
enabled: true,
|
||||
fast_fn: None,
|
||||
is_async: false,
|
||||
is_unstable: false,
|
||||
is_v8: false,
|
||||
arg_count: 0usize as u8,
|
||||
};
|
||||
}
|
||||
impl op_string_return {
|
||||
pub const fn name() -> &'static str {
|
||||
stringify!(op_string_return)
|
||||
}
|
||||
pub const fn decl() -> deno_core::_ops::OpDecl {
|
||||
deno_core::_ops::OpDecl {
|
||||
name: stringify!(op_string_return),
|
||||
v8_fn_ptr: Self::v8_fn_ptr as _,
|
||||
enabled: true,
|
||||
fast_fn: None,
|
||||
is_async: false,
|
||||
is_unstable: false,
|
||||
is_v8: false,
|
||||
arg_count: 0usize as u8,
|
||||
}
|
||||
}
|
||||
extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) {
|
||||
let scope = &mut unsafe { deno_core::v8::CallbackScope::new(&*info) };
|
||||
let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
|
||||
&*info
|
||||
});
|
||||
let result = Self::call();
|
||||
if result.is_empty() {
|
||||
rv.set_empty_string();
|
||||
} else {
|
||||
let temp = deno_core::v8::String::new(scope, &result).unwrap();
|
||||
rv.set(temp.into());
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn call() -> String {}
|
||||
}
|
5
ops/op2/test_cases/sync/string_return.rs
Normal file
5
ops/op2/test_cases/sync/string_return.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
#[op2]
|
||||
#[string]
|
||||
pub fn op_string_return() -> String {}
|
Loading…
Add table
Reference in a new issue