Skip to content

Commit 49745e3

Browse files
committed
more fast strings
1 parent 172f597 commit 49745e3

File tree

5 files changed

+123
-63
lines changed

5 files changed

+123
-63
lines changed

Cargo.lock

Lines changed: 15 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ deno_ops = { version = "0.192.0", path = "./ops" }
2525
serde_v8 = { version = "0.225.0", path = "./serde_v8" }
2626
deno_core_testing = { path = "./testing" }
2727

28-
v8 = { version = "0.106.0", default-features = false }
28+
v8 = { version = "129.0.0", default-features = false }
2929
deno_ast = { version = "=0.40.0", features = ["transpiling"] }
3030
deno_unsync = "0.4.0"
3131
deno_core_icudata = "0.0.73"
@@ -92,3 +92,6 @@ codegen-units = 1
9292
incremental = true
9393
lto = true
9494
opt-level = 'z' # Optimize for size
95+
96+
[patch.crates-io]
97+
v8 = { path = "../rusty_v8" }

core/ops_builtin_v8.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::OpState;
1818
use anyhow::Error;
1919
use serde::Deserialize;
2020
use serde::Serialize;
21+
use std::borrow::Cow;
2122
use std::cell::RefCell;
2223
use std::rc::Rc;
2324
use v8::ValueDeserializerHelper;
@@ -67,13 +68,13 @@ pub fn op_leak_tracing_submit(
6768
scope: &mut v8::HandleScope,
6869
#[smi] kind: u8,
6970
#[smi] id: i32,
70-
#[string] trace: &str,
71+
#[string] trace: Cow<'_, str>,
7172
) {
7273
let context_state = JsRealm::state_from_scope(scope);
7374
context_state.activity_traces.submit(
7475
RuntimeActivityType::from_u8(kind),
7576
id as _,
76-
trace,
77+
&trace,
7778
);
7879
}
7980

core/runtime/ops.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ pub fn to_external_option(external: &v8::Value) -> Option<*mut c_void> {
150150
}
151151
}
152152

153+
153154
/// Expands `inbuf` to `outbuf`, assuming that `outbuf` has at least 2x `input_length`.
154155
#[inline(always)]
155156
unsafe fn latin1_to_utf8(
@@ -235,10 +236,30 @@ pub fn to_string_ptr(string: &v8::fast_api::FastApiOneByteString) -> String {
235236
}
236237
}
237238

238-
pub fn to_cow_byte_ptr(
239-
string: &v8::fast_api::FastApiOneByteString,
240-
) -> Cow<[u8]> {
241-
string.as_bytes().into()
239+
240+
#[inline(always)]
241+
pub fn to_string_view<'a>(
242+
scope: &'a mut v8::Isolate,
243+
value: v8::Local<'a, v8::Value>,
244+
) -> Option<v8::ValueView<'a>> {
245+
if !value.is_string() {
246+
return None;
247+
}
248+
249+
// SAFETY: We checked is_string above.
250+
let string: v8::Local<'a, v8::String> = unsafe { std::mem::transmute(value) };
251+
252+
let string_view = v8::ValueView::new(scope, string);
253+
254+
Some(string_view)
255+
}
256+
257+
#[inline(always)]
258+
pub fn to_str_from_view<'a, const N: usize>(
259+
string: &'a v8::ValueView<'_>,
260+
buffer: &'a mut [MaybeUninit<u8>; N],
261+
) -> Cow<'a, str> {
262+
string.to_rust_cow_lossy(buffer)
242263
}
243264

244265
/// Converts a [`v8::Value`] to an owned string.

ops/op2/dispatch_fast.rs

Lines changed: 76 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -97,18 +97,29 @@ impl FastSignature {
9797
&self,
9898
generator_state: &mut GeneratorState,
9999
) -> Result<Vec<TokenStream>, V8SignatureMappingError> {
100+
// Collect virtual arguments in a deferred list that we compute at the very end. This allows us to borrow
101+
// the scope/opstate in the intermediate stages.
100102
let mut call_args = vec![];
103+
let mut deferred = vec![];
104+
101105
for arg in &self.args {
102106
match arg {
103-
FastArg::Actual { arg, name_out, .. }
104-
| FastArg::Virtual { name_out, arg } => call_args.push(
107+
FastArg::Actual { arg, name_out, .. } => call_args.push(
108+
map_v8_fastcall_arg_to_arg(generator_state, name_out, arg).map_err(
109+
|s| V8SignatureMappingError::NoArgMapping(s, arg.clone()),
110+
)?,
111+
),
112+
FastArg::Virtual { name_out, arg } => deferred.push(
105113
map_v8_fastcall_arg_to_arg(generator_state, name_out, arg).map_err(
106114
|s| V8SignatureMappingError::NoArgMapping(s, arg.clone()),
107115
)?,
108116
),
109117
FastArg::CallbackOptions | FastArg::PromiseId => {}
110118
}
111119
}
120+
121+
call_args.extend(deferred);
122+
112123
Ok(call_args)
113124
}
114125

@@ -313,6 +324,14 @@ pub(crate) fn get_fast_signature(
313324
}))
314325
}
315326

327+
fn create_isolate(generator_state: &mut GeneratorState) -> TokenStream {
328+
generator_state.needs_fast_api_callback_options = true;
329+
gs_quote!(generator_state(fast_api_callback_options) => {
330+
// SAFETY: This is using an &FastApiCallbackOptions inside a fast call.
331+
unsafe { &mut *#fast_api_callback_options.isolate };
332+
})
333+
}
334+
316335
fn create_scope(generator_state: &mut GeneratorState) -> TokenStream {
317336
generator_state.needs_fast_api_callback_options = true;
318337
gs_quote!(generator_state(fast_api_callback_options) => {
@@ -478,6 +497,11 @@ pub(crate) fn generate_dispatch_fast(
478497
gs_quote!(generator_state(scope) => {
479498
let mut #scope = #create_scope;
480499
})
500+
} else if generator_state.needs_isolate {
501+
let create_isolate = create_isolate(generator_state);
502+
gs_quote!(generator_state(scope) => {
503+
let mut #scope = #create_isolate;
504+
})
481505
} else {
482506
quote!()
483507
};
@@ -590,6 +614,7 @@ fn map_v8_fastcall_arg_to_arg(
590614
opctx,
591615
js_runtime_state,
592616
scope,
617+
needs_isolate,
593618
needs_scope,
594619
needs_opctx,
595620
needs_fast_api_callback_options,
@@ -658,9 +683,9 @@ fn map_v8_fastcall_arg_to_arg(
658683
fast_api_typed_array_to_buffer(arg_ident, arg_ident, *buffer)?
659684
}
660685
Arg::Special(Special::Isolate) => {
661-
*needs_fast_api_callback_options = true;
662-
gs_quote!(generator_state(fast_api_callback_options) => {
663-
let #arg_ident = #fast_api_callback_options.isolate;
686+
*needs_isolate = true;
687+
gs_quote!(generator_state(scope) => {
688+
let #arg_ident = &mut *#scope;
664689
})
665690
}
666691
Arg::Ref(RefType::Ref, Special::OpState) => {
@@ -720,22 +745,46 @@ fn map_v8_fastcall_arg_to_arg(
720745
}
721746
}
722747
Arg::String(Strings::RefStr) => {
723-
quote! {
724-
let mut #arg_temp: [::std::mem::MaybeUninit<u8>; deno_core::_ops::STRING_STACK_BUFFER_SIZE] = [::std::mem::MaybeUninit::uninit(); deno_core::_ops::STRING_STACK_BUFFER_SIZE];
725-
let #arg_ident = &deno_core::_ops::to_str_ptr(unsafe { &mut *#arg_ident }, &mut #arg_temp);
726-
}
748+
// quote! {
749+
// let mut #arg_temp: [::std::mem::MaybeUninit<u8>; deno_core::_ops::STRING_STACK_BUFFER_SIZE] = [::std::mem::MaybeUninit::uninit(); deno_core::_ops::STRING_STACK_BUFFER_SIZE];
750+
// let #arg_ident = &deno_core::_ops::to_str_ptr(unsafe { &mut *#arg_ident }, &mut #arg_temp);
751+
// }
752+
*needs_isolate = true;
753+
gs_quote!(generator_state(scope) => {
754+
let mut #arg_temp: ([::std::mem::MaybeUninit<u8>; deno_core::_ops::STRING_STACK_BUFFER_SIZE], Option<v8::ValueView>) = {
755+
let buf = [::std::mem::MaybeUninit::uninit(); deno_core::_ops::STRING_STACK_BUFFER_SIZE];
756+
let value_view = deno_core::_ops::to_string_view(&mut *#scope, #arg_ident);
757+
(buf, value_view)
758+
};
759+
let #arg_ident = if let Some(value_view) = &#arg_temp.1 {
760+
&deno_core::_ops::to_str_from_view(value_view, &mut #arg_temp.0)
761+
} else {
762+
""
763+
};
764+
// let value_view = deno_core::_ops::to_string_view(&mut *#scope, #arg_ident);
765+
// let #arg_ident = "";
766+
})
727767
}
728768
Arg::String(Strings::String) => {
729-
quote!(let #arg_ident = deno_core::_ops::to_string_ptr(unsafe { &mut *#arg_ident });)
769+
*needs_isolate = true;
770+
quote!(let #arg_ident = deno_core::_ops::to_string(&mut *#scope, &*#arg_ident);)
730771
}
731772
Arg::String(Strings::CowStr) => {
732-
quote! {
773+
*needs_isolate = true;
774+
gs_quote!(generator_state(scope) => {
733775
let mut #arg_temp: [::std::mem::MaybeUninit<u8>; deno_core::_ops::STRING_STACK_BUFFER_SIZE] = [::std::mem::MaybeUninit::uninit(); deno_core::_ops::STRING_STACK_BUFFER_SIZE];
734-
let #arg_ident = deno_core::_ops::to_str_ptr(unsafe { &mut *#arg_ident }, &mut #arg_temp);
735-
}
776+
let #arg_ident = deno_core::_ops::to_str(&mut *#scope, &*#arg_ident, &mut #arg_temp);
777+
})
736778
}
737779
Arg::String(Strings::CowByte) => {
738-
quote!(let #arg_ident = deno_core::_ops::to_cow_byte_ptr(unsafe { &mut *#arg_ident });)
780+
*needs_isolate = true;
781+
let throw_exception =
782+
throw_type_error(generator_state, "expected one byte string");
783+
gs_quote!(generator_state(scope) => {
784+
let Ok(#arg_ident) = deno_core::_ops::to_cow_one_byte(&mut *#scope, &*#arg_ident) else {
785+
#throw_exception
786+
};
787+
})
739788
}
740789
Arg::V8Local(v8)
741790
| Arg::OptionV8Local(v8)
@@ -755,13 +804,11 @@ fn map_v8_fastcall_arg_to_arg(
755804
let ty =
756805
syn::parse_str::<syn::Path>(ty).expect("Failed to reparse state type");
757806

758-
*needs_fast_api_callback_options = true;
807+
*needs_isolate = true;
759808
let throw_exception =
760809
throw_type_error(generator_state, format!("expected {ty:?}"));
761-
gs_quote!(generator_state(fast_api_callback_options) => {
762-
// SAFETY: Isolate is valid if this function is being called.
763-
let isolate = unsafe { &mut *#fast_api_callback_options.isolate };
764-
let Some(#arg_ident) = deno_core::_ops::try_unwrap_cppgc_object::<#ty>(isolate, #arg_ident) else {
810+
gs_quote!(generator_state(scope) => {
811+
let Some(#arg_ident) = deno_core::_ops::try_unwrap_cppgc_object::<#ty>(&mut *#scope, #arg_ident) else {
765812
#throw_exception
766813
};
767814
let #arg_ident = &*#arg_ident;
@@ -851,7 +898,6 @@ fn map_arg_to_v8_fastcall_type(
851898
// Other types + ref types are not handled
852899
Arg::OptionNumeric(..)
853900
| Arg::Option(_)
854-
| Arg::OptionString(_)
855901
| Arg::OptionBuffer(..)
856902
| Arg::SerdeV8(_)
857903
| Arg::FromV8(_)
@@ -885,15 +931,16 @@ fn map_arg_to_v8_fastcall_type(
885931
) => V8FastCallType::F64,
886932
Arg::Numeric(NumericArg::f32, _) => V8FastCallType::F32,
887933
Arg::Numeric(NumericArg::f64, _) => V8FastCallType::F64,
888-
// Ref strings that are one byte internally may be passed as a SeqOneByteString,
889-
// which gives us a FastApiOneByteString.
890-
Arg::String(Strings::RefStr) => V8FastCallType::SeqOneByteString,
891-
// Owned strings can be fast, but we'll have to copy them.
892-
Arg::String(Strings::String) => V8FastCallType::SeqOneByteString,
893-
// Cow strings can be fast, but may require copying
894-
Arg::String(Strings::CowStr) => V8FastCallType::SeqOneByteString,
895-
// Cow byte strings can be fast and don't require copying
896-
Arg::String(Strings::CowByte) => V8FastCallType::SeqOneByteString,
934+
// Strings are passed as v8::Value, because SeqOneByteString is too
935+
// restrictive in what values are eligible for fastcalls.
936+
Arg::OptionString(Strings::RefStr) => return Ok(None),
937+
Arg::OptionString(Strings::String) => return Ok(None),
938+
Arg::OptionString(Strings::CowStr) => return Ok(None),
939+
Arg::OptionString(Strings::CowByte) => return Ok(None),
940+
Arg::String(Strings::RefStr) => V8FastCallType::V8Value,
941+
Arg::String(Strings::String) => V8FastCallType::V8Value,
942+
Arg::String(Strings::CowStr) => V8FastCallType::V8Value,
943+
Arg::String(Strings::CowByte) => V8FastCallType::V8Value,
897944
Arg::External(..) => V8FastCallType::Pointer,
898945
Arg::CppGcResource(..) => V8FastCallType::V8Value,
899946
Arg::OptionCppGcResource(..) => V8FastCallType::V8Value,

0 commit comments

Comments
 (0)