Skip to main content

bytecode_lang/
module.rs

1//! The module model: everything one LSB unit contains.
2
3use alloc::string::String;
4use alloc::vec::Vec;
5use core::fmt;
6
7use crate::call::{CallShape, ParamList};
8use crate::ids::{ConstId, FuncId, GlobalId, ImportId, Reg, StrId, Target, TypeId};
9use crate::inst::Inst;
10use crate::types::{TypeDef, ValType};
11
12/// An entry of the module's constant pool.
13///
14/// Constants are exact and independent of any runtime value layout: a
15/// 64-bit integer is a 64-bit integer, floats are kept as IEEE bit patterns
16/// (so NaN payloads and `-0.0` survive encoding bit for bit), and strings
17/// are referenced from the string table. A VM converts a constant into its
18/// own representation when it loads the module (see
19/// [`Inst::LoadConst`](crate::Inst::LoadConst) and
20/// [`Inst::DLoadConst`](crate::Inst::DLoadConst)).
21///
22/// Aggregate constants ([`Array`](Const::Array), [`Map`](Const::Map)) refer
23/// only to **earlier** constants, so the pool is a DAG by construction and
24/// its depth is a cheap, enforced decoding budget.
25///
26/// # Examples
27///
28/// ```
29/// use bytecode_lang::Const;
30///
31/// let half = Const::f64(0.5);
32/// assert_eq!(half, Const::F64(0.5f64.to_bits()));
33/// assert_eq!(half.to_string(), "f64 0.5");
34/// assert_eq!(Const::Int(-3).to_string(), "int -3");
35/// ```
36#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
37pub enum Const {
38    /// A boolean.
39    Bool(bool),
40    /// A signed integer, loadable into any integer type it fits.
41    Int(i64),
42    /// An unsigned integer, for `u64` values above `i64::MAX`.
43    UInt(u64),
44    /// A binary32 float, as its IEEE bit pattern.
45    F32(u32),
46    /// A binary64 float, as its IEEE bit pattern.
47    F64(u64),
48    /// A Unicode scalar value.
49    Char(char),
50    /// A UTF-8 string from the string table, loaded as a `str`.
51    Str(StrId),
52    /// An arbitrary byte string, loaded as a `str` (PHP strings are bytes).
53    Bytes(Vec<u8>),
54    /// An immutable array of earlier constants.
55    Array(Vec<ConstId>),
56    /// An immutable insertion-ordered map of earlier constants.
57    Map(Vec<(ConstId, ConstId)>),
58}
59
60impl Const {
61    /// A binary32 constant from its value.
62    ///
63    /// # Examples
64    ///
65    /// ```
66    /// use bytecode_lang::Const;
67    ///
68    /// assert_eq!(Const::f32(1.0), Const::F32(0x3f80_0000));
69    /// ```
70    #[must_use]
71    pub fn f32(value: f32) -> Self {
72        Const::F32(value.to_bits())
73    }
74
75    /// A binary64 constant from its value.
76    ///
77    /// # Examples
78    ///
79    /// ```
80    /// use bytecode_lang::Const;
81    ///
82    /// assert_eq!(Const::f64(-0.0), Const::F64(0x8000_0000_0000_0000));
83    /// ```
84    #[must_use]
85    pub fn f64(value: f64) -> Self {
86        Const::F64(value.to_bits())
87    }
88
89    /// The encoding tag.
90    pub(crate) const fn tag(&self) -> u8 {
91        match self {
92            Const::Bool(_) => 0,
93            Const::Int(_) => 1,
94            Const::UInt(_) => 2,
95            Const::F32(_) => 3,
96            Const::F64(_) => 4,
97            Const::Char(_) => 5,
98            Const::Str(_) => 6,
99            Const::Bytes(_) => 7,
100            Const::Array(_) => 8,
101            Const::Map(_) => 9,
102        }
103    }
104
105    /// The constants this one refers to, in order.
106    pub(crate) fn children(&self) -> impl Iterator<Item = ConstId> + '_ {
107        let (list, pairs): (&[ConstId], &[(ConstId, ConstId)]) = match self {
108            Const::Array(items) => (items, &[]),
109            Const::Map(entries) => (&[], entries),
110            _ => (&[], &[]),
111        };
112        list.iter()
113            .copied()
114            .chain(pairs.iter().flat_map(|&(k, v)| [k, v]))
115    }
116}
117
118/// Floats print with Rust's shortest round-trip form; NaNs print their bit
119/// pattern, since the value alone would lose the payload.
120impl fmt::Display for Const {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        match self {
123            Const::Bool(v) => write!(f, "bool {v}"),
124            Const::Int(v) => write!(f, "int {v}"),
125            Const::UInt(v) => write!(f, "uint {v}"),
126            Const::F32(bits) => {
127                let v = f32::from_bits(*bits);
128                if v.is_nan() {
129                    write!(f, "f32 nan:{bits:#010x}")
130                } else {
131                    write!(f, "f32 {v:?}")
132                }
133            }
134            Const::F64(bits) => {
135                let v = f64::from_bits(*bits);
136                if v.is_nan() {
137                    write!(f, "f64 nan:{bits:#018x}")
138                } else {
139                    write!(f, "f64 {v:?}")
140                }
141            }
142            Const::Char(c) => write!(f, "char {c:?}"),
143            Const::Str(id) => write!(f, "str {id}"),
144            Const::Bytes(bytes) => {
145                f.write_str("bytes b\"")?;
146                for &b in bytes {
147                    write!(f, "{}", core::ascii::escape_default(b))?;
148                }
149                f.write_str("\"")
150            }
151            Const::Array(items) => {
152                f.write_str("array [")?;
153                for (i, id) in items.iter().enumerate() {
154                    if i > 0 {
155                        f.write_str(", ")?;
156                    }
157                    write!(f, "{id}")?;
158                }
159                f.write_str("]")
160            }
161            Const::Map(entries) => {
162                f.write_str("map {")?;
163                for (i, (k, v)) in entries.iter().enumerate() {
164                    if i > 0 {
165                        f.write_str(", ")?;
166                    }
167                    write!(f, "{k}: {v}")?;
168                }
169                f.write_str("}")
170            }
171        }
172    }
173}
174
175/// A host function the module calls by index
176/// ([`Inst::CallImport`](crate::Inst::CallImport)), bound by the loader by
177/// `module` and `name`.
178///
179/// An import is also a first-class value: [`Inst::LoadImport`](crate::Inst::LoadImport)
180/// makes a function value of it that every dynamic call form accepts. With a
181/// [`ParamList`], dynamic calls of it take named, spread, extra, and missing
182/// arguments as for a bytecode function (`specs/LSB.md` §5.15); the host
183/// receives the bound parameters in signature order, presence mask included.
184///
185/// # Examples
186///
187/// ```
188/// use bytecode_lang::{Import, StrId, TypeId};
189///
190/// let print = Import { module: StrId(0), name: StrId(1), sig: TypeId(2), params: None };
191/// assert_eq!(print.sig, TypeId(2));
192/// ```
193#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
194pub struct Import {
195    /// The providing module's name (for example `ls.io`).
196    pub module: StrId,
197    /// The function's name within that module.
198    pub name: StrId,
199    /// The function's signature (a [`TypeDef::Func`] entry).
200    pub sig: TypeId,
201    /// The dynamic-call signature, if the host function takes named,
202    /// variadic, optional, or by-reference arguments; `None` means exact
203    /// positional arity.
204    pub params: Option<ParamList>,
205}
206
207/// A module-level variable.
208///
209/// # Examples
210///
211/// ```
212/// use bytecode_lang::{ConstId, Global, StrId, ValType};
213///
214/// let counter = Global { name: StrId(0), ty: ValType::I64, mutable: true, init: Some(ConstId(0)) };
215/// assert!(counter.mutable);
216/// ```
217#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
218pub struct Global {
219    /// The global's name.
220    pub name: StrId,
221    /// The global's type.
222    pub ty: ValType,
223    /// Whether [`Inst::SetGlobal`](crate::Inst::SetGlobal) may write it.
224    pub mutable: bool,
225    /// The initial value; `None` means the type's default (zero, `false`,
226    /// U+0000, or `nil`).
227    pub init: Option<ConstId>,
228}
229
230/// What an [`Export`] makes visible to other units.
231///
232/// # Examples
233///
234/// ```
235/// use bytecode_lang::{ExportItem, FuncId};
236///
237/// assert_eq!(ExportItem::Func(FuncId(0)).to_string(), "f0");
238/// ```
239#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
240pub enum ExportItem {
241    /// A function.
242    Func(FuncId),
243    /// A global.
244    Global(GlobalId),
245    /// A type.
246    Type(TypeId),
247}
248
249impl ExportItem {
250    pub(crate) const fn tag(self) -> u8 {
251        match self {
252            ExportItem::Func(_) => 0,
253            ExportItem::Global(_) => 1,
254            ExportItem::Type(_) => 2,
255        }
256    }
257
258    pub(crate) const fn index(self) -> u32 {
259        match self {
260            ExportItem::Func(id) => id.0,
261            ExportItem::Global(id) => id.0,
262            ExportItem::Type(id) => id.0,
263        }
264    }
265}
266
267impl fmt::Display for ExportItem {
268    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269        match self {
270            ExportItem::Func(id) => write!(f, "{id}"),
271            ExportItem::Global(id) => write!(f, "{id}"),
272            ExportItem::Type(id) => write!(f, "{id}"),
273        }
274    }
275}
276
277/// A named item the module exports: the shared module interface the
278/// loader links native and bytecode units through.
279///
280/// # Examples
281///
282/// ```
283/// use bytecode_lang::{Export, ExportItem, FuncId, StrId};
284///
285/// let main = Export { name: StrId(0), item: ExportItem::Func(FuncId(0)) };
286/// assert_eq!(main.item, ExportItem::Func(FuncId(0)));
287/// ```
288#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
289pub struct Export {
290    /// The exported name.
291    pub name: StrId,
292    /// The exported item.
293    pub item: ExportItem,
294}
295
296code_enum! {
297    /// A dynamic-operation hook: the function a dynamic instruction calls when
298    /// its built-in fast path does not apply (for example `dadd` on two
299    /// strings, or `get_prop` on an object with no such field).
300    ///
301    /// Hooks are how a dynamic language supplies its own semantics (PHP's
302    /// loose `==`, Python's list equality) without a separate instruction
303    /// set per language. A missing hook makes the operation raise
304    /// `TypeError` (E0100) or the error the instruction documents.
305    ///
306    /// # Examples
307    ///
308    /// ```
309    /// use bytecode_lang::Hook;
310    ///
311    /// assert_eq!(Hook::Add.code(), 0);
312    /// assert_eq!(Hook::GetProp.to_string(), "get_prop");
313    /// ```
314    Hook {
315        /// `dadd` fallback: `(dyn, dyn) -> dyn`.
316        Add = 0 => "add",
317        /// `dsub` fallback: `(dyn, dyn) -> dyn`.
318        Sub = 1 => "sub",
319        /// `dmul` fallback: `(dyn, dyn) -> dyn`.
320        Mul = 2 => "mul",
321        /// `ddiv` fallback: `(dyn, dyn) -> dyn`.
322        Div = 3 => "div",
323        /// `drem` fallback: `(dyn, dyn) -> dyn`.
324        Rem = 4 => "rem",
325        /// `dfloor_div` fallback: `(dyn, dyn) -> dyn`.
326        FloorDiv = 5 => "floor_div",
327        /// `dfloor_mod` fallback: `(dyn, dyn) -> dyn`.
328        FloorMod = 6 => "floor_mod",
329        /// `dand` fallback: `(dyn, dyn) -> dyn`.
330        BitAnd = 7 => "bit_and",
331        /// `dor` fallback: `(dyn, dyn) -> dyn`.
332        BitOr = 8 => "bit_or",
333        /// `dxor` fallback: `(dyn, dyn) -> dyn`.
334        BitXor = 9 => "bit_xor",
335        /// `dshl` fallback: `(dyn, dyn) -> dyn`.
336        Shl = 10 => "shl",
337        /// `dshr` fallback: `(dyn, dyn) -> dyn`.
338        Shr = 11 => "shr",
339        /// `dneg` fallback: `(dyn) -> dyn`.
340        Neg = 12 => "neg",
341        /// `dnot` fallback: `(dyn) -> dyn`.
342        BitNot = 13 => "bit_not",
343        /// `deq`/`dne` fallback: `(dyn, dyn) -> dyn` returning a boolean.
344        Eq = 14 => "eq",
345        /// `dlt`/`dgt` fallback: `(dyn, dyn) -> dyn` returning a boolean.
346        Lt = 15 => "lt",
347        /// `dle`/`dge` fallback: `(dyn, dyn) -> dyn` returning a boolean.
348        Le = 16 => "le",
349        /// `dtruthy` for kinds other than nil, bool, int, float: `(dyn) -> dyn`.
350        Truthy = 17 => "truthy",
351        /// `dconcat` fallback: `(dyn, dyn) -> dyn`.
352        Concat = 18 => "concat",
353        /// `dget_index` fallback: `(dyn, dyn) -> dyn`.
354        GetIndex = 19 => "get_index",
355        /// `dset_index` fallback: `(dyn, dyn, dyn) -> ()`.
356        SetIndex = 20 => "set_index",
357        /// `get_prop` fallback: `(dyn, dyn) -> dyn` (object, name string).
358        GetProp = 21 => "get_prop",
359        /// `set_prop` fallback: `(dyn, dyn, dyn) -> ()`.
360        SetProp = 22 => "set_prop",
361        /// `has_prop` fallback: `(dyn, dyn) -> dyn` returning a boolean.
362        HasProp = 23 => "has_prop",
363        /// `diter_new` fallback: `(dyn) -> dyn` returning something iterable.
364        Iter = 24 => "iter",
365        /// `dlen` fallback: `(dyn) -> dyn` returning an integer.
366        Len = 25 => "len",
367        /// `dcall` on a non-callable: `(dyn, dyn) -> dyn` (callee, argument array).
368        Call = 26 => "call",
369        /// `spawn`: hands a new coroutine to the host scheduler
370        /// (host-lang): `(dyn) -> dyn` (coroutine, task handle).
371        Spawn = 27 => "spawn",
372        /// `dpow` fallback: `(dyn, dyn) -> dyn`.
373        Pow = 28 => "pow",
374        /// `dabs` fallback: `(dyn) -> dyn`.
375        Abs = 29 => "abs",
376        /// `dcall_shape` on a non-callable: `(dyn, dyn, dyn) -> dyn` (callee,
377        /// positional arguments as an array, named arguments as a map from
378        /// name to value). Without it, a call with no named arguments falls
379        /// back to the `call` hook.
380        CallShape = 30 => "call_shape",
381    }
382}
383
384/// A function the module can call: one of its own, or an import.
385///
386/// # Examples
387///
388/// ```
389/// use bytecode_lang::{Callee, ImportId};
390///
391/// assert_eq!(Callee::Import(ImportId(3)).to_string(), "imp3");
392/// ```
393#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
394pub enum Callee {
395    /// A function of this module.
396    Func(FuncId),
397    /// A host function.
398    Import(ImportId),
399}
400
401impl fmt::Display for Callee {
402    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
403        match self {
404            Callee::Func(id) => write!(f, "{id}"),
405            Callee::Import(id) => write!(f, "{id}"),
406        }
407    }
408}
409
410/// One bound [`Hook`]. A module binds each hook at most once; bindings are
411/// kept sorted by hook code.
412///
413/// # Examples
414///
415/// ```
416/// use bytecode_lang::{Callee, FuncId, Hook, HookBinding};
417///
418/// let b = HookBinding { hook: Hook::Concat, callee: Callee::Func(FuncId(1)) };
419/// assert_eq!(b.hook, Hook::Concat);
420/// ```
421#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
422pub struct HookBinding {
423    /// The hook.
424    pub hook: Hook,
425    /// The function that implements it.
426    pub callee: Callee,
427}
428
429/// A jump table of [`Inst::Switch`](crate::Inst::Switch): `targets[v]` for a
430/// selector `v` in range, `default` otherwise.
431///
432/// # Examples
433///
434/// ```
435/// use bytecode_lang::{JumpTable, Target};
436///
437/// let t = JumpTable { targets: vec![Target(4), Target(9)], default: Target(12) };
438/// assert_eq!(t.targets.len(), 2);
439/// ```
440#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
441pub struct JumpTable {
442    /// Targets for selectors `0..len`.
443    pub targets: Vec<Target>,
444    /// The target for every other selector.
445    pub default: Target,
446}
447
448/// An exception-handling region: an error raised by an instruction at a pc
449/// in `start..end` transfers control to `target` with the error value in
450/// `catch`.
451///
452/// A function's handlers are searched in order and the first that covers
453/// the faulting pc wins, so inner regions are listed before the regions
454/// that enclose them.
455///
456/// # Examples
457///
458/// ```
459/// use bytecode_lang::{Handler, Reg, Target};
460///
461/// let h = Handler { start: 0, end: 5, target: Target(7), catch: Reg(3) };
462/// assert!(h.start < h.end);
463/// ```
464#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
465pub struct Handler {
466    /// First covered pc.
467    pub start: u32,
468    /// One past the last covered pc.
469    pub end: u32,
470    /// Where control continues when an error is caught.
471    pub target: Target,
472    /// The `dyn` register that receives the error value.
473    pub catch: Reg,
474}
475
476/// A row of a function's line table: instructions from `pc` up to the next
477/// row's `pc` came from this source position.
478///
479/// # Examples
480///
481/// ```
482/// use bytecode_lang::{LineRow, StrId};
483///
484/// let row = LineRow { pc: 0, file: StrId(2), line: 10, column: 5 };
485/// assert_eq!(row.line, 10);
486/// ```
487#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
488pub struct LineRow {
489    /// The first instruction the row covers.
490    pub pc: u32,
491    /// The source file's name.
492    pub file: StrId,
493    /// The 1-based line.
494    pub line: u32,
495    /// The 1-based column.
496    pub column: u32,
497}
498
499/// A named source variable living in a register over a pc range, for
500/// debuggers.
501///
502/// # Examples
503///
504/// ```
505/// use bytecode_lang::{LocalVar, Reg, StrId};
506///
507/// let x = LocalVar { reg: Reg(0), name: StrId(1), start: 0, end: 9 };
508/// assert_eq!(x.reg, Reg(0));
509/// ```
510#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
511pub struct LocalVar {
512    /// The register holding the variable.
513    pub reg: Reg,
514    /// The variable's source name.
515    pub name: StrId,
516    /// First pc where the variable is live.
517    pub start: u32,
518    /// One past the last pc where it is live.
519    pub end: u32,
520}
521
522/// One function of a module: its frame layout, its tables, its code, and
523/// its debug information.
524///
525/// Built with [`FunctionBuilder`](crate::FunctionBuilder) or produced by
526/// [`decode`](crate::decode); read-only afterwards.
527///
528/// # Examples
529///
530/// ```
531/// use bytecode_lang::{Inst, ModuleBuilder, ValType};
532///
533/// let mut m = ModuleBuilder::new();
534/// let mut f = m.function("id", &[ValType::I64], &[ValType::I64]);
535/// let x = f.param(0);
536/// f.ret(x);
537/// let id = m.add_function(f).unwrap();
538/// let module = m.finish().unwrap();
539///
540/// let func = module.function(id).unwrap();
541/// assert_eq!(func.regs(), &[ValType::I64]);
542/// assert_eq!(func.code(), &[Inst::Ret { src: x }]);
543/// ```
544#[derive(Clone, PartialEq, Eq, Debug)]
545pub struct Function {
546    pub(crate) name: StrId,
547    pub(crate) sig: TypeId,
548    pub(crate) params: Option<ParamList>,
549    pub(crate) regs: Vec<ValType>,
550    pub(crate) captures: Vec<ValType>,
551    pub(crate) names: Vec<StrId>,
552    pub(crate) type_refs: Vec<TypeId>,
553    pub(crate) tables: Vec<JumpTable>,
554    pub(crate) shapes: Vec<CallShape>,
555    pub(crate) handlers: Vec<Handler>,
556    pub(crate) code: Vec<Inst>,
557    pub(crate) lines: Vec<LineRow>,
558    pub(crate) locals: Vec<LocalVar>,
559}
560
561impl Function {
562    /// The function's name.
563    ///
564    /// # Examples
565    ///
566    /// ```
567    /// use bytecode_lang::ModuleBuilder;
568    ///
569    /// let mut m = ModuleBuilder::new();
570    /// let mut f = m.function("main", &[], &[]);
571    /// f.ret_void();
572    /// let id = m.add_function(f).unwrap();
573    /// let module = m.finish().unwrap();
574    /// let func = module.function(id).unwrap();
575    /// assert_eq!(module.string(func.name()), Some("main"));
576    /// ```
577    #[must_use]
578    pub fn name(&self) -> StrId {
579        self.name
580    }
581
582    /// The function's signature (a [`TypeDef::Func`] entry).
583    ///
584    /// # Examples
585    ///
586    /// ```
587    /// use bytecode_lang::{FuncType, ModuleBuilder, TypeDef, ValType};
588    ///
589    /// let mut m = ModuleBuilder::new();
590    /// let mut f = m.function("main", &[], &[]);
591    /// f.ret_void();
592    /// let id = m.add_function(f).unwrap();
593    /// let module = m.finish().unwrap();
594    /// let sig = module.function(id).unwrap().sig();
595    /// assert_eq!(module.type_def(sig), Some(&TypeDef::Func(FuncType::default())));
596    /// ```
597    #[must_use]
598    pub fn sig(&self) -> TypeId {
599        self.sig
600    }
601
602    /// The function's dynamic-call signature, if it has one (see
603    /// [`ParamList`]); `None` means dynamic calls must pass exactly its
604    /// parameters, positionally.
605    ///
606    /// # Examples
607    ///
608    /// ```
609    /// use bytecode_lang::{ModuleBuilder, Param, ParamList, ValType};
610    ///
611    /// let mut m = ModuleBuilder::new();
612    /// let x = m.string("x");
613    /// let mut f = m.function("f", &[ValType::Dyn], &[]);
614    /// f.set_params(ParamList::new(vec![Param::normal(x)]));
615    /// f.ret_void();
616    /// let id = m.add_function(f).unwrap();
617    /// let module = m.finish().unwrap();
618    /// assert_eq!(module.function(id).unwrap().params().map(|p| p.params.len()), Some(1));
619    /// ```
620    #[must_use]
621    pub fn params(&self) -> Option<&ParamList> {
622        self.params.as_ref()
623    }
624
625    /// The declared type of every register; the parameters come first.
626    ///
627    /// # Examples
628    ///
629    /// ```
630    /// use bytecode_lang::{ModuleBuilder, ValType};
631    ///
632    /// let mut m = ModuleBuilder::new();
633    /// let mut f = m.function("f", &[ValType::Bool], &[]);
634    /// let _tmp = f.reg(ValType::Dyn);
635    /// f.ret_void();
636    /// let id = m.add_function(f).unwrap();
637    /// let module = m.finish().unwrap();
638    /// assert_eq!(module.function(id).unwrap().regs(), &[ValType::Bool, ValType::Dyn]);
639    /// ```
640    #[must_use]
641    pub fn regs(&self) -> &[ValType] {
642        &self.regs
643    }
644
645    /// The types of the values a closure of this function captures, read
646    /// with [`Inst::GetUpval`](crate::Inst::GetUpval).
647    ///
648    /// # Examples
649    ///
650    /// ```
651    /// use bytecode_lang::{ModuleBuilder, UpvalIdx, ValType};
652    ///
653    /// let mut m = ModuleBuilder::new();
654    /// let mut f = m.function("inner", &[], &[]);
655    /// assert_eq!(f.capture(ValType::Dyn), UpvalIdx(0));
656    /// f.ret_void();
657    /// let id = m.add_function(f).unwrap();
658    /// let module = m.finish().unwrap();
659    /// assert_eq!(module.function(id).unwrap().captures(), &[ValType::Dyn]);
660    /// ```
661    #[must_use]
662    pub fn captures(&self) -> &[ValType] {
663        &self.captures
664    }
665
666    /// The function's name list, indexed by [`NameRef`](crate::NameRef).
667    ///
668    /// # Examples
669    ///
670    /// ```
671    /// use bytecode_lang::ModuleBuilder;
672    ///
673    /// let mut m = ModuleBuilder::new();
674    /// let x = m.string("x");
675    /// let mut f = m.function("f", &[], &[]);
676    /// let n = f.name_ref(x);
677    /// f.ret_void();
678    /// let id = m.add_function(f).unwrap();
679    /// let module = m.finish().unwrap();
680    /// assert_eq!(module.function(id).unwrap().names()[n.index()], x);
681    /// ```
682    #[must_use]
683    pub fn names(&self) -> &[StrId] {
684        &self.names
685    }
686
687    /// The function's type list, indexed by [`TypeRef`](crate::TypeRef).
688    ///
689    /// # Examples
690    ///
691    /// ```
692    /// use bytecode_lang::{ModuleBuilder, TypeDef, ValType};
693    ///
694    /// let mut m = ModuleBuilder::new();
695    /// let arr = m.add_type(TypeDef::Array(ValType::I64));
696    /// let mut f = m.function("f", &[], &[]);
697    /// let t = f.type_ref(arr);
698    /// f.ret_void();
699    /// let id = m.add_function(f).unwrap();
700    /// let module = m.finish().unwrap();
701    /// assert_eq!(module.function(id).unwrap().type_refs()[t.index()], arr);
702    /// ```
703    #[must_use]
704    pub fn type_refs(&self) -> &[TypeId] {
705        &self.type_refs
706    }
707
708    /// The function's jump tables, indexed by [`TableId`](crate::TableId).
709    ///
710    /// # Examples
711    ///
712    /// ```
713    /// use bytecode_lang::{IntTy, ModuleBuilder, Target, ValType};
714    ///
715    /// let mut m = ModuleBuilder::new();
716    /// let mut f = m.function("f", &[ValType::I64], &[]);
717    /// let (a, b) = (f.label(), f.label());
718    /// f.switch(IntTy::I64, f.param(0), &[a], b);
719    /// f.bind(a);
720    /// f.bind(b);
721    /// f.ret_void();
722    /// let id = m.add_function(f).unwrap();
723    /// let module = m.finish().unwrap();
724    /// let table = &module.function(id).unwrap().tables()[0];
725    /// assert_eq!((table.targets.as_slice(), table.default), (&[Target(1)][..], Target(1)));
726    /// ```
727    #[must_use]
728    pub fn tables(&self) -> &[JumpTable] {
729        &self.tables
730    }
731
732    /// The function's call shapes, indexed by [`ShapeId`](crate::ShapeId).
733    ///
734    /// # Examples
735    ///
736    /// ```
737    /// use bytecode_lang::{ArgKind, ModuleBuilder};
738    ///
739    /// let mut m = ModuleBuilder::new();
740    /// let mut f = m.function("f", &[], &[]);
741    /// let s = f.call_shape(&[ArgKind::Positional, ArgKind::Spread]);
742    /// f.ret_void();
743    /// let id = m.add_function(f).unwrap();
744    /// let module = m.finish().unwrap();
745    /// assert_eq!(module.function(id).unwrap().shapes()[s.index()].args.len(), 2);
746    /// ```
747    #[must_use]
748    pub fn shapes(&self) -> &[CallShape] {
749        &self.shapes
750    }
751
752    /// The function's exception-handling regions, innermost first.
753    ///
754    /// # Examples
755    ///
756    /// ```
757    /// use bytecode_lang::{Inst, ModuleBuilder, ValType};
758    ///
759    /// let mut m = ModuleBuilder::new();
760    /// let mut f = m.function("f", &[], &[]);
761    /// let err = f.reg(ValType::Dyn);
762    /// let (start, end, catch) = (f.label(), f.label(), f.label());
763    /// f.bind(start);
764    /// f.emit(Inst::Nop {});
765    /// f.bind(end);
766    /// f.ret_void();
767    /// f.bind(catch);
768    /// f.ret_void();
769    /// f.try_region(start, end, catch, err);
770    /// let id = m.add_function(f).unwrap();
771    /// let module = m.finish().unwrap();
772    /// let h = module.function(id).unwrap().handlers()[0];
773    /// assert_eq!((h.start, h.end, h.target.0), (0, 1, 2));
774    /// ```
775    #[must_use]
776    pub fn handlers(&self) -> &[Handler] {
777        &self.handlers
778    }
779
780    /// The function's instructions; `code()[pc]` is the instruction at `pc`.
781    ///
782    /// # Examples
783    ///
784    /// ```
785    /// use bytecode_lang::{Inst, ModuleBuilder};
786    ///
787    /// let mut m = ModuleBuilder::new();
788    /// let mut f = m.function("f", &[], &[]);
789    /// f.ret_void();
790    /// let id = m.add_function(f).unwrap();
791    /// let module = m.finish().unwrap();
792    /// assert_eq!(module.function(id).unwrap().code(), &[Inst::RetVoid {}]);
793    /// ```
794    #[must_use]
795    pub fn code(&self) -> &[Inst] {
796        &self.code
797    }
798
799    /// The line table, ordered by pc.
800    ///
801    /// # Examples
802    ///
803    /// ```
804    /// use bytecode_lang::ModuleBuilder;
805    ///
806    /// let mut m = ModuleBuilder::new();
807    /// let file = m.string("main.mox");
808    /// let mut f = m.function("f", &[], &[]);
809    /// f.set_location(file, 3, 1);
810    /// f.ret_void();
811    /// let id = m.add_function(f).unwrap();
812    /// let module = m.finish().unwrap();
813    /// assert_eq!(module.function(id).unwrap().lines()[0].line, 3);
814    /// ```
815    #[must_use]
816    pub fn lines(&self) -> &[LineRow] {
817        &self.lines
818    }
819
820    /// The named source variables.
821    ///
822    /// # Examples
823    ///
824    /// ```
825    /// use bytecode_lang::{ModuleBuilder, ValType};
826    ///
827    /// let mut m = ModuleBuilder::new();
828    /// let name = m.string("x");
829    /// let mut f = m.function("f", &[ValType::I64], &[]);
830    /// let (start, end) = (f.label(), f.label());
831    /// f.bind(start);
832    /// f.ret_void();
833    /// f.bind(end);
834    /// f.local(f.param(0), name, start, end);
835    /// let id = m.add_function(f).unwrap();
836    /// let module = m.finish().unwrap();
837    /// assert_eq!(module.function(id).unwrap().locals()[0].end, 1);
838    /// ```
839    #[must_use]
840    pub fn locals(&self) -> &[LocalVar] {
841        &self.locals
842    }
843}
844
845/// The string table: UTF-8 strings stored back to back in one buffer.
846///
847/// One allocation for all strings (instead of one per string) is what keeps
848/// decoding a module with a million names cheap.
849#[derive(Clone, PartialEq, Eq, Debug, Default)]
850pub(crate) struct Strings {
851    data: String,
852    ends: Vec<u32>,
853}
854
855impl Strings {
856    pub(crate) fn with_capacity(count: usize, bytes: usize) -> Self {
857        Strings {
858            data: String::with_capacity(bytes),
859            ends: Vec::with_capacity(count),
860        }
861    }
862
863    pub(crate) fn len(&self) -> usize {
864        self.ends.len()
865    }
866
867    /// Appends `s`; `false` if the buffer would pass `u32::MAX` bytes.
868    pub(crate) fn push(&mut self, s: &str) -> bool {
869        let Some(end) = self
870            .data
871            .len()
872            .checked_add(s.len())
873            .and_then(|end| u32::try_from(end).ok())
874        else {
875            return false;
876        };
877        self.data.push_str(s);
878        self.ends.push(end);
879        true
880    }
881
882    pub(crate) fn get(&self, index: usize) -> Option<&str> {
883        let end = *self.ends.get(index)? as usize;
884        let start = match index.checked_sub(1) {
885            Some(prev) => *self.ends.get(prev)? as usize,
886            None => 0,
887        };
888        self.data.get(start..end)
889    }
890
891    pub(crate) fn iter(&self) -> impl Iterator<Item = &str> + '_ {
892        (0..self.len()).filter_map(|i| self.get(i))
893    }
894}
895
896/// A complete LSB module: the unit that is encoded, decoded, verified,
897/// loaded, and disassembled.
898///
899/// A module holds a string table, a type table, a constant pool, imports,
900/// globals, functions, exports, dynamic-operation hooks, an optional name and
901/// start function, and per-function debug information. It is built with
902/// [`ModuleBuilder`](crate::ModuleBuilder) or produced by
903/// [`decode`](crate::decode), and is read-only afterwards, which is what
904/// lets [`encode`](crate::encode) be infallible. `Display` prints the
905/// [`disassemble`](crate::disassemble) listing.
906///
907/// # Examples
908///
909/// ```
910/// use bytecode_lang::{Inst, IntOp, IntTy, ModuleBuilder, ValType};
911///
912/// let mut m = ModuleBuilder::new();
913/// let mut f = m.function("add", &[ValType::I64, ValType::I64], &[ValType::I64]);
914/// let (a, b) = (f.param(0), f.param(1));
915/// let sum = f.reg(ValType::I64);
916/// f.emit(Inst::IAdd { op: IntOp::new(IntTy::I64), dst: sum, lhs: a, rhs: b });
917/// f.ret(sum);
918/// m.add_function(f).unwrap();
919/// let module = m.finish().unwrap();
920///
921/// assert_eq!(module.functions().len(), 1);
922/// let bytes = bytecode_lang::encode(&module);
923/// assert_eq!(bytecode_lang::decode(&bytes).unwrap(), module);
924/// ```
925#[derive(Clone, PartialEq, Eq, Debug, Default)]
926pub struct Module {
927    pub(crate) strings: Strings,
928    pub(crate) types: Vec<TypeDef>,
929    pub(crate) consts: Vec<Const>,
930    pub(crate) imports: Vec<Import>,
931    pub(crate) globals: Vec<Global>,
932    pub(crate) functions: Vec<Function>,
933    pub(crate) exports: Vec<Export>,
934    pub(crate) hooks: Vec<HookBinding>,
935    pub(crate) name: Option<StrId>,
936    pub(crate) start: Option<FuncId>,
937}
938
939impl Module {
940    /// The string with this id, or `None` if it is out of range.
941    ///
942    /// # Examples
943    ///
944    /// ```
945    /// use bytecode_lang::{ModuleBuilder, StrId};
946    ///
947    /// let mut m = ModuleBuilder::new();
948    /// let hi = m.string("hi");
949    /// let module = m.finish().unwrap();
950    /// assert_eq!(module.string(hi), Some("hi"));
951    /// assert_eq!(module.string(StrId(99)), None);
952    /// ```
953    #[must_use]
954    pub fn string(&self, id: StrId) -> Option<&str> {
955        self.strings.get(id.index())
956    }
957
958    /// The number of strings in the string table.
959    ///
960    /// # Examples
961    ///
962    /// ```
963    /// use bytecode_lang::ModuleBuilder;
964    ///
965    /// let mut m = ModuleBuilder::new();
966    /// m.string("a");
967    /// m.string("a"); // deduplicated
968    /// assert_eq!(m.finish().unwrap().string_count(), 1);
969    /// ```
970    #[must_use]
971    pub fn string_count(&self) -> usize {
972        self.strings.len()
973    }
974
975    /// Every string, in id order.
976    ///
977    /// # Examples
978    ///
979    /// ```
980    /// use bytecode_lang::ModuleBuilder;
981    ///
982    /// let mut m = ModuleBuilder::new();
983    /// m.string("a");
984    /// m.string("b");
985    /// let module = m.finish().unwrap();
986    /// assert_eq!(module.strings().collect::<Vec<_>>(), ["a", "b"]);
987    /// ```
988    pub fn strings(&self) -> impl Iterator<Item = &str> + '_ {
989        self.strings.iter()
990    }
991
992    /// The type table.
993    ///
994    /// # Examples
995    ///
996    /// ```
997    /// use bytecode_lang::{ModuleBuilder, TypeDef, ValType};
998    ///
999    /// let mut m = ModuleBuilder::new();
1000    /// m.add_type(TypeDef::Cell(ValType::Dyn));
1001    /// assert_eq!(m.finish().unwrap().types(), &[TypeDef::Cell(ValType::Dyn)]);
1002    /// ```
1003    #[must_use]
1004    pub fn types(&self) -> &[TypeDef] {
1005        &self.types
1006    }
1007
1008    /// The type with this id, or `None` if it is out of range.
1009    ///
1010    /// # Examples
1011    ///
1012    /// ```
1013    /// use bytecode_lang::{ModuleBuilder, TypeDef, ValType};
1014    ///
1015    /// let mut m = ModuleBuilder::new();
1016    /// let t = m.add_type(TypeDef::Array(ValType::U8));
1017    /// assert_eq!(m.finish().unwrap().type_def(t), Some(&TypeDef::Array(ValType::U8)));
1018    /// ```
1019    #[must_use]
1020    pub fn type_def(&self, id: TypeId) -> Option<&TypeDef> {
1021        self.types.get(id.index())
1022    }
1023
1024    /// The constant pool.
1025    ///
1026    /// # Examples
1027    ///
1028    /// ```
1029    /// use bytecode_lang::{Const, ModuleBuilder};
1030    ///
1031    /// let mut m = ModuleBuilder::new();
1032    /// m.constant(Const::Int(7));
1033    /// assert_eq!(m.finish().unwrap().consts(), &[Const::Int(7)]);
1034    /// ```
1035    #[must_use]
1036    pub fn consts(&self) -> &[Const] {
1037        &self.consts
1038    }
1039
1040    /// The constant with this id, or `None` if it is out of range.
1041    ///
1042    /// # Examples
1043    ///
1044    /// ```
1045    /// use bytecode_lang::{Const, ModuleBuilder};
1046    ///
1047    /// let mut m = ModuleBuilder::new();
1048    /// let k = m.constant(Const::Bool(true));
1049    /// assert_eq!(m.finish().unwrap().constant(k), Some(&Const::Bool(true)));
1050    /// ```
1051    #[must_use]
1052    pub fn constant(&self, id: ConstId) -> Option<&Const> {
1053        self.consts.get(id.index())
1054    }
1055
1056    /// The import table.
1057    ///
1058    /// # Examples
1059    ///
1060    /// ```
1061    /// use bytecode_lang::{ModuleBuilder, ValType};
1062    ///
1063    /// let mut m = ModuleBuilder::new();
1064    /// let sig = m.func_type(&[ValType::Str], &[]);
1065    /// m.import("ls.io", "print", sig);
1066    /// assert_eq!(m.finish().unwrap().imports().len(), 1);
1067    /// ```
1068    #[must_use]
1069    pub fn imports(&self) -> &[Import] {
1070        &self.imports
1071    }
1072
1073    /// The import with this id, or `None` if it is out of range.
1074    ///
1075    /// # Examples
1076    ///
1077    /// ```
1078    /// use bytecode_lang::{ModuleBuilder, ValType};
1079    ///
1080    /// let mut m = ModuleBuilder::new();
1081    /// let sig = m.func_type(&[], &[]);
1082    /// let id = m.import("env", "tick", sig);
1083    /// let module = m.finish().unwrap();
1084    /// assert_eq!(module.import(id).map(|i| i.sig), Some(sig));
1085    /// ```
1086    #[must_use]
1087    pub fn import(&self, id: ImportId) -> Option<&Import> {
1088        self.imports.get(id.index())
1089    }
1090
1091    /// The global table.
1092    ///
1093    /// # Examples
1094    ///
1095    /// ```
1096    /// use bytecode_lang::{ModuleBuilder, ValType};
1097    ///
1098    /// let mut m = ModuleBuilder::new();
1099    /// m.global("count", ValType::I64, true, None);
1100    /// assert_eq!(m.finish().unwrap().globals().len(), 1);
1101    /// ```
1102    #[must_use]
1103    pub fn globals(&self) -> &[Global] {
1104        &self.globals
1105    }
1106
1107    /// The global with this id, or `None` if it is out of range.
1108    ///
1109    /// # Examples
1110    ///
1111    /// ```
1112    /// use bytecode_lang::{ModuleBuilder, ValType};
1113    ///
1114    /// let mut m = ModuleBuilder::new();
1115    /// let g = m.global("debug", ValType::Bool, false, None);
1116    /// assert_eq!(m.finish().unwrap().global(g).map(|g| g.ty), Some(ValType::Bool));
1117    /// ```
1118    #[must_use]
1119    pub fn global(&self, id: GlobalId) -> Option<&Global> {
1120        self.globals.get(id.index())
1121    }
1122
1123    /// The function table.
1124    ///
1125    /// # Examples
1126    ///
1127    /// ```
1128    /// use bytecode_lang::ModuleBuilder;
1129    ///
1130    /// assert!(ModuleBuilder::new().finish().unwrap().functions().is_empty());
1131    /// ```
1132    #[must_use]
1133    pub fn functions(&self) -> &[Function] {
1134        &self.functions
1135    }
1136
1137    /// The function with this id, or `None` if it is out of range.
1138    ///
1139    /// # Examples
1140    ///
1141    /// ```
1142    /// use bytecode_lang::{FuncId, ModuleBuilder};
1143    ///
1144    /// let module = ModuleBuilder::new().finish().unwrap();
1145    /// assert!(module.function(FuncId(0)).is_none());
1146    /// ```
1147    #[must_use]
1148    pub fn function(&self, id: FuncId) -> Option<&Function> {
1149        self.functions.get(id.index())
1150    }
1151
1152    /// The export table.
1153    ///
1154    /// # Examples
1155    ///
1156    /// ```
1157    /// use bytecode_lang::{ExportItem, ModuleBuilder};
1158    ///
1159    /// let mut m = ModuleBuilder::new();
1160    /// let mut f = m.function("main", &[], &[]);
1161    /// f.ret_void();
1162    /// let main = m.add_function(f).unwrap();
1163    /// m.export("main", ExportItem::Func(main));
1164    /// assert_eq!(m.finish().unwrap().exports()[0].item, ExportItem::Func(main));
1165    /// ```
1166    #[must_use]
1167    pub fn exports(&self) -> &[Export] {
1168        &self.exports
1169    }
1170
1171    /// The bound dynamic-operation hooks, sorted by hook code.
1172    ///
1173    /// # Examples
1174    ///
1175    /// ```
1176    /// use bytecode_lang::{Callee, Hook, ModuleBuilder, ValType};
1177    ///
1178    /// let mut m = ModuleBuilder::new();
1179    /// let mut f = m.function("concat", &[ValType::Dyn, ValType::Dyn], &[ValType::Dyn]);
1180    /// let a = f.param(0);
1181    /// f.ret(a);
1182    /// let id = m.add_function(f).unwrap();
1183    /// m.hook(Hook::Concat, Callee::Func(id));
1184    /// assert_eq!(m.finish().unwrap().hooks()[0].hook, Hook::Concat);
1185    /// ```
1186    #[must_use]
1187    pub fn hooks(&self) -> &[HookBinding] {
1188        &self.hooks
1189    }
1190
1191    /// The function bound to `hook`, if any.
1192    ///
1193    /// # Examples
1194    ///
1195    /// ```
1196    /// use bytecode_lang::{Hook, ModuleBuilder};
1197    ///
1198    /// assert_eq!(ModuleBuilder::new().finish().unwrap().hook(Hook::Add), None);
1199    /// ```
1200    #[must_use]
1201    pub fn hook(&self, hook: Hook) -> Option<Callee> {
1202        self.hooks
1203            .binary_search_by_key(&hook, |b| b.hook)
1204            .ok()
1205            .and_then(|i| self.hooks.get(i))
1206            .map(|b| b.callee)
1207    }
1208
1209    /// The module's name, if it has one.
1210    ///
1211    /// # Examples
1212    ///
1213    /// ```
1214    /// use bytecode_lang::ModuleBuilder;
1215    ///
1216    /// let mut m = ModuleBuilder::new();
1217    /// m.set_name("app.main");
1218    /// let module = m.finish().unwrap();
1219    /// assert_eq!(module.name().and_then(|n| module.string(n)), Some("app.main"));
1220    /// ```
1221    #[must_use]
1222    pub fn name(&self) -> Option<StrId> {
1223        self.name
1224    }
1225
1226    /// The initializer the loader runs after binding imports, if any.
1227    ///
1228    /// # Examples
1229    ///
1230    /// ```
1231    /// use bytecode_lang::ModuleBuilder;
1232    ///
1233    /// let mut m = ModuleBuilder::new();
1234    /// let mut f = m.function("init", &[], &[]);
1235    /// f.ret_void();
1236    /// let init = m.add_function(f).unwrap();
1237    /// m.set_start(init);
1238    /// assert_eq!(m.finish().unwrap().start(), Some(init));
1239    /// ```
1240    #[must_use]
1241    pub fn start(&self) -> Option<FuncId> {
1242        self.start
1243    }
1244}
1245
1246/// A runtime error kind with its stable code.
1247///
1248/// Codes `E0001`–`E0099` are defined by `specs/OPS.md` §6 for arithmetic
1249/// and conversions, `E0100`–`E0199` by LSB for the other instructions, and
1250/// `E0200`–`E0299` by HIR for its own forms (`specs/HIR.md` §8.10). Every
1251/// execution tier raises the same kind for the same fault.
1252/// [`Inst::ErrCode`](crate::Inst::ErrCode) reads the numeric code back from
1253/// a caught error value, and [`Inst::Raise`](crate::Inst::Raise) raises any
1254/// catchable kind from bytecode (how a code generator raises HIR's
1255/// `NoMatch`).
1256///
1257/// # Examples
1258///
1259/// ```
1260/// use bytecode_lang::ErrorKind;
1261///
1262/// assert_eq!(ErrorKind::DivByZero.code(), 2);
1263/// assert_eq!(ErrorKind::DivByZero.to_string(), "E0002 DivByZero");
1264/// assert_eq!(ErrorKind::from_code(102), Some(ErrorKind::IndexOutOfBounds));
1265/// assert!(!ErrorKind::OutOfFuel.is_catchable());
1266/// ```
1267#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
1268#[non_exhaustive]
1269pub enum ErrorKind {
1270    /// E0001: an integer result does not fit under `overflow = error`.
1271    ArithOverflow,
1272    /// E0002: integer division by zero under `div_zero = error`.
1273    DivByZero,
1274    /// E0003: a shift amount out of range under `shift = error`.
1275    ShiftOutOfRange,
1276    /// E0004: NaN or an out-of-range float converted under `float_to_int = error`.
1277    InvalidConversion,
1278    /// E0005: `char_from_u32` on a value that is not a Unicode scalar value.
1279    InvalidChar,
1280    /// E0006: integer `pow` with a negative exponent under any `overflow`
1281    /// policy but `promote` (OPS v2).
1282    NegativeExponent,
1283    /// E0100: a dynamic operation on kinds it does not support, a failed
1284    /// `cast` or `from_dyn`, or an argument of a dynamic call that does not
1285    /// convert to its parameter's type.
1286    TypeError,
1287    /// E0101: a heap instruction on a `nil` reference.
1288    NullReference,
1289    /// E0102: an array, string, or length operand out of range.
1290    IndexOutOfBounds,
1291    /// E0103: `map_get` (or `dget_index` on a map) of an absent key.
1292    KeyNotFound,
1293    /// E0104: `get_prop`/`set_prop` of a name the object does not have.
1294    UndefinedProperty,
1295    /// E0105: the call depth limit was reached.
1296    StackOverflow,
1297    /// E0106: an allocation failed. Always a trap.
1298    OutOfMemory,
1299    /// E0107: the run's fuel budget is spent. Always a trap.
1300    OutOfFuel,
1301    /// E0108: a UTF-8 `str_slice` boundary inside a character.
1302    InvalidStrIndex,
1303    /// E0109: `unreachable` was executed. Always a trap.
1304    Unreachable,
1305    /// E0110: a coroutine operation in a state that does not allow it:
1306    /// resuming one that is running, returned, or failed (a coroutine
1307    /// resuming itself included), or `coro_result` before it returned.
1308    InvalidCoroState,
1309    /// E0111: `yield` or `await` with no coroutine to suspend, or with a
1310    /// host frame between the instruction and its coroutine.
1311    CannotSuspend,
1312    /// E0112: `spawn` in a module with no `spawn` hook bound.
1313    NoScheduler,
1314    /// E0113: a coroutine being closed yielded instead of finishing
1315    /// (Python's "generator ignored GeneratorExit").
1316    CloseIgnored,
1317    /// E0114: a dynamic call's arguments do not bind to the callee's
1318    /// parameters: too many or too few, an unknown or repeated name, a
1319    /// positional argument after a named one (see
1320    /// [`ParamList::bind`](crate::ParamList::bind)).
1321    ArgumentError,
1322    /// E0200: a `match` none of whose arms matches (HIR v3 §8.10; Mox's
1323    /// `UnhandledMatchError`). No instruction raises it by itself: code
1324    /// generators raise it with [`Inst::Raise`](crate::Inst::Raise), the
1325    /// scrutinee as payload.
1326    NoMatch,
1327}
1328
1329impl ErrorKind {
1330    /// Every kind, in code order.
1331    pub const ALL: &'static [ErrorKind] = &[
1332        ErrorKind::ArithOverflow,
1333        ErrorKind::DivByZero,
1334        ErrorKind::ShiftOutOfRange,
1335        ErrorKind::InvalidConversion,
1336        ErrorKind::InvalidChar,
1337        ErrorKind::NegativeExponent,
1338        ErrorKind::TypeError,
1339        ErrorKind::NullReference,
1340        ErrorKind::IndexOutOfBounds,
1341        ErrorKind::KeyNotFound,
1342        ErrorKind::UndefinedProperty,
1343        ErrorKind::StackOverflow,
1344        ErrorKind::OutOfMemory,
1345        ErrorKind::OutOfFuel,
1346        ErrorKind::InvalidStrIndex,
1347        ErrorKind::Unreachable,
1348        ErrorKind::InvalidCoroState,
1349        ErrorKind::CannotSuspend,
1350        ErrorKind::NoScheduler,
1351        ErrorKind::CloseIgnored,
1352        ErrorKind::ArgumentError,
1353        ErrorKind::NoMatch,
1354    ];
1355
1356    /// The stable numeric code (`2` for `E0002`).
1357    ///
1358    /// # Examples
1359    ///
1360    /// ```
1361    /// use bytecode_lang::ErrorKind;
1362    ///
1363    /// assert_eq!(ErrorKind::TypeError.code(), 100);
1364    /// ```
1365    #[must_use]
1366    pub const fn code(self) -> u32 {
1367        match self {
1368            ErrorKind::ArithOverflow => 1,
1369            ErrorKind::DivByZero => 2,
1370            ErrorKind::ShiftOutOfRange => 3,
1371            ErrorKind::InvalidConversion => 4,
1372            ErrorKind::InvalidChar => 5,
1373            ErrorKind::NegativeExponent => 6,
1374            ErrorKind::TypeError => 100,
1375            ErrorKind::NullReference => 101,
1376            ErrorKind::IndexOutOfBounds => 102,
1377            ErrorKind::KeyNotFound => 103,
1378            ErrorKind::UndefinedProperty => 104,
1379            ErrorKind::StackOverflow => 105,
1380            ErrorKind::OutOfMemory => 106,
1381            ErrorKind::OutOfFuel => 107,
1382            ErrorKind::InvalidStrIndex => 108,
1383            ErrorKind::Unreachable => 109,
1384            ErrorKind::InvalidCoroState => 110,
1385            ErrorKind::CannotSuspend => 111,
1386            ErrorKind::NoScheduler => 112,
1387            ErrorKind::CloseIgnored => 113,
1388            ErrorKind::ArgumentError => 114,
1389            ErrorKind::NoMatch => 200,
1390        }
1391    }
1392
1393    /// The kind with this numeric code, or `None`.
1394    ///
1395    /// # Examples
1396    ///
1397    /// ```
1398    /// use bytecode_lang::ErrorKind;
1399    ///
1400    /// assert_eq!(ErrorKind::from_code(1), Some(ErrorKind::ArithOverflow));
1401    /// assert_eq!(ErrorKind::from_code(0), None);
1402    /// ```
1403    #[must_use]
1404    pub fn from_code(code: u32) -> Option<Self> {
1405        ErrorKind::ALL.iter().copied().find(|k| k.code() == code)
1406    }
1407
1408    /// The kind's name, as in OPS and LSB.
1409    ///
1410    /// # Examples
1411    ///
1412    /// ```
1413    /// use bytecode_lang::ErrorKind;
1414    ///
1415    /// assert_eq!(ErrorKind::KeyNotFound.name(), "KeyNotFound");
1416    /// ```
1417    #[must_use]
1418    pub const fn name(self) -> &'static str {
1419        match self {
1420            ErrorKind::ArithOverflow => "ArithOverflow",
1421            ErrorKind::DivByZero => "DivByZero",
1422            ErrorKind::ShiftOutOfRange => "ShiftOutOfRange",
1423            ErrorKind::InvalidConversion => "InvalidConversion",
1424            ErrorKind::InvalidChar => "InvalidChar",
1425            ErrorKind::NegativeExponent => "NegativeExponent",
1426            ErrorKind::TypeError => "TypeError",
1427            ErrorKind::NullReference => "NullReference",
1428            ErrorKind::IndexOutOfBounds => "IndexOutOfBounds",
1429            ErrorKind::KeyNotFound => "KeyNotFound",
1430            ErrorKind::UndefinedProperty => "UndefinedProperty",
1431            ErrorKind::StackOverflow => "StackOverflow",
1432            ErrorKind::OutOfMemory => "OutOfMemory",
1433            ErrorKind::OutOfFuel => "OutOfFuel",
1434            ErrorKind::InvalidStrIndex => "InvalidStrIndex",
1435            ErrorKind::Unreachable => "Unreachable",
1436            ErrorKind::InvalidCoroState => "InvalidCoroState",
1437            ErrorKind::CannotSuspend => "CannotSuspend",
1438            ErrorKind::NoScheduler => "NoScheduler",
1439            ErrorKind::CloseIgnored => "CloseIgnored",
1440            ErrorKind::ArgumentError => "ArgumentError",
1441            ErrorKind::NoMatch => "NoMatch",
1442        }
1443    }
1444
1445    /// Whether a try region can catch this kind. Resource exhaustion and
1446    /// `unreachable` always abort the run; every other kind is raised as an
1447    /// error (or aborts, if the instruction's policy says `trap`).
1448    ///
1449    /// # Examples
1450    ///
1451    /// ```
1452    /// use bytecode_lang::ErrorKind;
1453    ///
1454    /// assert!(ErrorKind::KeyNotFound.is_catchable());
1455    /// assert!(!ErrorKind::Unreachable.is_catchable());
1456    /// ```
1457    #[must_use]
1458    pub const fn is_catchable(self) -> bool {
1459        !matches!(
1460            self,
1461            ErrorKind::OutOfMemory | ErrorKind::OutOfFuel | ErrorKind::Unreachable
1462        )
1463    }
1464}
1465
1466/// `E0002 DivByZero`.
1467impl fmt::Display for ErrorKind {
1468    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1469        write!(f, "E{:04} {}", self.code(), self.name())
1470    }
1471}
1472
1473#[cfg(test)]
1474mod tests {
1475    use alloc::string::ToString;
1476    use alloc::vec;
1477
1478    use super::*;
1479
1480    #[test]
1481    fn strings_store_back_to_back() {
1482        let mut s = Strings::default();
1483        assert!(s.push("ab"));
1484        assert!(s.push(""));
1485        assert!(s.push("é"));
1486        assert_eq!(s.get(0), Some("ab"));
1487        assert_eq!(s.get(1), Some(""));
1488        assert_eq!(s.get(2), Some("é"));
1489        assert_eq!(s.get(3), None);
1490        assert_eq!(s.iter().count(), 3);
1491    }
1492
1493    #[test]
1494    fn error_codes_are_unique_and_round_trip() {
1495        for &kind in ErrorKind::ALL {
1496            assert_eq!(ErrorKind::from_code(kind.code()), Some(kind));
1497        }
1498        assert_eq!(ErrorKind::ArithOverflow.to_string(), "E0001 ArithOverflow");
1499        assert_eq!(ErrorKind::Unreachable.to_string(), "E0109 Unreachable");
1500        assert_eq!(ErrorKind::NoMatch.to_string(), "E0200 NoMatch");
1501        assert_eq!(
1502            ErrorKind::NegativeExponent.to_string(),
1503            "E0006 NegativeExponent"
1504        );
1505        // Every code fits the one-byte modifier of `raise`.
1506        assert!(ErrorKind::ALL.iter().all(|k| k.code() <= 255));
1507    }
1508
1509    #[test]
1510    fn const_children_cover_arrays_and_maps() {
1511        let map = Const::Map(vec![(ConstId(0), ConstId(1)), (ConstId(2), ConstId(3))]);
1512        assert_eq!(
1513            map.children().collect::<Vec<_>>(),
1514            [ConstId(0), ConstId(1), ConstId(2), ConstId(3)]
1515        );
1516        assert_eq!(Const::Int(1).children().count(), 0);
1517    }
1518
1519    #[test]
1520    fn nan_constants_print_their_payload() {
1521        assert_eq!(
1522            Const::F64(0x7ff8_0000_0000_0001).to_string(),
1523            "f64 nan:0x7ff8000000000001"
1524        );
1525        assert_eq!(Const::F32(0x7fc0_0000).to_string(), "f32 nan:0x7fc00000");
1526        assert_eq!(
1527            Const::Bytes(vec![b'a', 0xff]).to_string(),
1528            "bytes b\"a\\xff\""
1529        );
1530    }
1531}