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