0
0
Fork 0
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:
Matt Mastracci 2023-07-01 16:07:05 -06:00 committed by GitHub
parent 0f719aa79c
commit 6afdcf59b8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 710 additions and 20 deletions

View file

@ -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;

View file

@ -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>() {}

View file

@ -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(

View file

@ -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",

View file

@ -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)),

View file

@ -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) {

View file

@ -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) {

View 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 {}
}

View 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 {}

View 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> {}
}

View 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> {}

View 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 {}
}

View 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 {}

View 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 {}
}

View 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 {}

View 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 {}
}

View file

@ -0,0 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
#[op2]
#[string]
pub fn op_string_return() -> String {}