Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ name = "vm"
[features]
default = ["runtime", "cli", "cranelift-jit"]
runtime = []
sqlite = ["runtime", "dep:rusqlite"]
edge-abi = [
"dep:edge_abi",
"edge_abi/console",
Expand Down Expand Up @@ -60,6 +61,7 @@ cranelift-jit = { version = "0.129.1", optional = true }
cranelift-module = { version = "0.129.1", optional = true }
cranelift-native = { version = "0.129.1", optional = true }
pd-host-function = { path = "./pd-host-function", version = "0.1.0" }
rusqlite = { version = "0.32", default-features = false, features = ["bundled", "hooks", "limits"], optional = true }
edge_abi = { package = "pd-edge-abi", version = "0.1.1", default-features = false, optional = true }
futures-channel = "0.3"
paste = "1"
Expand Down
55 changes: 49 additions & 6 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ impl HostBindingKind {
/// Documented call-index blocks shared by builtins and host imports.
///
/// Must match the block table in `src/builtins/catalog.rs`.
/// The ordinary block's top four IDs are frozen for SQLite. Keep allocation
/// explicit here: incrementing a `u16` cursor from `0xFFFF` would overflow.
pub(crate) const SQLITE_RESERVED_TOP_START: u16 = 0xFFFC;
pub(crate) const SQLITE_RESERVED_TOP_END: u16 = u16::MAX;
pub(crate) const ORDINARY_BLOCK_START: u16 = 0xFFA2;
pub(crate) const SPECIAL_CALL_BLOCK_START: u16 = 0xFF90;
pub(crate) const SPECIAL_CALL_BLOCK_END: u16 = 0xFFA1;
Expand Down Expand Up @@ -143,11 +147,24 @@ fn main() {
.join("runtime")
.join("namespaces.rs");
println!("cargo:rerun-if-changed={}", namespace_manifest.display());
let namespaces = parse_namespace_manifest(&namespace_manifest);
let mut namespaces = parse_namespace_manifest(&namespace_manifest);

let catalog_path = manifest_dir.join("src").join("builtins").join("catalog.rs");
println!("cargo:rerun-if-changed={}", catalog_path.display());
let catalog = parse_catalog(&catalog_path);
let mut catalog = parse_catalog(&catalog_path);

// The SQLite namespace is optional: its builtin module links rusqlite,
// which is not available on every target or without the `sqlite` feature.
// When the feature is off (or the target is wasm32, where rusqlite's
// bundled build is unsupported), drop the namespace and its static
// catalog IDs so the generated catalog, dispatch, and compiler namespace
// surface stay consistent and feature-clean.
let sqlite_enabled = env::var_os("CARGO_FEATURE_SQLITE").is_some()
&& env::var("CARGO_CFG_TARGET_ARCH").as_deref() != Ok("wasm32");
if !sqlite_enabled {
namespaces.retain(|namespace| namespace.namespace != "sqlite");
catalog.retain(|entry| !entry.source_name.starts_with("sqlite::"));
}

let host_sources = [SourceSpec {
path: "src/builtins/runtime/host.rs".to_string(),
Expand Down Expand Up @@ -229,10 +246,20 @@ fn write_generated_file(path: &Path, contents: &str) {
fn builtin_source_specs(namespaces: &[NamespaceDecl]) -> Vec<SourceSpec> {
namespaces
.iter()
.map(|namespace| SourceSpec {
path: format!("src/builtins/runtime/{}.rs", namespace.module),
module: namespace.module.clone(),
category: SourceCategory::NamespacedBuiltin,
.map(|namespace| {
// At this layer the `io` namespace maps directly to the blocking
// backend; the async/blocking split is introduced later with the
// host async execution layer.
let path = if namespace.module == "io" {
"src/builtins/runtime/io/blocking.rs".to_string()
} else {
format!("src/builtins/runtime/{}.rs", namespace.module)
};
SourceSpec {
path,
module: namespace.module.clone(),
category: SourceCategory::NamespacedBuiltin,
}
})
.collect()
}
Expand Down Expand Up @@ -522,6 +549,7 @@ fn strip_quoted(value: &str) -> Option<String> {
/// - a catalog variant does not match the derived variant for its source name;
/// - a class disagrees with the dispatch classification (ordinary vs
/// special-call) or with the `__` internal-name prefix;
/// - a non-SQLite entry uses one of the frozen top-u16 SQLite IDs;
/// - an ID falls outside its documented block.
pub(crate) fn validate_catalog_contract(
entries: &[CatalogEntry],
Expand Down Expand Up @@ -554,6 +582,16 @@ pub(crate) fn validate_catalog_contract(
entry.source_name, entry.variant
);
}
if (SQLITE_RESERVED_TOP_START..=SQLITE_RESERVED_TOP_END).contains(&entry.id)
&& !entry.source_name.starts_with("sqlite::")
{
panic!(
"builtin '{}' id 0x{:04X} falls in the SQLite-reserved top-u16 range \
0x{SQLITE_RESERVED_TOP_START:04X}..=0x{SQLITE_RESERVED_TOP_END:04X}; \
do not allocate IDs by arithmetic",
entry.source_name, entry.id
);
}
let is_special_call = special_variants.contains(&entry.variant);
match entry.class {
CatalogClass::Ordinary => {
Expand Down Expand Up @@ -766,6 +804,11 @@ fn render_builtin_catalog(
.collect::<Vec<_>>(),
));

writeln!(
&mut out,
"// The top-u16 range 0xFFFC..=0xFFFF is reserved for SQLite's frozen IDs; do not allocate it arithmetically."
)
.unwrap();
writeln!(
&mut out,
"#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]"
Expand Down
1 change: 1 addition & 0 deletions crates/rustscript/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ name = "rustscript"
[features]
default = ["runtime", "cli", "cranelift-jit"]
runtime = ["pd_vm_crate/runtime"]
sqlite = ["pd_vm_crate/sqlite"]
edge-abi = ["pd_vm_crate/edge-abi"]
cli = ["pd_vm_crate/cli"]
cranelift-jit = ["pd_vm_crate/cranelift-jit"]
Expand Down
4 changes: 2 additions & 2 deletions pd-host-function/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,13 +223,13 @@ fn generate_vm_wrapper(

Ok(quote! {
#[allow(dead_code)]
pub(super) fn #wrapper_name(#(#imm_wrapper_params),*) -> #wrapper_output {
pub(crate) fn #wrapper_name(#(#imm_wrapper_params),*) -> #wrapper_output {
#(#imm_extract_stmts)*
#call_expr
}

#[allow(dead_code)]
pub(super) fn #mutable_wrapper_name(#(#mut_wrapper_params),*) -> #wrapper_output {
pub(crate) fn #mutable_wrapper_name(#(#mut_wrapper_params),*) -> #wrapper_output {
#(#mut_extract_stmts)*
#call_expr
}
Expand Down
Loading
Loading