Skip to content

Commit d0bfbd7

Browse files
committed
refactor(vm): unify host runtime lifecycle
1 parent 7f536ed commit d0bfbd7

50 files changed

Lines changed: 10598 additions & 1811 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 106 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: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ name = "vm"
2727
[features]
2828
default = ["runtime", "cli", "cranelift-jit"]
2929
runtime = []
30-
http-client = ["dep:reqwest", "dep:url", "dep:tokio", "dep:futures-util"]
30+
http-client = ["runtime", "dep:reqwest", "dep:url", "dep:tokio", "dep:futures-util"]
31+
sqlite = ["runtime", "dep:rusqlite"]
3132
edge-abi = [
3233
"dep:edge_abi",
3334
"edge_abi/console",
@@ -62,9 +63,10 @@ cranelift-module = { version = "0.129.1", optional = true }
6263
cranelift-native = { version = "0.129.1", optional = true }
6364
pd-host-function = { path = "./pd-host-function", version = "0.1.0" }
6465
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "stream"], optional = true }
66+
rusqlite = { version = "0.32", default-features = false, features = ["bundled", "hooks", "limits"], optional = true }
6567
url = { version = "2", optional = true }
6668
futures-util = { version = "0.3", optional = true }
67-
tokio = { version = "1", features = ["rt", "net", "time"], optional = true }
69+
tokio = { version = "1", features = ["rt-multi-thread", "net", "time", "sync"], optional = true }
6870
edge_abi = { package = "pd-edge-abi", version = "0.1.1", default-features = false, optional = true }
6971
futures-channel = "0.3"
7072
paste = "1"
@@ -82,6 +84,7 @@ windows-sys = { version = "0.59", features = ["Win32_System_Diagnostics_Debug",
8284
libc = "0.2"
8385

8486
[dev-dependencies]
87+
futures-util = "0.3"
8588
syn = { version = "2", features = ["full"] }
8689
tokio = { version = "1", features = ["macros", "rt", "time", "sync"] }
8790

@@ -93,7 +96,12 @@ required-features = ["cranelift-jit"]
9396
[[test]]
9497
name = "http_host_tests"
9598
path = "tests/vm/http_host_tests.rs"
96-
required-features = ["http-client"]
99+
required-features = ["runtime", "http-client"]
100+
101+
[[test]]
102+
name = "sqlite_host_tests"
103+
path = "tests/vm/sqlite_host_tests.rs"
104+
required-features = ["sqlite"]
97105

98106
[build-dependencies]
99107
syn = { version = "2", features = ["full"] }

build.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ fn main() {
149149
println!("cargo:rerun-if-changed={}", catalog_path.display());
150150
let catalog = parse_catalog(&catalog_path);
151151

152-
let host_sources = [
152+
let mut host_sources = vec![
153153
SourceSpec {
154154
path: "src/builtins/runtime/host.rs".to_string(),
155155
module: "host".to_string(),
@@ -160,7 +160,19 @@ fn main() {
160160
module: "http".to_string(),
161161
category: SourceCategory::DefaultHost,
162162
},
163+
SourceSpec {
164+
path: "src/builtins/runtime/context_host.rs".to_string(),
165+
module: "context_host".to_string(),
166+
category: SourceCategory::DefaultHost,
167+
},
163168
];
169+
if env::var_os("CARGO_FEATURE_SQLITE").is_some() {
170+
host_sources.push(SourceSpec {
171+
path: "src/builtins/runtime/sqlite.rs".to_string(),
172+
module: "sqlite".to_string(),
173+
category: SourceCategory::DefaultHost,
174+
});
175+
}
164176
let builtin_sources = builtin_source_specs(&namespaces);
165177
let core_sources = [SourceSpec {
166178
path: "src/builtins/runtime/core.rs".to_string(),
@@ -941,6 +953,7 @@ fn render_builtin_catalog(
941953

942954
writeln!(&mut out, "impl BuiltinFunction {{").unwrap();
943955
render_builtin_name_method(&mut out, &builtin_variant_order, &actual_builtin_by_variant);
956+
render_builtin_capability_method(&mut out, builtin_callables);
944957
render_builtin_arity_method(&mut out, &builtin_variant_order, &actual_builtin_by_variant);
945958
render_builtin_accepts_arity_method(
946959
&mut out,
@@ -1082,6 +1095,12 @@ fn render_builtin_runtime_dispatch(
10821095
)
10831096
.unwrap();
10841097
}
1098+
writeln!(
1099+
&mut out,
1100+
" registry.mark_runtime_owned_pending({:?});",
1101+
callable.name
1102+
)
1103+
.unwrap();
10851104
}
10861105
writeln!(&mut out, "}}").unwrap();
10871106
writeln!(&mut out).unwrap();
@@ -1098,6 +1117,12 @@ fn render_builtin_runtime_dispatch(
10981117
.render_bind_static_call(&callable.name, &host_wrapper_adapter_name(callable));
10991118
writeln!(&mut out, " {:?} => {{", callable.name).unwrap();
11001119
writeln!(&mut out, " {bind_call}").unwrap();
1120+
writeln!(
1121+
&mut out,
1122+
" vm.mark_runtime_owned_pending_binding({:?});",
1123+
callable.name
1124+
)
1125+
.unwrap();
11011126
writeln!(&mut out, " true").unwrap();
11021127
writeln!(&mut out, " }}").unwrap();
11031128
}
@@ -1415,6 +1440,35 @@ fn render_builtin_name_method(
14151440
writeln!(out).unwrap();
14161441
}
14171442

1443+
fn render_builtin_capability_method(out: &mut String, builtin_callables: &[CallableDecl]) {
1444+
let mut capability_variants = Vec::new();
1445+
for callable in builtin_callables {
1446+
let variant = builtin_variant_name(&callable.name);
1447+
if !capability_variants.contains(&variant) {
1448+
capability_variants.push(variant);
1449+
}
1450+
}
1451+
capability_variants.sort();
1452+
writeln!(out, " #[cfg(feature = \"runtime\")]").unwrap();
1453+
writeln!(
1454+
out,
1455+
" pub(crate) const fn requires_explicit_host_capability(self) -> bool {{"
1456+
)
1457+
.unwrap();
1458+
if capability_variants.is_empty() {
1459+
writeln!(out, " false").unwrap();
1460+
} else {
1461+
let patterns = capability_variants
1462+
.iter()
1463+
.map(|variant| format!("BuiltinFunction::{variant}"))
1464+
.collect::<Vec<_>>()
1465+
.join(" | ");
1466+
writeln!(out, " matches!(self, {patterns})").unwrap();
1467+
}
1468+
writeln!(out, " }}").unwrap();
1469+
writeln!(out).unwrap();
1470+
}
1471+
14181472
fn render_builtin_arity_method(
14191473
out: &mut String,
14201474
builtin_variant_order: &[String],

crates/rustscript/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ runtime = ["pd_vm_crate/runtime"]
1616
edge-abi = ["pd_vm_crate/edge-abi"]
1717
cli = ["pd_vm_crate/cli"]
1818
cranelift-jit = ["pd_vm_crate/cranelift-jit"]
19+
http-client = ["runtime", "pd_vm_crate/http-client"]
20+
sqlite = ["pd_vm_crate/sqlite"]
1921

2022
[dependencies]
21-
pd_vm_crate = { package = "pd-vm", path = "../..", version = ">=0.1.0, <1.0.0" }
23+
pd_vm_crate = { package = "pd-vm", path = "../..", version = "=0.1.0", default-features = false }

crates/rustscript/tests/alias_smoke.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,38 @@ fn alias_exports_op_code() {
2121
let _ = rustscript::OpCode::Nop;
2222
let _ = rustscript::OpCode::Add;
2323
}
24+
25+
#[cfg(feature = "runtime")]
26+
#[test]
27+
fn alias_exports_public_runtime_event_contract() {
28+
fn accept_sink<S: rustscript::EventSink>(_sink: S) {}
29+
30+
struct Sink;
31+
impl rustscript::EventSink for Sink {
32+
fn emit(&mut self, _payload: rustscript::EventPayload) -> rustscript::RuntimeResult<()> {
33+
Ok(())
34+
}
35+
}
36+
37+
accept_sink(Sink);
38+
}
39+
40+
#[cfg(feature = "http-client")]
41+
#[test]
42+
fn alias_http_client_includes_runtime_contract() {
43+
fn accept_runtime_result(_result: rustscript::RuntimeResult<()>) {}
44+
45+
accept_runtime_result(Ok(()));
46+
}
47+
48+
#[cfg(feature = "sqlite")]
49+
#[test]
50+
fn alias_exports_public_sqlite_configuration() {
51+
let program = rustscript::compile_source("0;")
52+
.expect("minimal alias SQLite program should compile")
53+
.program;
54+
let mut vm = rustscript::Vm::new(program);
55+
vm.configure_sqlite(rustscript::SqlitePolicy::default());
56+
let _limits = rustscript::SqliteLimits::default();
57+
vm.clear_sqlite_configuration();
58+
}

docs/callable-runtime.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ RustScript bytecode format version 11 (VMBC v11) introduces runtime script call
99
- callable environments are bound through the internal builtin call path; callable creation adds no bytecode opcode.
1010
- `ret` completes the active script frame. A nested frame leaves exactly one result at the caller segment base, using `null` when the body produced no value. Root `ret` keeps the historical program-result stack behavior.
1111

12-
VMBC v11 is a hard format boundary. Decoders reject all earlier versions (v10 and below) with a deterministic unsupported-version error; there is no compatibility decoder and no old-ID alias. The stream includes script-function entry ranges, callable prototypes, function regions, root callable bindings, and call indices drawn from the static builtin catalog. PDRC v6 recordings and AOT artifacts (format 7, ABI 6) use their corresponding bumped versions and include callable metadata in cache identity.
12+
VMBC v11 is a hard format boundary. Decoders reject all earlier versions (v10 and below) with a deterministic unsupported-version error; there is no compatibility decoder and no old-ID alias. The stream includes script-function entry ranges, callable prototypes, function regions, root callable bindings, and call indices drawn from the static builtin catalog. PDRC v6 recordings and AOT artifacts (format 7, ABI 7) use their corresponding bumped versions and include callable metadata in cache identity.
1313

1414
## Static builtin IDs
1515

0 commit comments

Comments
 (0)