2024-08-23 19:45:07 +02:00
|
|
|
#![cfg_attr(not(feature = "std"), no_std)]
|
2024-08-10 23:10:03 +02:00
|
|
|
|
|
|
|
extern crate alloc;
|
|
|
|
|
2024-09-01 09:35:26 +02:00
|
|
|
use core::{alloc::Layout, slice};
|
2024-08-10 23:10:03 +02:00
|
|
|
|
|
|
|
use alloc::{boxed::Box, vec::Vec};
|
|
|
|
use haku::{
|
2024-08-27 20:43:14 +02:00
|
|
|
ast::Ast,
|
2024-09-08 13:53:29 +02:00
|
|
|
bytecode::{Chunk, Defs, DefsImage, DefsLimits},
|
2024-09-01 09:35:26 +02:00
|
|
|
compiler::{compile_expr, ClosureSpec, CompileError, Compiler, Source},
|
2024-08-27 20:43:14 +02:00
|
|
|
diagnostic::Diagnostic,
|
|
|
|
lexer::{lex, Lexer},
|
2024-09-01 09:35:26 +02:00
|
|
|
parser::{self, IntoAstError, Parser},
|
2024-08-10 23:13:20 +02:00
|
|
|
render::{
|
|
|
|
tiny_skia::{Pixmap, PremultipliedColorU8},
|
2024-09-08 13:53:29 +02:00
|
|
|
RendererLimits,
|
2024-08-10 23:13:20 +02:00
|
|
|
},
|
2024-08-27 20:43:14 +02:00
|
|
|
source::SourceCode,
|
2024-08-10 23:10:03 +02:00
|
|
|
system::{ChunkId, System, SystemImage},
|
2024-08-27 20:43:14 +02:00
|
|
|
token::Lexis,
|
2024-09-08 13:53:29 +02:00
|
|
|
trampoline::{Cont, Trampoline},
|
|
|
|
value::{Closure, Ref, Vec2},
|
2024-08-10 23:10:03 +02:00
|
|
|
vm::{Exception, Vm, VmImage, VmLimits},
|
|
|
|
};
|
2024-08-15 20:01:23 +02:00
|
|
|
use log::{debug, info};
|
2024-08-10 23:10:03 +02:00
|
|
|
|
|
|
|
pub mod logging;
|
2024-08-23 19:45:07 +02:00
|
|
|
#[cfg(not(feature = "std"))]
|
2024-08-10 23:10:03 +02:00
|
|
|
mod panicking;
|
|
|
|
|
|
|
|
#[global_allocator]
|
|
|
|
static ALLOCATOR: dlmalloc::GlobalDlmalloc = dlmalloc::GlobalDlmalloc;
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
unsafe extern "C" fn haku_alloc(size: usize, align: usize) -> *mut u8 {
|
|
|
|
alloc::alloc::alloc(Layout::from_size_align(size, align).unwrap())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
unsafe extern "C" fn haku_free(ptr: *mut u8, size: usize, align: usize) {
|
|
|
|
alloc::alloc::dealloc(ptr, Layout::from_size_align(size, align).unwrap())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
struct Limits {
|
2024-08-22 20:27:25 +02:00
|
|
|
max_source_code_len: usize,
|
2024-08-10 23:10:03 +02:00
|
|
|
max_chunks: usize,
|
|
|
|
max_defs: usize,
|
2024-09-08 13:53:29 +02:00
|
|
|
max_tags: usize,
|
2024-08-27 20:43:14 +02:00
|
|
|
max_tokens: usize,
|
|
|
|
max_parser_events: usize,
|
2024-08-10 23:10:03 +02:00
|
|
|
ast_capacity: usize,
|
|
|
|
chunk_capacity: usize,
|
|
|
|
stack_capacity: usize,
|
|
|
|
call_stack_capacity: usize,
|
|
|
|
ref_capacity: usize,
|
|
|
|
fuel: usize,
|
2024-08-20 23:00:39 +02:00
|
|
|
memory: usize,
|
2024-08-10 23:13:20 +02:00
|
|
|
pixmap_stack_capacity: usize,
|
2024-08-10 23:10:03 +02:00
|
|
|
transform_stack_capacity: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Limits {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
2024-08-22 20:27:25 +02:00
|
|
|
max_source_code_len: 65536,
|
2024-08-10 23:10:03 +02:00
|
|
|
max_chunks: 2,
|
|
|
|
max_defs: 256,
|
2024-09-08 13:53:29 +02:00
|
|
|
max_tags: 256,
|
2024-08-27 20:43:14 +02:00
|
|
|
max_tokens: 1024,
|
|
|
|
max_parser_events: 1024,
|
2024-08-10 23:10:03 +02:00
|
|
|
ast_capacity: 1024,
|
|
|
|
chunk_capacity: 65536,
|
|
|
|
stack_capacity: 1024,
|
|
|
|
call_stack_capacity: 256,
|
|
|
|
ref_capacity: 2048,
|
|
|
|
fuel: 65536,
|
2024-08-20 23:00:39 +02:00
|
|
|
memory: 1024 * 1024,
|
2024-08-10 23:13:20 +02:00
|
|
|
pixmap_stack_capacity: 4,
|
2024-08-10 23:10:03 +02:00
|
|
|
transform_stack_capacity: 16,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-15 20:01:23 +02:00
|
|
|
#[no_mangle]
|
|
|
|
extern "C" fn haku_limits_new() -> *mut Limits {
|
2024-08-22 20:49:24 +02:00
|
|
|
let ptr = Box::leak(Box::new(Limits::default())) as *mut _;
|
|
|
|
debug!("created limits: {ptr:?}");
|
|
|
|
ptr
|
2024-08-15 20:01:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
unsafe extern "C" fn haku_limits_destroy(limits: *mut Limits) {
|
2024-08-22 20:49:24 +02:00
|
|
|
debug!("destroying limits: {limits:?}");
|
2024-08-15 20:01:23 +02:00
|
|
|
drop(Box::from_raw(limits))
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! limit_setter {
|
|
|
|
($name:tt) => {
|
|
|
|
paste::paste! {
|
|
|
|
#[no_mangle]
|
|
|
|
unsafe extern "C" fn [<haku_limits_set_ $name>](limits: *mut Limits, value: usize) {
|
|
|
|
debug!("set limit {} = {value}", stringify!($name));
|
|
|
|
|
|
|
|
let limits = &mut *limits;
|
|
|
|
limits.$name = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-08-22 20:27:25 +02:00
|
|
|
limit_setter!(max_source_code_len);
|
2024-08-15 20:01:23 +02:00
|
|
|
limit_setter!(max_chunks);
|
|
|
|
limit_setter!(max_defs);
|
2024-09-08 13:53:29 +02:00
|
|
|
limit_setter!(max_tags);
|
2024-08-27 20:43:14 +02:00
|
|
|
limit_setter!(max_tokens);
|
|
|
|
limit_setter!(max_parser_events);
|
2024-08-15 20:01:23 +02:00
|
|
|
limit_setter!(ast_capacity);
|
|
|
|
limit_setter!(chunk_capacity);
|
|
|
|
limit_setter!(stack_capacity);
|
|
|
|
limit_setter!(call_stack_capacity);
|
|
|
|
limit_setter!(ref_capacity);
|
|
|
|
limit_setter!(fuel);
|
2024-08-20 23:00:39 +02:00
|
|
|
limit_setter!(memory);
|
2024-08-15 20:01:23 +02:00
|
|
|
limit_setter!(pixmap_stack_capacity);
|
|
|
|
limit_setter!(transform_stack_capacity);
|
|
|
|
|
2024-08-10 23:10:03 +02:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
struct Instance {
|
|
|
|
limits: Limits,
|
|
|
|
|
|
|
|
system: System,
|
|
|
|
system_image: SystemImage,
|
|
|
|
defs: Defs,
|
|
|
|
defs_image: DefsImage,
|
|
|
|
vm: Vm,
|
|
|
|
vm_image: VmImage,
|
2024-08-15 20:01:23 +02:00
|
|
|
|
2024-09-08 13:53:29 +02:00
|
|
|
trampoline: Option<Trampoline>,
|
2024-08-10 23:10:03 +02:00
|
|
|
exception: Option<Exception>,
|
|
|
|
}
|
|
|
|
|
2024-09-08 13:53:29 +02:00
|
|
|
impl Instance {
|
|
|
|
fn set_exception(&mut self, exn: Exception) {
|
|
|
|
debug!("setting exception = {exn:?}");
|
|
|
|
self.exception = Some(exn);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn reset_exception(&mut self) {
|
|
|
|
debug!("resetting exception");
|
|
|
|
self.exception = None;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-10 23:10:03 +02:00
|
|
|
#[no_mangle]
|
2024-08-15 20:01:23 +02:00
|
|
|
unsafe extern "C" fn haku_instance_new(limits: *const Limits) -> *mut Instance {
|
|
|
|
let limits = *limits;
|
|
|
|
debug!("creating new instance with limits: {limits:?}");
|
|
|
|
|
2024-08-10 23:10:03 +02:00
|
|
|
let system = System::new(limits.max_chunks);
|
|
|
|
|
2024-09-08 13:53:29 +02:00
|
|
|
let defs = Defs::new(&DefsLimits {
|
|
|
|
max_defs: limits.max_defs,
|
|
|
|
max_tags: limits.max_tags,
|
|
|
|
});
|
2024-08-10 23:10:03 +02:00
|
|
|
let vm = Vm::new(
|
|
|
|
&defs,
|
|
|
|
&VmLimits {
|
|
|
|
stack_capacity: limits.stack_capacity,
|
|
|
|
call_stack_capacity: limits.call_stack_capacity,
|
|
|
|
ref_capacity: limits.ref_capacity,
|
|
|
|
fuel: limits.fuel,
|
2024-08-20 23:00:39 +02:00
|
|
|
memory: limits.memory,
|
2024-08-10 23:10:03 +02:00
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
let system_image = system.image();
|
|
|
|
let defs_image = defs.image();
|
|
|
|
let vm_image = vm.image();
|
|
|
|
|
|
|
|
let instance = Box::new(Instance {
|
|
|
|
limits,
|
|
|
|
system,
|
|
|
|
system_image,
|
|
|
|
defs,
|
|
|
|
defs_image,
|
|
|
|
vm,
|
|
|
|
vm_image,
|
2024-09-08 13:53:29 +02:00
|
|
|
trampoline: None,
|
2024-08-10 23:10:03 +02:00
|
|
|
exception: None,
|
|
|
|
});
|
2024-08-22 20:49:24 +02:00
|
|
|
|
|
|
|
let ptr = Box::leak(instance) as *mut _;
|
|
|
|
debug!("created instance: {ptr:?}");
|
|
|
|
ptr
|
2024-08-10 23:10:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
unsafe extern "C" fn haku_instance_destroy(instance: *mut Instance) {
|
2024-08-22 20:49:24 +02:00
|
|
|
debug!("destroying instance: {instance:?}");
|
2024-08-10 23:10:03 +02:00
|
|
|
drop(Box::from_raw(instance));
|
|
|
|
}
|
|
|
|
|
2024-08-10 23:13:20 +02:00
|
|
|
#[no_mangle]
|
|
|
|
unsafe extern "C" fn haku_reset(instance: *mut Instance) {
|
2024-08-24 18:04:18 +02:00
|
|
|
debug!("resetting instance: {instance:?}");
|
2024-08-10 23:13:20 +02:00
|
|
|
let instance = &mut *instance;
|
|
|
|
instance.system.restore_image(&instance.system_image);
|
|
|
|
instance.defs.restore_image(&instance.defs_image);
|
|
|
|
}
|
|
|
|
|
2024-08-10 23:10:03 +02:00
|
|
|
#[no_mangle]
|
|
|
|
unsafe extern "C" fn haku_has_exception(instance: *mut Instance) -> bool {
|
|
|
|
(*instance).exception.is_some()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
unsafe extern "C" fn haku_exception_message(instance: *const Instance) -> *const u8 {
|
|
|
|
(*instance).exception.as_ref().unwrap().message.as_ptr()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
unsafe extern "C" fn haku_exception_message_len(instance: *const Instance) -> u32 {
|
|
|
|
(*instance).exception.as_ref().unwrap().message.len() as u32
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
#[repr(C)]
|
|
|
|
enum StatusCode {
|
|
|
|
Ok,
|
2024-08-22 20:27:25 +02:00
|
|
|
SourceCodeTooLong,
|
2024-08-27 20:43:14 +02:00
|
|
|
TooManyTokens,
|
|
|
|
TooManyAstNodes,
|
2024-09-02 20:40:59 +02:00
|
|
|
TooManyParserEvents,
|
2024-09-01 09:35:26 +02:00
|
|
|
ParserUnbalancedEvents,
|
2024-08-10 23:10:03 +02:00
|
|
|
ChunkTooBig,
|
|
|
|
DiagnosticsEmitted,
|
|
|
|
TooManyChunks,
|
|
|
|
OutOfRefSlots,
|
|
|
|
EvalException,
|
|
|
|
RenderException,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
extern "C" fn haku_is_ok(code: StatusCode) -> bool {
|
|
|
|
code == StatusCode::Ok
|
|
|
|
}
|
|
|
|
|
2024-08-10 23:13:20 +02:00
|
|
|
#[no_mangle]
|
|
|
|
extern "C" fn haku_is_diagnostics_emitted(code: StatusCode) -> bool {
|
|
|
|
code == StatusCode::DiagnosticsEmitted
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
extern "C" fn haku_is_exception(code: StatusCode) -> bool {
|
|
|
|
matches!(
|
|
|
|
code,
|
|
|
|
StatusCode::EvalException | StatusCode::RenderException
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-08-10 23:10:03 +02:00
|
|
|
#[no_mangle]
|
|
|
|
extern "C" fn haku_status_string(code: StatusCode) -> *const i8 {
|
|
|
|
match code {
|
|
|
|
StatusCode::Ok => c"ok",
|
2024-08-22 20:27:25 +02:00
|
|
|
StatusCode::SourceCodeTooLong => c"source code is too long",
|
2024-08-27 20:43:14 +02:00
|
|
|
StatusCode::TooManyTokens => c"source code has too many tokens",
|
|
|
|
StatusCode::TooManyAstNodes => c"source code has too many AST nodes",
|
2024-09-02 20:40:59 +02:00
|
|
|
StatusCode::TooManyParserEvents => c"source code has too many parser events",
|
2024-09-01 09:35:26 +02:00
|
|
|
StatusCode::ParserUnbalancedEvents => c"parser produced unbalanced events",
|
2024-08-10 23:10:03 +02:00
|
|
|
StatusCode::ChunkTooBig => c"compiled bytecode is too large",
|
|
|
|
StatusCode::DiagnosticsEmitted => c"diagnostics were emitted",
|
|
|
|
StatusCode::TooManyChunks => c"too many registered bytecode chunks",
|
|
|
|
StatusCode::OutOfRefSlots => c"out of ref slots (did you forget to restore the VM image?)",
|
|
|
|
StatusCode::EvalException => c"an exception occurred while evaluating your code",
|
|
|
|
StatusCode::RenderException => c"an exception occurred while rendering your brush",
|
|
|
|
}
|
|
|
|
.as_ptr()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Default)]
|
|
|
|
enum BrushState {
|
|
|
|
#[default]
|
|
|
|
Default,
|
2024-09-01 09:35:26 +02:00
|
|
|
Ready(ChunkId, ClosureSpec),
|
2024-08-10 23:10:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Default)]
|
|
|
|
struct Brush {
|
|
|
|
diagnostics: Vec<Diagnostic>,
|
|
|
|
state: BrushState,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
extern "C" fn haku_brush_new() -> *mut Brush {
|
2024-08-22 20:49:24 +02:00
|
|
|
let ptr = Box::leak(Box::new(Brush::default())) as *mut _;
|
|
|
|
debug!("created brush: {ptr:?}");
|
|
|
|
ptr
|
2024-08-10 23:10:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
unsafe extern "C" fn haku_brush_destroy(brush: *mut Brush) {
|
2024-08-22 20:49:24 +02:00
|
|
|
debug!("destroying brush: {brush:?}");
|
2024-08-10 23:10:03 +02:00
|
|
|
drop(Box::from_raw(brush))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
unsafe extern "C" fn haku_num_diagnostics(brush: *const Brush) -> u32 {
|
|
|
|
(*brush).diagnostics.len() as u32
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
unsafe extern "C" fn haku_diagnostic_start(brush: *const Brush, index: u32) -> u32 {
|
2024-08-27 20:43:14 +02:00
|
|
|
(*brush).diagnostics[index as usize].span().start
|
2024-08-10 23:10:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
unsafe extern "C" fn haku_diagnostic_end(brush: *const Brush, index: u32) -> u32 {
|
2024-08-27 20:43:14 +02:00
|
|
|
(*brush).diagnostics[index as usize].span().end
|
2024-08-10 23:10:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
unsafe extern "C" fn haku_diagnostic_message(brush: *const Brush, index: u32) -> *const u8 {
|
2024-08-27 20:43:14 +02:00
|
|
|
(*brush).diagnostics[index as usize].message().as_ptr()
|
2024-08-10 23:10:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
unsafe extern "C" fn haku_diagnostic_message_len(brush: *const Brush, index: u32) -> u32 {
|
2024-08-27 20:43:14 +02:00
|
|
|
(*brush).diagnostics[index as usize].message().len() as u32
|
2024-08-10 23:10:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
unsafe extern "C" fn haku_compile_brush(
|
|
|
|
instance: *mut Instance,
|
|
|
|
out_brush: *mut Brush,
|
|
|
|
code_len: u32,
|
|
|
|
code: *const u8,
|
|
|
|
) -> StatusCode {
|
|
|
|
info!("compiling brush");
|
|
|
|
|
|
|
|
let instance = &mut *instance;
|
|
|
|
let brush = &mut *out_brush;
|
|
|
|
|
|
|
|
*brush = Brush::default();
|
|
|
|
|
|
|
|
let code = core::str::from_utf8(slice::from_raw_parts(code, code_len as usize))
|
|
|
|
.expect("invalid UTF-8");
|
2024-08-27 20:43:14 +02:00
|
|
|
let Some(code) = SourceCode::limited_len(code, instance.limits.max_source_code_len as u32)
|
|
|
|
else {
|
|
|
|
return StatusCode::SourceCodeTooLong;
|
2024-08-22 20:27:25 +02:00
|
|
|
};
|
2024-08-10 23:10:03 +02:00
|
|
|
|
2024-09-02 20:14:20 +02:00
|
|
|
debug!("compiling: lexing");
|
|
|
|
|
2024-08-27 20:43:14 +02:00
|
|
|
let mut lexer = Lexer::new(Lexis::new(instance.limits.max_tokens), code);
|
|
|
|
if lex(&mut lexer).is_err() {
|
2024-09-02 20:14:20 +02:00
|
|
|
info!("compiling failed: too many tokens");
|
2024-08-27 20:43:14 +02:00
|
|
|
return StatusCode::TooManyTokens;
|
|
|
|
};
|
|
|
|
|
2024-09-02 20:14:20 +02:00
|
|
|
debug!(
|
|
|
|
"compiling: lexed successfully to {} tokens",
|
|
|
|
lexer.lexis.len()
|
|
|
|
);
|
|
|
|
debug!("compiling: parsing");
|
|
|
|
|
2024-08-27 20:43:14 +02:00
|
|
|
let mut ast = Ast::new(instance.limits.ast_capacity);
|
|
|
|
let mut parser = Parser::new(
|
|
|
|
&lexer.lexis,
|
|
|
|
&haku::parser::ParserLimits {
|
|
|
|
max_events: instance.limits.max_parser_events,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
parser::toplevel(&mut parser);
|
2024-09-01 09:35:26 +02:00
|
|
|
let (root, mut parser_diagnostics) = match parser.into_ast(&mut ast) {
|
|
|
|
Ok((r, d)) => (r, d),
|
2024-09-02 20:14:20 +02:00
|
|
|
Err(IntoAstError::NodeAlloc(_)) => {
|
|
|
|
info!("compiling failed: too many AST nodes");
|
|
|
|
return StatusCode::TooManyAstNodes;
|
|
|
|
}
|
2024-09-02 20:40:59 +02:00
|
|
|
Err(IntoAstError::TooManyEvents) => {
|
|
|
|
info!("compiling failed: too many parser events");
|
|
|
|
return StatusCode::TooManyParserEvents;
|
|
|
|
}
|
2024-09-02 20:14:20 +02:00
|
|
|
Err(IntoAstError::UnbalancedEvents) => {
|
|
|
|
info!("compiling failed: parser produced unbalanced events");
|
|
|
|
return StatusCode::ParserUnbalancedEvents;
|
|
|
|
}
|
2024-08-27 20:43:14 +02:00
|
|
|
};
|
2024-08-10 23:10:03 +02:00
|
|
|
|
2024-09-02 20:14:20 +02:00
|
|
|
debug!(
|
|
|
|
"compiling: parsed successfully into {} AST nodes",
|
|
|
|
ast.len()
|
|
|
|
);
|
|
|
|
|
2024-08-10 23:10:03 +02:00
|
|
|
let src = Source {
|
|
|
|
code,
|
|
|
|
ast: &ast,
|
|
|
|
system: &instance.system,
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut chunk = Chunk::new(instance.limits.chunk_capacity).unwrap();
|
|
|
|
let mut compiler = Compiler::new(&mut instance.defs, &mut chunk);
|
|
|
|
if let Err(error) = compile_expr(&mut compiler, &src, root) {
|
|
|
|
match error {
|
2024-09-02 20:14:20 +02:00
|
|
|
CompileError::Emit => {
|
|
|
|
info!("compiling failed: chunk overflowed while emitting code");
|
|
|
|
return StatusCode::ChunkTooBig;
|
|
|
|
}
|
2024-08-10 23:10:03 +02:00
|
|
|
}
|
|
|
|
}
|
2024-09-01 09:35:26 +02:00
|
|
|
let closure_spec = compiler.closure_spec();
|
2024-08-10 23:10:03 +02:00
|
|
|
|
2024-08-27 20:43:14 +02:00
|
|
|
let mut diagnostics = lexer.diagnostics;
|
|
|
|
diagnostics.append(&mut parser_diagnostics);
|
|
|
|
diagnostics.append(&mut compiler.diagnostics);
|
|
|
|
if !diagnostics.is_empty() {
|
|
|
|
brush.diagnostics = diagnostics;
|
2024-09-02 20:14:20 +02:00
|
|
|
debug!("compiling failed: diagnostics were emitted");
|
2024-08-10 23:10:03 +02:00
|
|
|
return StatusCode::DiagnosticsEmitted;
|
|
|
|
}
|
|
|
|
|
2024-09-02 20:14:20 +02:00
|
|
|
debug!(
|
|
|
|
"compiling: chunk has {} bytes of bytecode",
|
|
|
|
chunk.bytecode.len()
|
|
|
|
);
|
|
|
|
debug!("compiling: {closure_spec:?}");
|
|
|
|
|
2024-08-10 23:10:03 +02:00
|
|
|
let chunk_id = match instance.system.add_chunk(chunk) {
|
|
|
|
Ok(chunk_id) => chunk_id,
|
|
|
|
Err(_) => return StatusCode::TooManyChunks,
|
|
|
|
};
|
2024-09-01 09:35:26 +02:00
|
|
|
brush.state = BrushState::Ready(chunk_id, closure_spec);
|
2024-08-10 23:10:03 +02:00
|
|
|
|
|
|
|
info!("brush compiled into {chunk_id:?}");
|
|
|
|
|
|
|
|
StatusCode::Ok
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
2024-09-08 13:53:29 +02:00
|
|
|
extern "C" fn haku_pixmap_new(width: u32, height: u32) -> *mut Pixmap {
|
|
|
|
let ptr = Box::leak(Box::new(
|
|
|
|
Pixmap::new(width, height).expect("invalid pixmap size"),
|
|
|
|
)) as *mut _;
|
2024-08-22 20:49:24 +02:00
|
|
|
debug!("created pixmap with size {width}x{height}: {ptr:?}");
|
|
|
|
ptr
|
2024-08-10 23:10:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
2024-09-08 13:53:29 +02:00
|
|
|
unsafe extern "C" fn haku_pixmap_destroy(pixmap: *mut Pixmap) {
|
2024-08-22 20:49:24 +02:00
|
|
|
debug!("destroying pixmap: {pixmap:?}");
|
2024-08-10 23:13:20 +02:00
|
|
|
drop(Box::from_raw(pixmap))
|
2024-08-10 23:10:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
2024-09-08 13:53:29 +02:00
|
|
|
unsafe extern "C" fn haku_pixmap_data(pixmap: *mut Pixmap) -> *mut u8 {
|
|
|
|
let pixmap = &mut *pixmap;
|
2024-08-10 23:13:20 +02:00
|
|
|
pixmap.pixels_mut().as_mut_ptr() as *mut u8
|
|
|
|
}
|
2024-08-10 23:10:03 +02:00
|
|
|
|
2024-08-10 23:13:20 +02:00
|
|
|
#[no_mangle]
|
2024-09-08 13:53:29 +02:00
|
|
|
unsafe extern "C" fn haku_pixmap_clear(pixmap: *mut Pixmap) {
|
|
|
|
let pixmap = &mut *pixmap;
|
2024-08-10 23:13:20 +02:00
|
|
|
pixmap.pixels_mut().fill(PremultipliedColorU8::TRANSPARENT);
|
2024-08-10 23:10:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
2024-09-08 13:53:29 +02:00
|
|
|
unsafe extern "C" fn haku_begin_brush(instance: *mut Instance, brush: *const Brush) -> StatusCode {
|
2024-08-10 23:10:03 +02:00
|
|
|
let instance = &mut *instance;
|
|
|
|
let brush = &*brush;
|
|
|
|
|
2024-09-01 09:35:26 +02:00
|
|
|
let BrushState::Ready(chunk_id, closure_spec) = brush.state else {
|
2024-08-10 23:10:03 +02:00
|
|
|
panic!("brush is not compiled and ready to be used");
|
|
|
|
};
|
|
|
|
|
2024-09-08 13:53:29 +02:00
|
|
|
instance.vm.restore_image(&instance.vm_image);
|
2024-08-24 18:04:18 +02:00
|
|
|
instance.vm.apply_defs(&instance.defs);
|
2024-09-08 13:53:29 +02:00
|
|
|
instance.reset_exception();
|
|
|
|
instance.trampoline = None;
|
2024-08-24 18:04:18 +02:00
|
|
|
|
2024-09-01 09:35:26 +02:00
|
|
|
let Ok(closure_id) = instance
|
|
|
|
.vm
|
|
|
|
.create_ref(Ref::Closure(Closure::chunk(chunk_id, closure_spec)))
|
|
|
|
else {
|
2024-08-10 23:10:03 +02:00
|
|
|
return StatusCode::OutOfRefSlots;
|
|
|
|
};
|
|
|
|
|
2024-09-08 13:53:29 +02:00
|
|
|
instance.reset_exception();
|
|
|
|
let value = match instance.vm.run(&instance.system, closure_id, &[]) {
|
2024-08-10 23:10:03 +02:00
|
|
|
Ok(value) => value,
|
|
|
|
Err(exn) => {
|
2024-09-08 13:53:29 +02:00
|
|
|
instance.set_exception(exn);
|
2024-08-10 23:10:03 +02:00
|
|
|
return StatusCode::EvalException;
|
|
|
|
}
|
|
|
|
};
|
2024-09-08 13:53:29 +02:00
|
|
|
instance.trampoline = Some(Trampoline::new(value));
|
2024-08-10 23:10:03 +02:00
|
|
|
|
2024-08-15 20:01:23 +02:00
|
|
|
StatusCode::Ok
|
|
|
|
}
|
2024-08-10 23:13:20 +02:00
|
|
|
|
2024-08-15 20:01:23 +02:00
|
|
|
#[no_mangle]
|
2024-09-08 13:53:29 +02:00
|
|
|
unsafe extern "C" fn haku_cont_kind(instance: *mut Instance) -> Cont {
|
|
|
|
let instance = &mut *instance;
|
|
|
|
instance.trampoline.as_ref().unwrap().cont(&instance.vm)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn wrap_exception(
|
|
|
|
instance: &mut Instance,
|
|
|
|
error_code: StatusCode,
|
|
|
|
f: impl FnOnce(&mut Instance) -> Result<(), Exception>,
|
|
|
|
) -> StatusCode {
|
|
|
|
match f(instance) {
|
|
|
|
Ok(_) => StatusCode::Ok,
|
|
|
|
Err(exn) => {
|
|
|
|
instance.set_exception(exn);
|
|
|
|
error_code
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
unsafe extern "C" fn haku_cont_scribble(
|
2024-08-15 20:01:23 +02:00
|
|
|
instance: *mut Instance,
|
2024-09-08 13:53:29 +02:00
|
|
|
pixmap: *mut Pixmap,
|
2024-08-15 20:01:23 +02:00
|
|
|
translation_x: f32,
|
|
|
|
translation_y: f32,
|
|
|
|
) -> StatusCode {
|
|
|
|
let instance = &mut *instance;
|
2024-09-08 13:53:29 +02:00
|
|
|
instance.reset_exception();
|
|
|
|
|
|
|
|
debug!("cont_scribble: pixmap={pixmap:?} translation_x={translation_x:?} translation_y={translation_y:?} trampoline={:?}", instance.trampoline);
|
|
|
|
|
|
|
|
wrap_exception(instance, StatusCode::RenderException, |instance| {
|
|
|
|
instance.trampoline.as_mut().unwrap().scribble(
|
|
|
|
&instance.vm,
|
|
|
|
&mut *pixmap,
|
|
|
|
Vec2 {
|
|
|
|
x: translation_x,
|
|
|
|
y: translation_y,
|
|
|
|
},
|
|
|
|
&RendererLimits {
|
|
|
|
pixmap_stack_capacity: instance.limits.pixmap_stack_capacity,
|
|
|
|
transform_stack_capacity: instance.limits.transform_stack_capacity,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
2024-09-01 18:54:38 +02:00
|
|
|
|
2024-09-08 13:53:29 +02:00
|
|
|
#[no_mangle]
|
|
|
|
unsafe extern "C" fn haku_cont_dotter(
|
|
|
|
instance: *mut Instance,
|
|
|
|
from_x: f32,
|
|
|
|
from_y: f32,
|
|
|
|
to_x: f32,
|
|
|
|
to_y: f32,
|
|
|
|
num: f32,
|
|
|
|
) -> StatusCode {
|
|
|
|
let instance = &mut *instance;
|
|
|
|
instance.reset_exception();
|
2024-08-10 23:13:20 +02:00
|
|
|
|
2024-09-08 13:53:29 +02:00
|
|
|
debug!(
|
|
|
|
"cont_dotter: from_x={from_x} from_y={from_y} to_x={to_x} to_y={to_y} trampoline={:?}",
|
|
|
|
instance.trampoline
|
2024-08-15 20:01:23 +02:00
|
|
|
);
|
2024-08-10 23:10:03 +02:00
|
|
|
|
2024-09-08 13:53:29 +02:00
|
|
|
wrap_exception(instance, StatusCode::RenderException, |instance| {
|
|
|
|
instance.trampoline.as_mut().unwrap().dotter(
|
|
|
|
&mut instance.vm,
|
|
|
|
&instance.system,
|
|
|
|
Vec2::new(from_x, from_y),
|
|
|
|
Vec2::new(to_x, to_y),
|
|
|
|
num,
|
|
|
|
)
|
|
|
|
})
|
2024-08-10 23:10:03 +02:00
|
|
|
}
|