Skip to content

Commit 4db623c

Browse files
committed
Move Cache hooks into core runtime
1 parent f29d0b5 commit 4db623c

8 files changed

Lines changed: 129 additions & 17 deletions

File tree

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -410,15 +410,13 @@ rss verify-rust [--json] <file.rss>
410410
rss verify-rust [--json] <file.rss> --out-dir <directory>
411411
```
412412

413-
`rss check` loads bundled core `.rssi` signatures by default. Use `--no-core` only when testing a file against user-supplied interfaces in isolation.
414-
415-
The checker prototype also keeps a small `core/prototype` interface bundle for the remaining cache fixtures that have not yet moved into `core/`. Those signatures are still `.rssi` sources, not a second Rust-side builtin table.
413+
`rss check` loads bundled core `.rssi` signatures by default. Use `--no-core` only when testing a file against user-supplied interfaces in isolation. Bundled signatures are ordinary `.rssi` files under `core/`, not a second Rust-side builtin table.
416414

417415
If a lowered package contains `fn main() -> Unit` or `fn main() -> Result<Unit, E>`, `rss lower --rust --out-dir` also emits a Rust `src/main.rs` harness. `rss run <file.rss>` uses the same lowering path, writes a temporary Rust package, and delegates execution to `cargo run`. Use `rss run <file.rss> --out-dir <directory>` to keep the generated Rust package for inspection.
418416

419417
`rss verify-rust <file.rss> --out-dir <directory>` keeps the generated package used for backend checking, including `rsscript-source-map.json`, so unmappable rustc diagnostics can be inspected against the generated Rust.
420418

421-
The first observable runtime boundaries are `Log.write(message: read String)`, `Assert.equal(left: read String, right: read String)`, `OS.close(fd: Fd)`, `List.consume(list: take List<T>)`, `Buffer.consume(buffer: take Buffer)`, and the core `File`, `Json`, `Csv`, `Image`, `ImageCache`, HTTP handler, DB resource-pool, config reload, rules config reload, interpreter object-cycle, and `Counter` APIs, which lower to explicit runtime hooks. `ImageCache` is the first small retained managed-container hook: it stores managed image handles and enforces a capacity limit. The interpreter hooks model `Environment` and `FunctionObject` as managed handles, including an environment/function closure cycle. Simple core operations such as `String.concat(left: read String, right: read String)` keep RSScript `.rssi` signatures for checking and review, but lower directly to Rust standard-library expressions. Built-in boolean literals, numeric operators, comparison operators, `Option<T>` constructors, and core surface types such as `Bytes`, `Buffer`, `Path`, `List<T>`, `Map<K,V>`, and `Set<T>` lower to the corresponding Rust literals, operators, enum constructors, and standard-library types; user-defined operator overloading remains forbidden.
419+
The first observable runtime boundaries are `Log.write(message: read String)`, `Assert.equal(left: read String, right: read String)`, `OS.close(fd: Fd)`, `List.consume(list: take List<T>)`, `Buffer.consume(buffer: take Buffer)`, and the core `File`, `Json`, `Csv`, `Cache`, `Image`, `ImageCache`, HTTP handler, DB resource-pool, config reload, rules config reload, interpreter object-cycle, and `Counter` APIs, which lower to explicit runtime hooks. `ImageCache` is the first small retained managed-container hook: it stores managed image handles and enforces a capacity limit. The interpreter hooks model `Environment` and `FunctionObject` as managed handles, including an environment/function closure cycle. Simple core operations such as `String.concat(left: read String, right: read String)` keep RSScript `.rssi` signatures for checking and review, but lower directly to Rust standard-library expressions. Built-in boolean literals, numeric operators, comparison operators, `Option<T>` constructors, and core surface types such as `Bytes`, `Buffer`, `Path`, `List<T>`, `Map<K,V>`, and `Set<T>` lower to the corresponding Rust literals, operators, enum constructors, and standard-library types; user-defined operator overloading remains forbidden.
422420

423421
Smallest runnable example:
424422

core/cache/cache.rssi

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
pub fn Cache.new() -> fresh Cache
2+
3+
pub fn Cache.insert(
4+
cache: mut Cache,
5+
key: read String,
6+
value: read String,
7+
) -> Unit
8+
effects(retains(value))
9+
10+
pub fn Cache.lookup(cache: read Cache, key: read String) -> String
11+
12+
pub fn Cache.get(cache: read Cache) -> Image

core/prototype/builtins.rssi

Lines changed: 0 additions & 3 deletions
This file was deleted.

examples/cache_lookup.rss

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
fn main() -> Unit {
2+
let cache = Cache.new()
3+
Cache.insert(
4+
cache: mut cache,
5+
key: read "/users",
6+
value: read "handled /users",
7+
)
8+
9+
let body = Cache.lookup(cache: read cache, key: read "/users")
10+
Assert.equal(left: read body, right: read "handled /users")
11+
Log.write(message: read body)
12+
return Unit
13+
}

runtime/src/lib.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::cell::{BorrowError, BorrowMutError, Ref, RefCell, RefMut};
2-
use std::collections::VecDeque;
2+
use std::collections::{HashMap, VecDeque};
33
use std::fmt;
44
use std::io::{Read, Write};
55
use std::ops::{Deref, DerefMut};
@@ -40,6 +40,11 @@ pub struct ConfigStore {
4040
current: ConfigValue,
4141
}
4242

43+
#[derive(Debug, Clone, PartialEq, Eq)]
44+
pub struct Cache {
45+
entries: HashMap<String, String>,
46+
}
47+
4348
#[derive(Debug, Clone, PartialEq, Eq)]
4449
pub struct Rule {
4550
name: String,
@@ -383,6 +388,35 @@ pub fn buffer_consume(buffer: Vec<u8>) {
383388
drop(buffer);
384389
}
385390

391+
pub fn cache_new() -> Cache {
392+
Cache {
393+
entries: HashMap::new(),
394+
}
395+
}
396+
397+
pub fn cache_insert(cache: &mut Cache, key: &str, value: &str) {
398+
cache.entries.insert(key.to_string(), value.to_string());
399+
}
400+
401+
pub fn cache_lookup(cache: &Cache, key: &str) -> String {
402+
cache.entries.get(key).cloned().unwrap_or_default()
403+
}
404+
405+
pub fn cache_get(cache: &Cache) -> Image {
406+
let bytes = cache
407+
.entries
408+
.values()
409+
.next()
410+
.map(|value| value.as_bytes().to_vec())
411+
.unwrap_or_default();
412+
Image {
413+
bytes,
414+
width: None,
415+
height: None,
416+
operations: vec!["cache-get"],
417+
}
418+
}
419+
386420
pub fn request_new(path: &str) -> Request {
387421
Request {
388422
path: path.to_string(),
@@ -1122,6 +1156,16 @@ mod tests {
11221156
super::os_close(0);
11231157
}
11241158

1159+
#[test]
1160+
fn cache_runtime_hooks_insert_and_lookup_values() {
1161+
let mut cache = super::cache_new();
1162+
1163+
super::cache_insert(&mut cache, "/users", "handled /users");
1164+
1165+
assert_eq!(super::cache_lookup(&cache, "/users"), "handled /users");
1166+
assert_eq!(super::cache_get(&cache).bytes, b"handled /users");
1167+
}
1168+
11251169
#[test]
11261170
fn interpreter_runtime_hooks_link_environment_function_cycle() {
11271171
let root = super::manage(super::environment_root());

src/interfaces.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
pub(crate) const CORE_INTERFACES: &[(&str, &str)] = &[
2+
(
3+
"core/cache/cache.rssi",
4+
include_str!("../core/cache/cache.rssi"),
5+
),
26
(
37
"core/cache/image_cache.rssi",
48
include_str!("../core/cache/image_cache.rssi"),
@@ -62,14 +66,6 @@ pub(crate) const CORE_INTERFACES: &[(&str, &str)] = &[
6266
),
6367
];
6468

65-
pub(crate) const PROTOTYPE_INTERFACES: &[(&str, &str)] = &[(
66-
"core/prototype/builtins.rssi",
67-
include_str!("../core/prototype/builtins.rssi"),
68-
)];
69-
7069
pub(crate) fn builtin_interfaces() -> impl Iterator<Item = (&'static str, &'static str)> {
71-
CORE_INTERFACES
72-
.iter()
73-
.chain(PROTOTYPE_INTERFACES.iter())
74-
.copied()
70+
CORE_INTERFACES.iter().copied()
7571
}

src/rust_lower.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,9 @@ impl<'a> RustLowerer<'a> {
811811
"Fd" => "i64".to_string(),
812812
"Bytes" | "Buffer" => "Vec<u8>".to_string(),
813813
"Path" => "std::path::PathBuf".to_string(),
814+
"Cache" if !self.type_kinds.contains_key("Cache") => {
815+
"rsscript_runtime::Cache".to_string()
816+
}
814817
"Rule" if !self.type_kinds.contains_key("Rule") => "rsscript_runtime::Rule".to_string(),
815818
"Config" if !self.type_kinds.contains_key("Config") => {
816819
"rsscript_runtime::Config".to_string()
@@ -1197,6 +1200,10 @@ fn lower_callee(callee: &Callee) -> String {
11971200
callee if is_buffer_consume_callee(callee) => {
11981201
"rsscript_runtime::buffer_consume".to_string()
11991202
}
1203+
callee if is_cache_new_callee(callee) => "rsscript_runtime::cache_new".to_string(),
1204+
callee if is_cache_insert_callee(callee) => "rsscript_runtime::cache_insert".to_string(),
1205+
callee if is_cache_lookup_callee(callee) => "rsscript_runtime::cache_lookup".to_string(),
1206+
callee if is_cache_get_callee(callee) => "rsscript_runtime::cache_get".to_string(),
12001207
callee if is_file_open_callee(callee) => "rsscript_runtime::file_open".to_string(),
12011208
callee if is_file_open_read_callee(callee) => {
12021209
"rsscript_runtime::file_open_read".to_string()
@@ -1331,6 +1338,22 @@ fn is_buffer_consume_callee(callee: &Callee) -> bool {
13311338
matches!(callee, Callee::Qualified { namespace, name } if type_root_name(namespace) == "Buffer" && name == "consume")
13321339
}
13331340

1341+
fn is_cache_new_callee(callee: &Callee) -> bool {
1342+
matches!(callee, Callee::Qualified { namespace, name } if type_root_name(namespace) == "Cache" && name == "new")
1343+
}
1344+
1345+
fn is_cache_insert_callee(callee: &Callee) -> bool {
1346+
matches!(callee, Callee::Qualified { namespace, name } if type_root_name(namespace) == "Cache" && name == "insert")
1347+
}
1348+
1349+
fn is_cache_lookup_callee(callee: &Callee) -> bool {
1350+
matches!(callee, Callee::Qualified { namespace, name } if type_root_name(namespace) == "Cache" && name == "lookup")
1351+
}
1352+
1353+
fn is_cache_get_callee(callee: &Callee) -> bool {
1354+
matches!(callee, Callee::Qualified { namespace, name } if type_root_name(namespace) == "Cache" && name == "get")
1355+
}
1356+
13341357
fn is_string_concat_callee(callee: &Callee) -> bool {
13351358
matches!(callee, Callee::Qualified { namespace, name } if type_root_name(namespace) == "String" && name == "concat")
13361359
}

tests/checker.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ fn bundled_core_interfaces_are_available_to_checker() {
8787
.iter()
8888
.any(|(path, _)| *path == "core/log/log.rssi")
8989
);
90+
assert!(
91+
core_interfaces()
92+
.iter()
93+
.any(|(path, _)| *path == "core/cache/cache.rssi")
94+
);
9095
assert!(
9196
core_interfaces()
9297
.iter()
@@ -372,6 +377,30 @@ fn consume_buffer(buffer: take Buffer) -> Unit {
372377
assert!(rust.contains("rsscript_runtime::buffer_consume(buffer);"));
373378
}
374379

380+
#[test]
381+
fn rust_lowering_maps_cache_core_calls_to_runtime_hooks() {
382+
let source = r#"
383+
fn main() -> Unit {
384+
let cache = Cache.new()
385+
Cache.insert(cache: mut cache, key: read "/users", value: read "handled /users")
386+
let body = Cache.lookup(cache: read cache, key: read "/users")
387+
Log.write(message: read body)
388+
return Unit
389+
}
390+
"#;
391+
let rust = lower_source_to_rust("cache.rss", source).expect("source should lower");
392+
393+
assert!(rust.contains("let mut cache = rsscript_runtime::cache_new();"));
394+
assert!(rust.contains(
395+
"rsscript_runtime::cache_insert(&mut cache, &\"/users\".to_string(), &\"handled /users\".to_string());"
396+
));
397+
assert!(
398+
rust.contains(
399+
"let body = rsscript_runtime::cache_lookup(&cache, &\"/users\".to_string());"
400+
)
401+
);
402+
}
403+
375404
#[test]
376405
fn rust_lowering_maps_file_core_calls_to_runtime_hooks() {
377406
let source = r#"

0 commit comments

Comments
 (0)