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