hotwire haku2 into rkgk
really a bodge job right now and it crashes but it's a start
This commit is contained in:
parent
550227da34
commit
5de4f9d7c6
9 changed files with 178 additions and 71 deletions
|
@ -9,7 +9,11 @@ const Scratch = @import("scratch.zig");
|
|||
const value = @import("value.zig");
|
||||
const Vm = @import("vm.zig");
|
||||
|
||||
const allocator = if (builtin.cpu.arch == .wasm32) std.heap.wasm_allocator else @import("allocator.zig").hostAllocator;
|
||||
const allocator =
|
||||
if (builtin.cpu.arch == .wasm32)
|
||||
std.heap.wasm_allocator
|
||||
else
|
||||
@import("allocator.zig").hostAllocator;
|
||||
|
||||
// Scratch
|
||||
|
||||
|
@ -28,7 +32,9 @@ export fn haku2_scratch_reset(scratch: *Scratch) void {
|
|||
// Limits
|
||||
|
||||
export fn haku2_limits_new() ?*Vm.Limits {
|
||||
return allocator.create(Vm.Limits) catch null;
|
||||
const limits = allocator.create(Vm.Limits) catch return null;
|
||||
limits.* = .{};
|
||||
return limits;
|
||||
}
|
||||
|
||||
export fn haku2_limits_destroy(limits: *Vm.Limits) void {
|
||||
|
@ -102,9 +108,15 @@ export fn haku2_vm_run_main(
|
|||
return true;
|
||||
}
|
||||
|
||||
export fn haku2_vm_has_cont(vm: *const Vm) bool {
|
||||
if (vm.stack.len == 0) return false;
|
||||
const top = vm.top();
|
||||
return top == .ref and top.ref.* == .reticle;
|
||||
}
|
||||
|
||||
export fn haku2_vm_is_dotter(vm: *const Vm) bool {
|
||||
if (vm.stack.len == 0) return false;
|
||||
const top = vm.stack[vm.stack_top];
|
||||
const top = vm.top();
|
||||
return top == .ref and top.ref.* == .reticle and top.ref.reticle == .dotter;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::{
|
|||
error::Error,
|
||||
fmt::{self, Display},
|
||||
marker::{PhantomData, PhantomPinned},
|
||||
mem::forget,
|
||||
ptr::{self, NonNull},
|
||||
};
|
||||
|
||||
|
@ -91,6 +92,7 @@ extern "C" {
|
|||
code_len: usize,
|
||||
local_count: u8,
|
||||
) -> bool;
|
||||
fn haku2_vm_has_cont(vm: *const VmC) -> bool;
|
||||
fn haku2_vm_is_dotter(vm: *const VmC) -> bool;
|
||||
fn haku2_vm_run_dotter(
|
||||
vm: *mut VmC,
|
||||
|
@ -211,10 +213,38 @@ impl Drop for Defs {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Code {
|
||||
defs: Defs,
|
||||
main_chunk: Vec<u8>,
|
||||
main_local_count: u8,
|
||||
}
|
||||
|
||||
impl Code {
|
||||
/// Creates a new instance of `Code` from a valid vector of bytes.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This does not perform any validation, and there is no way to perform such
|
||||
/// validation before constructing this. The bytecode must simply be valid, which is the case
|
||||
/// for bytecode emitted directly by the compiler.
|
||||
///
|
||||
/// Untrusted bytecode should never ever be loaded under any circumstances.
|
||||
pub unsafe fn new(defs: Defs, main_chunk: Vec<u8>, main_local_count: u8) -> Self {
|
||||
Self {
|
||||
defs,
|
||||
main_chunk,
|
||||
main_local_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A VM that is ready to run and loaded with valid bytecode.
|
||||
#[derive(Debug)]
|
||||
pub struct Vm {
|
||||
scratch: Scratch,
|
||||
raw: NonNull<VmC>,
|
||||
code: Code,
|
||||
inner: VmInner,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -236,17 +266,24 @@ pub struct Dotter {
|
|||
}
|
||||
|
||||
impl Vm {
|
||||
pub fn new(scratch: Scratch, defs: &Defs, limits: &Limits) -> Self {
|
||||
pub fn new(scratch: Scratch, code: Code, limits: &Limits) -> Self {
|
||||
Self {
|
||||
// SAFETY:
|
||||
// - Ownership of s is passed to the VM, so the VM cannot outlive the scratch space.
|
||||
// - Ownership of scratch is passed to the VM, so the VM cannot outlive the scratch space.
|
||||
// - The VM never gives you any references back, so this is safe to do.
|
||||
// - The other arguments are only borrowed immutably for construction.
|
||||
raw: NonNull::new(unsafe {
|
||||
haku2_vm_new(scratch.raw.as_ptr(), defs.raw.as_ptr(), limits.raw.as_ptr())
|
||||
})
|
||||
.expect("out of memory"),
|
||||
inner: VmInner {
|
||||
raw: NonNull::new(unsafe {
|
||||
haku2_vm_new(
|
||||
scratch.raw.as_ptr(),
|
||||
code.defs.raw.as_ptr(),
|
||||
limits.raw.as_ptr(),
|
||||
)
|
||||
})
|
||||
.expect("out of memory"),
|
||||
},
|
||||
scratch,
|
||||
code,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,19 +292,14 @@ impl Vm {
|
|||
///
|
||||
/// Calling `begin` again during this process will work correctly, and result in another
|
||||
/// continuation being stack on top of the old one---at the expense of a stack slot.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The bytecode passed in must be valid, because bytecode validation is done on a best-effort
|
||||
/// basis. Bytecode retrieved out of the compiler is guaranteed to be safe.
|
||||
pub unsafe fn begin(&mut self, code: &[u8], local_count: u8) -> Result<(), Exception> {
|
||||
pub fn begin(&mut self) -> Result<(), Exception> {
|
||||
let ok = unsafe {
|
||||
haku2_vm_run_main(
|
||||
self.raw.as_ptr(),
|
||||
self.inner.raw.as_ptr(),
|
||||
self.scratch.raw.as_ptr(),
|
||||
code.as_ptr(),
|
||||
code.len(),
|
||||
local_count,
|
||||
self.code.main_chunk.as_ptr(),
|
||||
self.code.main_chunk.len(),
|
||||
self.code.main_local_count,
|
||||
)
|
||||
};
|
||||
if ok {
|
||||
|
@ -277,9 +309,14 @@ impl Vm {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns whether `cont()` can be called to run the next continuation.
|
||||
pub fn has_cont(&self) -> bool {
|
||||
unsafe { haku2_vm_has_cont(self.inner.raw.as_ptr()) }
|
||||
}
|
||||
|
||||
fn is_dotter(&self) -> bool {
|
||||
// SAFETY: The pointer is valid.
|
||||
unsafe { haku2_vm_is_dotter(self.raw.as_ptr()) }
|
||||
unsafe { haku2_vm_is_dotter(self.inner.raw.as_ptr()) }
|
||||
}
|
||||
|
||||
/// Returns how the VM should continue executing after the previous execution.
|
||||
|
@ -291,12 +328,12 @@ impl Vm {
|
|||
}
|
||||
|
||||
/// Renders the current scribble on top of the stack.
|
||||
/// If the value on top is not a scribble, throws an exception (indicated by the return type.)
|
||||
/// If the value on top is not a scribble, throws an exception.
|
||||
///
|
||||
/// The rendering is performed by calling into the [`Canvas`] trait.
|
||||
pub fn render(&mut self, canvas: &mut dyn Canvas, max_depth: usize) -> Result<(), Exception> {
|
||||
let mut wrapped = CanvasC { inner: canvas };
|
||||
let ok = unsafe { haku2_render(self.raw.as_ptr(), &mut wrapped, max_depth) };
|
||||
let ok = unsafe { haku2_render(self.inner.raw.as_ptr(), &mut wrapped, max_depth) };
|
||||
if ok {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -308,7 +345,7 @@ impl Vm {
|
|||
/// Returns `None` if there's no exception.
|
||||
pub fn exception(&self) -> Option<Exception> {
|
||||
// SAFETY: The pointer passed to this function is valid.
|
||||
let len = unsafe { haku2_vm_exception_len(self.raw.as_ptr()) };
|
||||
let len = unsafe { haku2_vm_exception_len(self.inner.raw.as_ptr()) };
|
||||
if len == 0 {
|
||||
return None;
|
||||
}
|
||||
|
@ -316,12 +353,24 @@ impl Vm {
|
|||
let mut buffer = vec![0; len];
|
||||
// SAFETY: The length of the buffer is as indicated by haku2_vm_exception_len.
|
||||
unsafe {
|
||||
haku2_vm_exception_render(self.raw.as_ptr(), buffer.as_mut_ptr());
|
||||
haku2_vm_exception_render(self.inner.raw.as_ptr(), buffer.as_mut_ptr());
|
||||
}
|
||||
Some(Exception {
|
||||
message: String::from_utf8_lossy(&buffer).into_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Take the `Scratch` out of the VM for reuse in another one.
|
||||
/// The scratch memory will be reset (no bytes will be consumed.)
|
||||
pub fn into_scratch(self) -> Scratch {
|
||||
let Vm {
|
||||
mut scratch,
|
||||
code: _,
|
||||
inner: _,
|
||||
} = self;
|
||||
scratch.reset();
|
||||
scratch
|
||||
}
|
||||
}
|
||||
|
||||
impl ContDotter<'_> {
|
||||
|
@ -334,7 +383,7 @@ impl ContDotter<'_> {
|
|||
|
||||
let ok = unsafe {
|
||||
haku2_vm_run_dotter(
|
||||
self.vm.raw.as_ptr(),
|
||||
self.vm.inner.raw.as_ptr(),
|
||||
self.vm.scratch.raw.as_ptr(),
|
||||
from_x,
|
||||
from_y,
|
||||
|
@ -351,7 +400,12 @@ impl ContDotter<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Drop for Vm {
|
||||
#[derive(Debug)]
|
||||
struct VmInner {
|
||||
raw: NonNull<VmC>,
|
||||
}
|
||||
|
||||
impl Drop for VmInner {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: The pointer passed is non-null.
|
||||
unsafe {
|
||||
|
|
|
@ -54,6 +54,6 @@ fn renderRec(vm: *Vm, canvas: *Canvas, val: Value, depth: usize, max_depth: usiz
|
|||
}
|
||||
|
||||
pub fn render(vm: *Vm, canvas: *Canvas, max_depth: usize) !void {
|
||||
const val = try vm.pop();
|
||||
const val = vm.stack[vm.stack_top - 1];
|
||||
try renderRec(vm, canvas, val, 0, max_depth);
|
||||
}
|
||||
|
|
|
@ -113,6 +113,14 @@ pub fn pop(vm: *Vm) Error!Value {
|
|||
return vm.stack[vm.stack_top];
|
||||
}
|
||||
|
||||
pub fn top(vm: *const Vm) Value {
|
||||
if (vm.stack_top > 0) {
|
||||
return vm.stack[vm.stack_top - 1];
|
||||
} else {
|
||||
return .nil;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pushCall(vm: *Vm, frame: CallFrame) Error!void {
|
||||
if (vm.call_stack_top >= vm.call_stack.len) {
|
||||
return vm.throw("too much recursion", .{});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue