byteflow/bytecode/chunk.rs
1use super::instruction::Instruction;
2use super::value::Value;
3
4/// On-disk magic for the `.bf` module format (see design notes §32).
5/// Chosen so a corrupted/truncated file is rejected in the first 4 bytes
6/// rather than partway through decoding.
7pub const MAGIC: [u8; 4] = *b"BFV0";
8
9/// Current ABI version. Bump on any breaking change to instruction
10/// encoding, constant representation, or function-table layout.
11///
12/// v2: [`Value::Message`] wire tag `5`.
13/// v3: `Message.reply_cap`; [`Value::Cap`] wire tag `6` (FlowCap).
14/// v4: [`Value::Str`] tag `7`, [`Value::Bytes`] tag `8`.
15pub const ABI_VERSION: u32 = 4;
16
17/// A callable entry point inside a [`Chunk`]: either bytecode-defined or a
18/// slot reserved for a native (Rust) function registered with the runtime
19/// via the FFI table (design notes §30-31).
20#[derive(Clone, Debug, PartialEq)]
21pub struct FunctionDef {
22 pub name: String,
23 /// Index into `Chunk::code` where execution starts.
24 pub entry: u32,
25 /// Number of parameters, passed in registers `r0..r{arity}`.
26 pub arity: u8,
27 /// Upper bound on registers this function uses; the VM allocates
28 /// exactly this many per call frame instead of a fixed worst-case size.
29 pub num_registers: u8,
30}
31
32/// A compiled unit of Byteflow bytecode: code, constants and the function
33/// table. One `Chunk` can back many concurrently-running processes — it is
34/// immutable after construction, so it is shared behind an `Arc` rather than
35/// copied per Flow (see `byteflow-vm::Vm::chunk`).
36#[derive(Clone, Debug, Default)]
37pub struct Chunk {
38 pub name: String,
39 pub constants: Vec<Value>,
40 pub code: Vec<Instruction>,
41 pub functions: Vec<FunctionDef>,
42}
43
44impl Chunk {
45 pub fn function(&self, index: u32) -> Option<&FunctionDef> {
46 self.functions.get(index as usize)
47 }
48
49 pub fn constant(&self, index: u32) -> Option<&Value> {
50 self.constants.get(index as usize)
51 }
52
53 /// Number of instructions, used by the verifier to bound-check jump
54 /// targets ahead of time instead of on every branch at runtime.
55 pub fn len(&self) -> usize {
56 self.code.len()
57 }
58
59 pub fn is_empty(&self) -> bool {
60 self.code.is_empty()
61 }
62}