Skip to content

Commit 45fe584

Browse files
committed
refactor(host): isolate host-owned policies and async IO
1 parent 76f0b8d commit 45fe584

32 files changed

Lines changed: 1666 additions & 666 deletions

‎Cargo.lock‎

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

‎Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"
6767
rusqlite = { version = "0.32", default-features = false, features = ["bundled", "hooks", "limits"], optional = true }
6868
url = { version = "2", optional = true }
6969
futures-util = { version = "0.3", optional = true }
70-
tokio = { version = "1", features = ["rt-multi-thread", "net", "time", "sync"], optional = true }
70+
tokio = { version = "1", features = ["rt-multi-thread", "net", "time", "sync", "fs", "io-util", "process"], optional = true }
7171
edge_abi = { package = "pd-edge-abi", version = "0.1.1", default-features = false, optional = true }
7272
futures-channel = "0.3"
7373
paste = "1"

‎build.rs‎

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -251,10 +251,21 @@ fn write_generated_file(path: &Path, contents: &str) {
251251
fn builtin_source_specs(namespaces: &[NamespaceDecl]) -> Vec<SourceSpec> {
252252
namespaces
253253
.iter()
254-
.map(|namespace| SourceSpec {
255-
path: format!("src/builtins/runtime/{}.rs", namespace.module),
256-
module: namespace.module.clone(),
257-
category: SourceCategory::NamespacedBuiltin,
254+
.map(|namespace| {
255+
let path = if namespace.module == "io" {
256+
if cfg!(feature = "async") {
257+
"src/builtins/runtime/io/async_io.rs".to_string()
258+
} else {
259+
"src/builtins/runtime/io/blocking.rs".to_string()
260+
}
261+
} else {
262+
format!("src/builtins/runtime/{}.rs", namespace.module)
263+
};
264+
SourceSpec {
265+
path,
266+
module: namespace.module.clone(),
267+
category: SourceCategory::NamespacedBuiltin,
268+
}
258269
})
259270
.collect()
260271
}
@@ -2108,7 +2119,7 @@ fn type_label(ty: &Type) -> String {
21082119
};
21092120
format!("{} | null", type_label(inner))
21102121
}
2111-
"VmResult" | "HostCallResult" => {
2122+
"VmResult" | "HostCallResult" | "HostFutureOutput" => {
21122123
let syn::PathArguments::AngleBracketed(args) = &segment.arguments else {
21132124
panic!("{ident}<T> requires one generic argument");
21142125
};

‎crates/rustscript/tests/alias_smoke.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#[cfg(feature = "sqlite")]
2+
use rustscript::SqliteHostExt;
3+
14
/// Verify that the `rustscript` alias crate re-exports the same API as `pd-vm`.
25
#[test]
36
fn alias_exports_compile_source() {

‎pd-host-function/src/lib.rs‎

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -308,13 +308,13 @@ fn generate_vm_wrapper(
308308

309309
Ok(quote! {
310310
#[allow(dead_code)]
311-
pub(super) fn #wrapper_name(#(#imm_wrapper_params),*) -> #wrapper_output {
311+
pub(crate) fn #wrapper_name(#(#imm_wrapper_params),*) -> #wrapper_output {
312312
#(#imm_extract_stmts)*
313313
#call_expr
314314
}
315315

316316
#[allow(dead_code)]
317-
pub(super) fn #mutable_wrapper_name(#(#mut_wrapper_params),*) -> #wrapper_output {
317+
pub(crate) fn #mutable_wrapper_name(#(#mut_wrapper_params),*) -> #wrapper_output {
318318
#(#mut_extract_stmts)*
319319
#call_expr
320320
}
@@ -344,7 +344,7 @@ fn generate_async_vm_wrapper(
344344
let ty = &pat_type.ty;
345345
if is_host_context_param(input) {
346346
extract_stmts.push(quote! {
347-
let #ident = <#ty as super::CaptureAsyncHostContext>::capture(vm)?;
347+
let #ident = <#ty as super::CaptureAsyncHostContext>::capture_with_args(vm, args)?;
348348
});
349349
call_args.push(quote!(#ident));
350350
continue;
@@ -366,12 +366,14 @@ fn generate_async_vm_wrapper(
366366
} else {
367367
quote!(#impl_name(#(#call_args),*).await)
368368
};
369-
let body = quote! {
370-
#(#extract_stmts)*
371-
vm.submit_host_future(Box::pin(async move {
372-
let value = #await_value;
369+
let future_result = if return_is_host_future_output(&item.sig.output) {
370+
quote!(Ok(value.map(super::return_one)))
371+
} else {
372+
quote! {
373373
match super::IntoHostCallOutcome::into_host_call_outcome(value) {
374-
super::CallOutcome::Return(values) => Ok(values),
374+
super::CallOutcome::Return(values) => {
375+
Ok(super::HostFutureOutput::returning(values))
376+
}
375377
super::CallOutcome::Pending(op_id) => Err(super::VmError::HostError(
376378
format!("async host function returned nested pending operation {op_id}"),
377379
)),
@@ -381,20 +383,27 @@ fn generate_async_vm_wrapper(
381383
),
382384
),
383385
}
386+
}
387+
};
388+
let body = quote! {
389+
#(#extract_stmts)*
390+
vm.submit_host_future(Box::pin(async move {
391+
let value = #await_value;
392+
#future_result
384393
}))
385394
};
386395

387396
Ok(quote! {
388397
#[allow(dead_code)]
389-
pub(super) fn #wrapper_name(
398+
pub(crate) fn #wrapper_name(
390399
vm: &mut super::super::Vm,
391400
args: &[super::super::Value],
392401
) -> super::super::VmResult<super::CallOutcome> {
393402
#body
394403
}
395404

396405
#[allow(dead_code)]
397-
pub(super) fn #mutable_wrapper_name(
406+
pub(crate) fn #mutable_wrapper_name(
398407
vm: &mut super::super::Vm,
399408
args: &mut [super::super::Value],
400409
) -> super::super::VmResult<super::CallOutcome> {
@@ -465,6 +474,20 @@ fn unwrap_vm_result_type(ty: &Type) -> Result<Option<Type>, Error> {
465474
}
466475
}
467476

477+
fn return_is_host_future_output(output: &ReturnType) -> bool {
478+
vm_result_inner_type(output)
479+
.expect("pd_host_function return type should already be validated")
480+
.and_then(|ty| match ty {
481+
Type::Path(path) => path
482+
.path
483+
.segments
484+
.last()
485+
.map(|segment| segment.ident.clone()),
486+
_ => None,
487+
})
488+
.is_some_and(|ident| ident == "HostFutureOutput")
489+
}
490+
468491
fn return_is_vm_result(output: &ReturnType) -> bool {
469492
vm_result_inner_type(output)
470493
.expect("pd_host_function return type should already be validated")
@@ -526,7 +549,7 @@ fn type_label(ty: &Type) -> Result<String, Error> {
526549
let inner_label = type_label(inner)?;
527550
Ok(format!("{inner_label} | null"))
528551
}
529-
"VmResult" | "HostCallResult" => {
552+
"VmResult" | "HostCallResult" | "HostFutureOutput" => {
530553
let syn::PathArguments::AngleBracketed(args) = &segment.arguments else {
531554
return Err(Error::new_spanned(
532555
&segment.arguments,
@@ -720,9 +743,26 @@ mod tests {
720743
assert!(expanded.contains("async move"));
721744
assert!(expanded.contains("borrow_arg"));
722745
assert!(expanded.contains("CaptureAsyncHostContext"));
746+
assert!(expanded.contains("capture_with_args"));
723747
assert!(!expanded.contains("pd_host_context"));
724748
}
725749

750+
#[test]
751+
fn async_host_future_output_maps_its_inner_value_to_call_return() {
752+
let attr: Punctuated<Meta, Token![,]> = parse_quote!(name = "test::completion");
753+
let item: ItemFn = parse_quote! {
754+
/// Completes after mutating VM-owned state.
755+
async fn completion() -> VmResult<HostFutureOutput<i64>> {
756+
todo!()
757+
}
758+
};
759+
760+
let expanded = expand_pd_host_function(attr, item)
761+
.expect("host future output should be accepted")
762+
.to_string();
763+
assert!(expanded.contains("value . map (super :: return_one)"));
764+
}
765+
726766
#[test]
727767
fn async_signature_rejects_borrowed_parameters() {
728768
let attr: Punctuated<Meta, Token![,]> = parse_quote!(name = "test::borrowed");

‎src/builtins/runtime/cancellation.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ impl OperationState {
410410
self.core.status()
411411
}
412412

413+
#[cfg_attr(feature = "async", allow(dead_code))]
413414
pub fn set_payload(&self, payload: ResourceHandle) {
414415
self.core
415416
.inner
@@ -453,6 +454,7 @@ impl OperationState {
453454
.payload
454455
}
455456

457+
#[cfg_attr(feature = "async", allow(dead_code))]
456458
pub fn set_resource(&self, resource: ResourceHandle) {
457459
self.core
458460
.inner
@@ -560,6 +562,7 @@ impl OperationRegistry {
560562
Ok(id)
561563
}
562564

565+
#[cfg_attr(feature = "async", allow(dead_code))]
563566
pub fn start_owned(
564567
&mut self,
565568
owner: OperationOwner,

0 commit comments

Comments
 (0)