Skip to content

Commit 2c7dff7

Browse files
committed
feat(sqlite): add scoped SQLite host functions
1 parent 569253a commit 2c7dff7

18 files changed

Lines changed: 2316 additions & 10 deletions

Cargo.lock

Lines changed: 134 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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ name = "vm"
2727
[features]
2828
default = ["runtime", "cli", "cranelift-jit"]
2929
runtime = []
30+
sqlite = ["runtime", "dep:rusqlite"]
3031
edge-abi = [
3132
"dep:edge_abi",
3233
"edge_abi/console",
@@ -60,6 +61,7 @@ cranelift-jit = { version = "0.129.1", optional = true }
6061
cranelift-module = { version = "0.129.1", optional = true }
6162
cranelift-native = { version = "0.129.1", optional = true }
6263
pd-host-function = { path = "./pd-host-function", version = "0.1.0" }
64+
rusqlite = { version = "0.32", default-features = false, features = ["bundled", "hooks", "limits"], optional = true }
6365
edge_abi = { package = "pd-edge-abi", version = "0.1.1", default-features = false, optional = true }
6466
futures-channel = "0.3"
6567
paste = "1"

build.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ impl HostBindingKind {
6969
/// Documented call-index blocks shared by builtins and host imports.
7070
///
7171
/// Must match the block table in `src/builtins/catalog.rs`.
72+
/// The ordinary block's top four IDs are frozen for SQLite. Keep allocation
73+
/// explicit here: incrementing a `u16` cursor from `0xFFFF` would overflow.
74+
pub(crate) const SQLITE_RESERVED_TOP_START: u16 = 0xFFFC;
75+
pub(crate) const SQLITE_RESERVED_TOP_END: u16 = u16::MAX;
7276
pub(crate) const ORDINARY_BLOCK_START: u16 = 0xFFA2;
7377
pub(crate) const SPECIAL_CALL_BLOCK_START: u16 = 0xFF90;
7478
pub(crate) const SPECIAL_CALL_BLOCK_END: u16 = 0xFFA1;
@@ -143,11 +147,24 @@ fn main() {
143147
.join("runtime")
144148
.join("namespaces.rs");
145149
println!("cargo:rerun-if-changed={}", namespace_manifest.display());
146-
let namespaces = parse_namespace_manifest(&namespace_manifest);
150+
let mut namespaces = parse_namespace_manifest(&namespace_manifest);
147151

148152
let catalog_path = manifest_dir.join("src").join("builtins").join("catalog.rs");
149153
println!("cargo:rerun-if-changed={}", catalog_path.display());
150-
let catalog = parse_catalog(&catalog_path);
154+
let mut catalog = parse_catalog(&catalog_path);
155+
156+
// The SQLite namespace is optional: its builtin module links rusqlite,
157+
// which is not available on every target or without the `sqlite` feature.
158+
// When the feature is off (or the target is wasm32, where rusqlite's
159+
// bundled build is unsupported), drop the namespace and its static
160+
// catalog IDs so the generated catalog, dispatch, and compiler namespace
161+
// surface stay consistent and feature-clean.
162+
let sqlite_enabled = env::var_os("CARGO_FEATURE_SQLITE").is_some()
163+
&& env::var("CARGO_CFG_TARGET_ARCH").as_deref() != Ok("wasm32");
164+
if !sqlite_enabled {
165+
namespaces.retain(|namespace| namespace.namespace != "sqlite");
166+
catalog.retain(|entry| !entry.source_name.starts_with("sqlite::"));
167+
}
151168

152169
let host_sources = [SourceSpec {
153170
path: "src/builtins/runtime/host.rs".to_string(),
@@ -522,6 +539,7 @@ fn strip_quoted(value: &str) -> Option<String> {
522539
/// - a catalog variant does not match the derived variant for its source name;
523540
/// - a class disagrees with the dispatch classification (ordinary vs
524541
/// special-call) or with the `__` internal-name prefix;
542+
/// - a non-SQLite entry uses one of the frozen top-u16 SQLite IDs;
525543
/// - an ID falls outside its documented block.
526544
pub(crate) fn validate_catalog_contract(
527545
entries: &[CatalogEntry],
@@ -547,6 +565,16 @@ pub(crate) fn validate_catalog_contract(
547565
);
548566
}
549567
for entry in entries {
568+
if (SQLITE_RESERVED_TOP_START..=SQLITE_RESERVED_TOP_END).contains(&entry.id)
569+
&& !entry.source_name.starts_with("sqlite::")
570+
{
571+
panic!(
572+
"builtin '{}' id 0x{:04X} falls in the SQLite-reserved top-u16 range \
573+
0x{SQLITE_RESERVED_TOP_START:04X}..=0x{SQLITE_RESERVED_TOP_END:04X}; \
574+
do not allocate IDs by arithmetic",
575+
entry.source_name, entry.id
576+
);
577+
}
550578
let expected_variant = builtin_variant_name(&entry.source_name);
551579
if expected_variant != entry.variant {
552580
panic!(
@@ -766,6 +794,11 @@ fn render_builtin_catalog(
766794
.collect::<Vec<_>>(),
767795
));
768796

797+
writeln!(
798+
&mut out,
799+
"// The top-u16 range 0xFFFC..=0xFFFF is reserved for SQLite's frozen IDs; do not allocate it arithmetically."
800+
)
801+
.unwrap();
769802
writeln!(
770803
&mut out,
771804
"#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]"

crates/rustscript/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ name = "rustscript"
1313
[features]
1414
default = ["runtime", "cli", "cranelift-jit"]
1515
runtime = ["pd_vm_crate/runtime"]
16+
sqlite = ["pd_vm_crate/sqlite"]
1617
edge-abi = ["pd_vm_crate/edge-abi"]
1718
cli = ["pd_vm_crate/cli"]
1819
cranelift-jit = ["pd_vm_crate/cranelift-jit"]

pd-vm-nostd/src/generated_builtin_ids.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
// pd-vm-nostd dispatches on the same static indices without a build script.
66
// The workspace test `static_builtin_ids_are_frozen` fails when this file
77
// drifts from the catalog; do not edit by hand.
8+
//
9+
// The top-u16 range 0xFFFC..=0xFFFF is reserved for SQLite's frozen IDs;
10+
// never allocate a new builtin there by incrementing an integer cursor.
811

912
#![allow(dead_code)]
1013

@@ -50,6 +53,11 @@ pub const IO_WRITE_CALL_INDEX: u16 = 0xFFB9;
5053
pub const IO_FLUSH_CALL_INDEX: u16 = 0xFFBA;
5154
pub const IO_CLOSE_CALL_INDEX: u16 = 0xFFBB;
5255
pub const IO_EXISTS_CALL_INDEX: u16 = 0xFFBC;
56+
pub const SQLITE_OPEN_CALL_INDEX: u16 = 0xFFC3;
57+
pub const SQLITE_EXECUTE_CALL_INDEX: u16 = 0xFFFC;
58+
pub const SQLITE_QUERY_CALL_INDEX: u16 = 0xFFFD;
59+
pub const SQLITE_TRANSACTION_CALL_INDEX: u16 = 0xFFFE;
60+
pub const SQLITE_CLOSE_CALL_INDEX: u16 = 0xFFFF;
5361
pub const RE_MATCH_CALL_INDEX: u16 = 0xFFBD;
5462
pub const RE_FIND_CALL_INDEX: u16 = 0xFFBE;
5563
pub const RE_REPLACE_CALL_INDEX: u16 = 0xFFBF;
@@ -163,6 +171,7 @@ pub const ALL_CALL_INDICES: &[u16] = &[
163171
RE_SPLIT_CALL_INDEX,
164172
RE_CAPTURES_CALL_INDEX,
165173
JSON_ENCODE_CALL_INDEX,
174+
SQLITE_OPEN_CALL_INDEX,
166175
JSON_DECODE_CALL_INDEX,
167176
JIT_SET_CONFIG_CALL_INDEX,
168177
JIT_GET_CONFIG_CALL_INDEX,
@@ -219,4 +228,8 @@ pub const ALL_CALL_INDICES: &[u16] = &[
219228
MATH_CLAMP_CALL_INDEX,
220229
MATH_MUL_ADD_CALL_INDEX,
221230
COUNT_CALL_INDEX,
231+
SQLITE_EXECUTE_CALL_INDEX,
232+
SQLITE_QUERY_CALL_INDEX,
233+
SQLITE_TRANSACTION_CALL_INDEX,
234+
SQLITE_CLOSE_CALL_INDEX,
222235
];

src/builtins/catalog.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
// | special-call | 0xFF90 ..= 0xFFA1 | special-call builtins (incl. internal lowering builtins) |
1717
// | ordinary | 0xFFA2 ..= 0xFFFF | ordinary builtins (language + namespaced) |
1818
//
19+
// The top-u16 range 0xFFFC ..= 0xFFFF is reserved for the frozen SQLite
20+
// assignments below. Do not allocate an ordinary ID by incrementing a u16
21+
// cursor through this range: incrementing 0xFFFF would overflow, and these
22+
// four IDs must remain stable even when SQLite is feature-disabled.
23+
//
1924
// # Rules
2025
//
2126
// - IDs are immutable once assigned. Appending or reordering entries must not
@@ -63,6 +68,11 @@ builtin_id!(0xFFB9, "io::write", IoWrite, Ordinary, none);
6368
builtin_id!(0xFFBA, "io::flush", IoFlush, Ordinary, none);
6469
builtin_id!(0xFFBB, "io::close", IoClose, Ordinary, none);
6570
builtin_id!(0xFFBC, "io::exists", IoExists, Ordinary, none);
71+
builtin_id!(0xFFC3, "sqlite::open", SqliteOpen, Ordinary, none);
72+
builtin_id!(0xFFFC, "sqlite::execute", SqliteExecute, Ordinary, none);
73+
builtin_id!(0xFFFD, "sqlite::query", SqliteQuery, Ordinary, none);
74+
builtin_id!(0xFFFE, "sqlite::transaction", SqliteTransaction, Ordinary, none);
75+
builtin_id!(0xFFFF, "sqlite::close", SqliteClose, Ordinary, none);
6676
builtin_id!(0xFFBD, "re::match", ReMatch, Ordinary, none);
6777
builtin_id!(0xFFBE, "re::find", ReFind, Ordinary, none);
6878
builtin_id!(0xFFBF, "re::replace", ReReplace, Ordinary, none);

0 commit comments

Comments
 (0)