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}