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
37 changes: 35 additions & 2 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 @@ -522,6 +539,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 All @@ -547,6 +565,16 @@ pub(crate) fn validate_catalog_contract(
);
}
for entry in entries {
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 expected_variant = builtin_variant_name(&entry.source_name);
if expected_variant != entry.variant {
panic!(
Expand Down Expand Up @@ -766,6 +794,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
13 changes: 13 additions & 0 deletions pd-vm-nostd/src/generated_builtin_ids.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
// pd-vm-nostd dispatches on the same static indices without a build script.
// The workspace test `static_builtin_ids_are_frozen` fails when this file
// drifts from the catalog; do not edit by hand.
//
// The top-u16 range 0xFFFC..=0xFFFF is reserved for SQLite's frozen IDs;
// never allocate a new builtin there by incrementing an integer cursor.

#![allow(dead_code)]

Expand Down Expand Up @@ -50,6 +53,11 @@ pub const IO_WRITE_CALL_INDEX: u16 = 0xFFB9;
pub const IO_FLUSH_CALL_INDEX: u16 = 0xFFBA;
pub const IO_CLOSE_CALL_INDEX: u16 = 0xFFBB;
pub const IO_EXISTS_CALL_INDEX: u16 = 0xFFBC;
pub const SQLITE_OPEN_CALL_INDEX: u16 = 0xFFC3;
pub const SQLITE_EXECUTE_CALL_INDEX: u16 = 0xFFFC;
pub const SQLITE_QUERY_CALL_INDEX: u16 = 0xFFFD;
pub const SQLITE_TRANSACTION_CALL_INDEX: u16 = 0xFFFE;
pub const SQLITE_CLOSE_CALL_INDEX: u16 = 0xFFFF;
pub const RE_MATCH_CALL_INDEX: u16 = 0xFFBD;
pub const RE_FIND_CALL_INDEX: u16 = 0xFFBE;
pub const RE_REPLACE_CALL_INDEX: u16 = 0xFFBF;
Expand Down Expand Up @@ -163,6 +171,7 @@ pub const ALL_CALL_INDICES: &[u16] = &[
RE_SPLIT_CALL_INDEX,
RE_CAPTURES_CALL_INDEX,
JSON_ENCODE_CALL_INDEX,
SQLITE_OPEN_CALL_INDEX,
JSON_DECODE_CALL_INDEX,
JIT_SET_CONFIG_CALL_INDEX,
JIT_GET_CONFIG_CALL_INDEX,
Expand Down Expand Up @@ -219,4 +228,8 @@ pub const ALL_CALL_INDICES: &[u16] = &[
MATH_CLAMP_CALL_INDEX,
MATH_MUL_ADD_CALL_INDEX,
COUNT_CALL_INDEX,
SQLITE_EXECUTE_CALL_INDEX,
SQLITE_QUERY_CALL_INDEX,
SQLITE_TRANSACTION_CALL_INDEX,
SQLITE_CLOSE_CALL_INDEX,
];
10 changes: 10 additions & 0 deletions src/builtins/catalog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
// | special-call | 0xFF90 ..= 0xFFA1 | special-call builtins (incl. internal lowering builtins) |
// | ordinary | 0xFFA2 ..= 0xFFFF | ordinary builtins (language + namespaced) |
//
// The top-u16 range 0xFFFC ..= 0xFFFF is reserved for the frozen SQLite
// assignments below. Do not allocate an ordinary ID by incrementing a u16
// cursor through this range: incrementing 0xFFFF would overflow, and these
// four IDs must remain stable even when SQLite is feature-disabled.
//
// # Rules
//
// - IDs are immutable once assigned. Appending or reordering entries must not
Expand Down Expand Up @@ -63,6 +68,11 @@ builtin_id!(0xFFB9, "io::write", IoWrite, Ordinary, none);
builtin_id!(0xFFBA, "io::flush", IoFlush, Ordinary, none);
builtin_id!(0xFFBB, "io::close", IoClose, Ordinary, none);
builtin_id!(0xFFBC, "io::exists", IoExists, Ordinary, none);
builtin_id!(0xFFC3, "sqlite::open", SqliteOpen, Ordinary, none);
builtin_id!(0xFFFC, "sqlite::execute", SqliteExecute, Ordinary, none);
builtin_id!(0xFFFD, "sqlite::query", SqliteQuery, Ordinary, none);
builtin_id!(0xFFFE, "sqlite::transaction", SqliteTransaction, Ordinary, none);
builtin_id!(0xFFFF, "sqlite::close", SqliteClose, Ordinary, none);
builtin_id!(0xFFBD, "re::match", ReMatch, Ordinary, none);
builtin_id!(0xFFBE, "re::find", ReFind, Ordinary, none);
builtin_id!(0xFFBF, "re::replace", ReReplace, Ordinary, none);
Expand Down
Loading
Loading