Skip to content

Commit 96c99ae

Browse files
committed
feat(host): add capability profiles and async host execution
1 parent 2c7dff7 commit 96c99ae

40 files changed

Lines changed: 7426 additions & 59 deletions

Cargo.lock

Lines changed: 58 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: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ members = [
55
"pd-vm-nostd",
66
"pd-vm-wasm",
77
"crates/rustscript",
8+
"crates/pd-host-schema",
89
]
910
resolver = "2"
1011

@@ -27,6 +28,7 @@ name = "vm"
2728
[features]
2829
default = ["runtime", "cli", "cranelift-jit"]
2930
runtime = []
31+
async = ["runtime", "dep:tokio"]
3032
sqlite = ["runtime", "dep:rusqlite"]
3133
edge-abi = [
3234
"dep:edge_abi",
@@ -62,11 +64,12 @@ cranelift-module = { version = "0.129.1", optional = true }
6264
cranelift-native = { version = "0.129.1", optional = true }
6365
pd-host-function = { path = "./pd-host-function", version = "0.1.0" }
6466
rusqlite = { version = "0.32", default-features = false, features = ["bundled", "hooks", "limits"], optional = true }
67+
tokio = { version = "1", features = ["rt-multi-thread", "net", "time", "sync", "fs", "io-util", "process"], optional = true }
6568
edge_abi = { package = "pd-edge-abi", version = "0.1.1", default-features = false, optional = true }
6669
futures-channel = "0.3"
6770
paste = "1"
6871
regex = "1"
69-
serde = "1"
72+
serde = { version = "1", features = ["derive"] }
7073
serde_json = "1"
7174
rt-format = "0.3.1"
7275
self_cell = "1"
@@ -87,5 +90,15 @@ name = "host_binding_generation_tests"
8790
path = "tests/host_binding_generation_tests.rs"
8891
required-features = ["cranelift-jit"]
8992

93+
[[test]]
94+
name = "host_sdk_tests"
95+
path = "tests/host_sdk_tests.rs"
96+
required-features = ["runtime"]
97+
98+
[[test]]
99+
name = "host_context_arch_tests"
100+
path = "tests/host_context_arch_tests.rs"
101+
required-features = ["runtime"]
102+
90103
[build-dependencies]
91104
syn = { version = "2", features = ["full"] }

build.rs

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -246,10 +246,21 @@ fn write_generated_file(path: &Path, contents: &str) {
246246
fn builtin_source_specs(namespaces: &[NamespaceDecl]) -> Vec<SourceSpec> {
247247
namespaces
248248
.iter()
249-
.map(|namespace| SourceSpec {
250-
path: format!("src/builtins/runtime/{}.rs", namespace.module),
251-
module: namespace.module.clone(),
252-
category: SourceCategory::NamespacedBuiltin,
249+
.map(|namespace| {
250+
let path = if namespace.module == "io" {
251+
if cfg!(feature = "async") {
252+
"src/builtins/runtime/io/async_io.rs".to_string()
253+
} else {
254+
"src/builtins/runtime/io/blocking.rs".to_string()
255+
}
256+
} else {
257+
format!("src/builtins/runtime/{}.rs", namespace.module)
258+
};
259+
SourceSpec {
260+
path,
261+
module: namespace.module.clone(),
262+
category: SourceCategory::NamespacedBuiltin,
263+
}
253264
})
254265
.collect()
255266
}
@@ -271,6 +282,9 @@ fn parse_sources(
271282
}
272283

273284
pub(crate) fn classify_host_binding(function: &ItemFn) -> HostBindingKind {
285+
if function.sig.asyncness.is_some() {
286+
return HostBindingKind::StaticStack;
287+
}
274288
if function.sig.inputs.iter().any(|input| match input {
275289
FnArg::Typed(pat_type) => is_vm_context_type(&pat_type.ty),
276290
_ => false,
@@ -296,6 +310,9 @@ pub(crate) fn classify_host_binding(function: &ItemFn) -> HostBindingKind {
296310
}
297311

298312
pub(crate) fn infer_host_execution(function: &ItemFn) -> HostExecutionKind {
313+
if function.sig.asyncness.is_some() {
314+
return HostExecutionKind::MaySuspend;
315+
}
299316
let return_type = normalized_return_type(&function.sig.output);
300317
if contains_host_call_result(&return_type) {
301318
HostExecutionKind::MaySuspend
@@ -979,6 +996,7 @@ fn render_builtin_catalog(
979996
&actual_builtin_by_variant,
980997
);
981998
render_builtin_signature_method(&mut out, &builtin_variant_order);
999+
render_builtin_capability_method(&mut out, builtin_callables);
9821000
writeln!(
9831001
&mut out,
9841002
" pub fn from_namespaced_name(name: &str) -> Option<Self> {{"
@@ -1553,6 +1571,35 @@ fn required_param_count(params: &[CallableParamDecl]) -> usize {
15531571
params.iter().take_while(|param| !param.optional).count()
15541572
}
15551573

1574+
fn render_builtin_capability_method(out: &mut String, builtin_callables: &[CallableDecl]) {
1575+
let mut capability_variants = Vec::new();
1576+
for callable in builtin_callables {
1577+
let variant = builtin_variant_name(&callable.name);
1578+
if !capability_variants.contains(&variant) {
1579+
capability_variants.push(variant);
1580+
}
1581+
}
1582+
capability_variants.sort();
1583+
writeln!(out, " #[cfg(feature = \"runtime\")]").unwrap();
1584+
writeln!(
1585+
out,
1586+
" pub(crate) const fn requires_explicit_host_capability(self) -> bool {{"
1587+
)
1588+
.unwrap();
1589+
if capability_variants.is_empty() {
1590+
writeln!(out, " false").unwrap();
1591+
} else {
1592+
let patterns = capability_variants
1593+
.iter()
1594+
.map(|variant| format!("BuiltinFunction::{variant}"))
1595+
.collect::<Vec<_>>()
1596+
.join(" | ");
1597+
writeln!(out, " matches!(self, {patterns})").unwrap();
1598+
}
1599+
writeln!(out, " }}").unwrap();
1600+
writeln!(out).unwrap();
1601+
}
1602+
15561603
fn stable_groups<F>(callables: &[CallableDecl], mut key_fn: F) -> Vec<Group<'_>>
15571604
where
15581605
F: FnMut(&CallableDecl) -> String,
@@ -1863,6 +1910,9 @@ fn host_wrapper_adapter_name(callable: &CallableDecl) -> String {
18631910

18641911
fn generated_wrapper_decl(function: &ItemFn) -> WrapperDecl {
18651912
let mut params = Vec::new();
1913+
if function.sig.asyncness.is_some() {
1914+
params.push(WrapperParamKind::Vm);
1915+
}
18661916
for input in &function.sig.inputs {
18671917
let FnArg::Typed(pat_type) = input else {
18681918
panic!("methods are not supported in #[pd_host_function] declarations");
@@ -1889,6 +1939,13 @@ fn parse_callable_params(function: &ItemFn) -> Vec<CallableParamDecl> {
18891939
let FnArg::Typed(pat_type) = input else {
18901940
panic!("methods are not supported in #[pd_host_function] declarations");
18911941
};
1942+
if pat_type
1943+
.attrs
1944+
.iter()
1945+
.any(|attr| attr.path().is_ident("pd_host_context"))
1946+
{
1947+
return None;
1948+
}
18921949
if is_vm_context_type(&pat_type.ty) {
18931950
return None;
18941951
}
@@ -2055,7 +2112,7 @@ fn type_label(ty: &Type) -> String {
20552112
};
20562113
format!("{} | null", type_label(inner))
20572114
}
2058-
"VmResult" | "HostCallResult" => {
2115+
"VmResult" | "HostCallResult" | "HostFutureOutput" => {
20592116
let syn::PathArguments::AngleBracketed(args) = &segment.arguments else {
20602117
panic!("{ident}<T> requires one generic argument");
20612118
};

crates/pd-host-schema/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "pd-host-schema"
3+
version.workspace = true
4+
edition.workspace = true
5+
description = "Shared host-schema parsing for the pd-host-function proc macro and the pd-vm build script"
6+
license = "MIT"
7+
homepage = "https://rustscript.org/"
8+
repository = "https://github.com/rustscript-lang/rustscript"
9+
10+
[dependencies]
11+
proc-macro2 = "1"
12+
syn = { version = "2", features = ["full", "extra-traits"] }

0 commit comments

Comments
 (0)