bytecode_lang/lib.rs
1//! # bytecode_lang
2//!
3//! LSB, the LexerSketch bytecode format: one register-machine instruction
4//! set for every language and every execution tier, its module model, a
5//! versioned binary encoding, a budgeted decoder, a deterministic
6//! disassembler, and a builder that resolves branch labels.
7//!
8//! The VM (bvm-lang 2.0) executes LSB, code generators (codegen-lang 2.0)
9//! emit it, program images (loader-lang) store it, and the debugger
10//! (dap-lang) reads its line tables. This crate owns the format only; it
11//! executes nothing.
12//!
13//! ## Quick start
14//!
15//! Build a function, encode the module, decode it back, and read the listing:
16//!
17//! ```
18//! use bytecode_lang::{decode, disassemble, encode, Inst, IntOp, IntTy, ModuleBuilder, ValType};
19//!
20//! let mut m = ModuleBuilder::new();
21//! let mut f = m.function("add", &[ValType::I64, ValType::I64], &[ValType::I64]);
22//! let (a, b) = (f.param(0), f.param(1));
23//! let sum = f.reg(ValType::I64);
24//! f.emit(Inst::IAdd { dst: sum, lhs: a, rhs: b, op: IntOp::new(IntTy::I64) });
25//! f.ret(sum);
26//! m.add_function(f)?;
27//! let module = m.finish()?;
28//!
29//! let bytes = encode(&module);
30//! let back = decode(&bytes)?;
31//! assert_eq!(back, module);
32//! assert!(disassemble(&back).contains("iadd.i64 r2, r0, r1"));
33//! # Ok::<(), Box<dyn std::error::Error>>(())
34//! ```
35//!
36//! ## The model
37//!
38//! - **Registers, typed.** Every function has a frame of 64-bit registers,
39//! each with a declared [`ValType`]: unboxed scalars for static languages,
40//! [`ValType::Dyn`] for dynamic values, and references. Declared types make
41//! GC roots exact and let the verifier check every operand.
42//! - **Eight-byte instructions.** [`Inst`] is a `Copy` enum of eight bytes;
43//! a function body is a flat `&[Inst]` that an interpreter walks without
44//! re-parsing. Branch targets are resolved instruction indices.
45//! - **Policies on every operation.** Integer instructions carry an
46//! [`IntOp`]: the operand type plus the complete OPS policy set (overflow,
47//! division by zero, shift range, float-to-int), so every tier computes the
48//! same result or raises the same [`ErrorKind`].
49//! - **Dynamic languages built in.** `dyn` arithmetic and comparisons with a
50//! numeric fast path, ordered hash maps, property access, iteration, and
51//! per-module [`Hook`]s that let a language supply its own semantics for
52//! the slow path. `overflow = promote` gives PHP's int-to-float arithmetic.
53//! - **Coroutines.** Stackful coroutines (`coro_new`, `yield`, `await`,
54//! `resume`, `spawn`) for generators, fibers, and async tasks; see
55//! [`CoroState`].
56//!
57//! The normative description of every instruction is `specs/LSB.md` in the
58//! LexerSketch plan; `docs/API.md` documents every public item.
59//!
60//! ## Guarantees
61//!
62//! - [`decode`] never panics on any input, never recurses, and allocates no
63//! more than its [`Limits`] and the input length justify.
64//! - `decode(&encode(&m)) == Ok(m)` for every module, and
65//! `encode(&decode(bytes)?) == bytes` for every accepted input: each module
66//! has exactly one encoding.
67//! - [`encode`] and [`disassemble`] are deterministic.
68//! - A function produced by [`ModuleBuilder::add_function`] has no unresolved
69//! or out-of-range branch target.
70//!
71//! What this release does **not** do: verify a module's meaning (index
72//! ranges, register types, frame shapes). That is the v0.5 verifier, whose
73//! rules are specified in `specs/LSB.md` ยง8. Until then a decoded module is
74//! well-formed but unverified.
75
76#![cfg_attr(not(feature = "std"), no_std)]
77#![cfg_attr(docsrs, feature(doc_cfg))]
78#![forbid(unsafe_code)]
79#![deny(missing_docs)]
80#![deny(unused_must_use)]
81#![deny(unused_results)]
82#![deny(clippy::unwrap_used)]
83#![deny(clippy::expect_used)]
84#![deny(clippy::todo)]
85#![deny(clippy::unimplemented)]
86#![deny(clippy::print_stdout)]
87#![deny(clippy::print_stderr)]
88#![deny(clippy::dbg_macro)]
89
90extern crate alloc;
91
92#[macro_use]
93mod macros;
94
95mod builder;
96mod decode;
97mod disasm;
98mod encode;
99mod ids;
100mod inst;
101mod module;
102mod policy;
103mod types;
104
105use alloc::string::{String, ToString};
106use alloc::vec::Vec;
107
108pub use builder::{BuildError, FunctionBuilder, Label, ModuleBuilder};
109pub use decode::{DecodeError, DecodeErrorKind, Limit, Limits};
110pub use ids::{
111 ConstId, FieldIdx, FuncId, GlobalId, ImportId, NameRef, Reg, StrId, TableId, Target, TypeId,
112 TypeRef, UpvalIdx,
113};
114pub use inst::{FieldKind, FieldSpec, Inst, InstError, Opcode, Slot};
115pub use module::{
116 Callee, Const, ErrorKind, Export, ExportItem, Function, Global, Handler, Hook, HookBinding,
117 Import, JumpTable, LineRow, LocalVar, Module,
118};
119pub use policy::{
120 DivZero, FloatToInt, FloatTy, IntConv, IntOp, IntPair, IntTy, Overflow, Policy, Shift,
121};
122pub use types::{CoroState, Field, FuncType, Kind, Method, Prim, StructDef, TypeDef, ValType};
123
124/// The four bytes every encoded module starts with: `LSB\0`.
125///
126/// # Examples
127///
128/// ```
129/// use bytecode_lang::{encode, ModuleBuilder, MAGIC};
130///
131/// let bytes = encode(&ModuleBuilder::new().finish().unwrap());
132/// assert_eq!(&bytes[..4], &MAGIC);
133/// ```
134pub const MAGIC: [u8; 4] = *b"LSB\0";
135
136/// The format version this crate writes and the only one it reads.
137///
138/// The version changes whenever the encoding or any instruction's meaning
139/// changes; [`decode`] refuses every other version rather than guess.
140///
141/// # Examples
142///
143/// ```
144/// use bytecode_lang::{encode, ModuleBuilder, FORMAT_VERSION};
145///
146/// let bytes = encode(&ModuleBuilder::new().finish().unwrap());
147/// assert_eq!(bytes[4..8], FORMAT_VERSION.to_le_bytes());
148/// ```
149pub const FORMAT_VERSION: u32 = 1;
150
151/// Encodes a module into its canonical bytes.
152///
153/// Infallible and deterministic: a [`Module`] can only be made by
154/// [`ModuleBuilder::finish`] or [`decode`], both of which guarantee every
155/// section fits its 32-bit length.
156///
157/// # Examples
158///
159/// ```
160/// use bytecode_lang::{encode, ModuleBuilder};
161///
162/// let module = ModuleBuilder::new().finish().unwrap();
163/// let bytes = encode(&module);
164/// assert_eq!(bytes, encode(&module));
165/// // A 12-byte header, ten 8-byte section headers, and the empty sections'
166/// // counts and option flags.
167/// assert_eq!(bytes.len(), 130);
168/// ```
169#[must_use]
170pub fn encode(module: &Module) -> Vec<u8> {
171 encode::encode(module)
172}
173
174/// Decodes a module with the default [`Limits`].
175///
176/// # Errors
177///
178/// A [`DecodeError`] with the byte offset and the reason when the input is
179/// not a well-formed module of this format version.
180///
181/// # Examples
182///
183/// ```
184/// use bytecode_lang::{decode, encode, DecodeErrorKind, ModuleBuilder};
185///
186/// let module = ModuleBuilder::new().finish().unwrap();
187/// let bytes = encode(&module);
188/// assert_eq!(decode(&bytes).unwrap(), module);
189///
190/// let truncated = &bytes[..bytes.len() - 1];
191/// assert_eq!(decode(truncated).unwrap_err().kind(), &DecodeErrorKind::UnexpectedEnd);
192/// ```
193pub fn decode(bytes: &[u8]) -> Result<Module, DecodeError> {
194 decode::decode(bytes, &Limits::default())
195}
196
197/// Decodes a module with explicit budgets.
198///
199/// # Errors
200///
201/// As [`decode`], plus [`DecodeErrorKind::LimitExceeded`] when the input
202/// exceeds one of `limits`.
203///
204/// # Examples
205///
206/// ```
207/// use bytecode_lang::{decode_with, encode, DecodeErrorKind, Limit, Limits, ModuleBuilder};
208///
209/// let bytes = encode(&ModuleBuilder::new().finish().unwrap());
210/// let mut tight = Limits::default();
211/// tight.max_bytes = 16;
212/// assert_eq!(
213/// decode_with(&bytes, &tight).unwrap_err().kind(),
214/// &DecodeErrorKind::LimitExceeded(Limit::Bytes),
215/// );
216/// ```
217pub fn decode_with(bytes: &[u8], limits: &Limits) -> Result<Module, DecodeError> {
218 decode::decode(bytes, limits)
219}
220
221/// Compiles and runs the `rust` code blocks in `README.md` and `docs/API.md` as
222/// part of `cargo test`, so the published examples cannot drift from the API.
223///
224/// Present only while collecting doctests (`#[cfg(doctest)]`); it is not part of
225/// the public surface and does not appear in the built library or its docs.
226#[cfg(doctest)]
227#[doc = include_str!("../README.md")]
228#[doc = include_str!("../docs/API.md")]
229pub struct MarkdownDocTests;
230
231/// The module's disassembly: a deterministic text listing of every table
232/// and every function, with branch targets as labels and names resolved in
233/// comments.
234///
235/// Never panics, including on unverified modules with out-of-range
236/// references (they print as `<invalid>`).
237///
238/// # Examples
239///
240/// ```
241/// use bytecode_lang::{disassemble, ModuleBuilder};
242///
243/// let mut m = ModuleBuilder::new();
244/// let mut f = m.function("main", &[], &[]);
245/// f.ret_void();
246/// m.add_function(f).unwrap();
247/// let text = disassemble(&m.finish().unwrap());
248/// assert!(text.starts_with("lsb 1\n"));
249/// assert!(text.contains("func f0 s0 \"main\" : t0"));
250/// assert!(text.contains(" 0000 ret_void"));
251/// ```
252#[must_use]
253pub fn disassemble(module: &Module) -> String {
254 module.to_string()
255}