diff --git a/crates/haku-cli/src/main.rs b/crates/haku-cli/src/main.rs index 43be465..02f2770 100644 --- a/crates/haku-cli/src/main.rs +++ b/crates/haku-cli/src/main.rs @@ -51,6 +51,7 @@ fn eval(code: &str) -> Result> { call_stack_capacity: 256, ref_capacity: 256, fuel: 32768, + memory: 1024, }, ); let chunk_id = system.add_chunk(chunk)?; diff --git a/crates/haku-wasm/src/lib.rs b/crates/haku-wasm/src/lib.rs index e723590..dc94179 100644 --- a/crates/haku-wasm/src/lib.rs +++ b/crates/haku-wasm/src/lib.rs @@ -45,6 +45,7 @@ struct Limits { call_stack_capacity: usize, ref_capacity: usize, fuel: usize, + memory: usize, pixmap_stack_capacity: usize, transform_stack_capacity: usize, } @@ -60,6 +61,7 @@ impl Default for Limits { call_stack_capacity: 256, ref_capacity: 2048, fuel: 65536, + memory: 1024 * 1024, pixmap_stack_capacity: 4, transform_stack_capacity: 16, } @@ -98,6 +100,7 @@ limit_setter!(stack_capacity); limit_setter!(call_stack_capacity); limit_setter!(ref_capacity); limit_setter!(fuel); +limit_setter!(memory); limit_setter!(pixmap_stack_capacity); limit_setter!(transform_stack_capacity); @@ -131,6 +134,7 @@ unsafe extern "C" fn haku_instance_new(limits: *const Limits) -> *mut Instance { call_stack_capacity: limits.call_stack_capacity, ref_capacity: limits.ref_capacity, fuel: limits.fuel, + memory: limits.memory, }, ); diff --git a/crates/haku/src/render.rs b/crates/haku/src/render.rs index 11d32af..e91bea9 100644 --- a/crates/haku/src/render.rs +++ b/crates/haku/src/render.rs @@ -71,12 +71,17 @@ impl<'a> Renderer<'a> { let (_id, scribble) = vm .get_ref_value(value) .ok_or_else(|| Self::create_exception(vm, value, NOT_A_SCRIBBLE))?; - let Ref::Scribble(scribble) = scribble else { - return Err(Self::create_exception(vm, value, NOT_A_SCRIBBLE)); - }; - match scribble { - Scribble::Stroke(stroke) => self.render_stroke(vm, value, stroke)?, + match &scribble { + Ref::List(list) => { + for element in &list.elements { + self.render(vm, *element)?; + } + } + Ref::Scribble(scribble) => match scribble { + Scribble::Stroke(stroke) => self.render_stroke(vm, value, stroke)?, + }, + _ => return Err(Self::create_exception(vm, value, NOT_A_SCRIBBLE))?, } Ok(()) diff --git a/crates/haku/src/system.rs b/crates/haku/src/system.rs index 052cb5c..29f3a2b 100644 --- a/crates/haku/src/system.rs +++ b/crates/haku/src/system.rs @@ -99,8 +99,10 @@ impl Display for ChunkError { impl Error for ChunkError {} pub mod fns { + use alloc::vec::Vec; + use crate::{ - value::{Ref, Rgba, Scribble, Shape, Stroke, Value, Vec4}, + value::{List, Ref, Rgba, Scribble, Shape, Stroke, Value, Vec4}, vm::{Exception, FnArgs, Vm}, }; @@ -133,6 +135,8 @@ pub mod fns { 0x88 ".b" => rgba_b, 0x89 ".a" => rgba_a, + 0x90 "list" => list, + 0xc0 "to-shape" => to_shape_f, 0xc1 "line" => line, 0xe0 "stroke" => stroke, @@ -389,6 +393,13 @@ pub mod fns { Ok(Value::Number(rgba.r)) } + pub fn list(vm: &mut Vm, args: FnArgs) -> Result { + let elements: Vec<_> = (0..args.num()).map(|i| args.get(vm, i)).collect(); + vm.track_array(&elements)?; + let id = vm.create_ref(Ref::List(List { elements }))?; + Ok(Value::Ref(id)) + } + fn to_shape(value: Value, vm: &Vm) -> Option { match value { Value::Nil | Value::False | Value::True | Value::Number(_) | Value::Rgba(_) => None, diff --git a/crates/haku/src/value.rs b/crates/haku/src/value.rs index 59672b7..6490eaa 100644 --- a/crates/haku/src/value.rs +++ b/crates/haku/src/value.rs @@ -105,6 +105,7 @@ impl RefId { #[derive(Debug, Clone)] pub enum Ref { Closure(Closure), + List(List), Shape(Shape), Scribble(Scribble), } @@ -143,6 +144,11 @@ pub struct Closure { pub captures: Vec, } +#[derive(Debug, Clone, PartialEq)] +pub struct List { + pub elements: Vec, +} + #[derive(Debug, Clone)] pub enum Shape { Point(Vec4), diff --git a/crates/haku/src/vm.rs b/crates/haku/src/vm.rs index a73331d..71bf5e9 100644 --- a/crates/haku/src/vm.rs +++ b/crates/haku/src/vm.rs @@ -17,6 +17,7 @@ pub struct VmLimits { pub call_stack_capacity: usize, pub ref_capacity: usize, pub fuel: usize, + pub memory: usize, } #[derive(Debug, Clone)] @@ -26,6 +27,7 @@ pub struct Vm { refs: Vec, defs: Vec, fuel: usize, + memory: usize, } #[derive(Debug, Clone, Copy)] @@ -33,6 +35,7 @@ pub struct VmImage { refs: usize, defs: usize, fuel: usize, + memory: usize, } #[derive(Debug, Clone)] @@ -55,6 +58,7 @@ impl Vm { refs: Vec::with_capacity(limits.ref_capacity), defs: Vec::from_iter(iter::repeat(Value::Nil).take(defs.len() as usize)), fuel: limits.fuel, + memory: limits.memory, } } @@ -75,6 +79,7 @@ impl Vm { refs: self.refs.len(), defs: self.defs.len(), fuel: self.fuel, + memory: self.memory, } } @@ -398,6 +403,14 @@ impl Vm { pub fn create_exception(&self, message: &'static str) -> Exception { Exception { message } } + + pub fn track_array(&mut self, array: &[T]) -> Result<(), Exception> { + self.memory = self + .memory + .checked_sub(core::mem::size_of_val(array)) + .ok_or_else(|| self.create_exception("out of heap memory"))?; + Ok(()) + } } pub struct FnArgs { diff --git a/crates/haku/tests/language.rs b/crates/haku/tests/language.rs index 746068a..48d52e2 100644 --- a/crates/haku/tests/language.rs +++ b/crates/haku/tests/language.rs @@ -44,6 +44,7 @@ fn eval(code: &str) -> Result> { call_stack_capacity: 256, ref_capacity: 256, fuel: 32768, + memory: 1024, }; let mut vm = Vm::new(defs, &limits); let chunk_id = system.add_chunk(chunk)?; diff --git a/crates/rkgk/src/haku.rs b/crates/rkgk/src/haku.rs index 58df968..073d381 100644 --- a/crates/rkgk/src/haku.rs +++ b/crates/rkgk/src/haku.rs @@ -30,6 +30,7 @@ pub struct Limits { pub call_stack_capacity: usize, pub ref_capacity: usize, pub fuel: usize, + pub memory: usize, pub pixmap_stack_capacity: usize, pub transform_stack_capacity: usize, } @@ -58,6 +59,7 @@ impl Haku { call_stack_capacity: limits.call_stack_capacity, ref_capacity: limits.ref_capacity, fuel: limits.fuel, + memory: limits.memory, }, ); diff --git a/rkgk.toml b/rkgk.toml index 4a2202b..572feb2 100644 --- a/rkgk.toml +++ b/rkgk.toml @@ -68,6 +68,11 @@ ref_capacity = 2048 # out of fuel means it will not be rendered at all, because there is no complete value returned. fuel = 65536 +# Amount of heap memory available to the VM. +# Heap memory is used to limit how much data refs can allocate. +# In particular, large arrays use up this memory - such as list backing arrays. +memory = 1048576 + # Capacity of the renderer's pixmap stack. # The pixmap stack is used for blending layers together within a brush. # Each (composite)-type scribble requires a single entry on this pixmap stack.