gbf_core/
bytecode_loader.rs

1#![deny(missing_docs)]
2
3use crate::{
4    graal_io::{GraalIoError, GraalReader},
5    instruction::Instruction,
6    opcode::{Opcode, OpcodeError},
7    operand::{Operand, OperandError},
8    utils::Gs2BytecodeAddress,
9};
10
11use std::{
12    collections::{BTreeSet, HashMap},
13    io::Read,
14};
15
16use log::warn;
17use petgraph::graph::{DiGraph, NodeIndex};
18use serde::{Deserialize, Serialize};
19use thiserror::Error;
20
21/// Error type for bytecode operations.
22#[derive(Error, Debug, Clone, Serialize)]
23pub enum BytecodeLoaderError {
24    /// Error for when an invalid section type is encountered.
25    #[error("Invalid section type: {0}")]
26    InvalidSectionType(u32),
27
28    /// Error for when an invalid section length is encountered.
29    #[error("Invalid section length for {0}: {1}")]
30    InvalidSectionLength(SectionType, u32),
31
32    /// Error when string index is out of bounds.
33    #[error("String index {0} is out of bounds. Length: {1}")]
34    StringIndexOutOfBounds(usize, usize),
35
36    /// Error for when there is no previous instruction when setting an operand.
37    #[error("No previous instruction to set operand")]
38    NoPreviousInstruction,
39
40    /// Unreachable block error.
41    #[error("Block at address {0} is unreachable")]
42    UnreachableBlock(Gs2BytecodeAddress),
43
44    /// Error for when an I/O error occurs.
45    #[error("GraalIo error: {0}")]
46    GraalIo(#[from] GraalIoError),
47
48    /// Error for when an invalid opcode is encountered.
49    #[error("Invalid opcode: {0}")]
50    OpcodeError(#[from] OpcodeError),
51
52    /// Error for when an invalid operand is encountered.
53    #[error("Invalid operand: {0}")]
54    InvalidOperand(#[from] OperandError),
55}
56
57impl std::fmt::Display for SectionType {
58    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59        match self {
60            SectionType::Gs1Flags => write!(f, "Gs1Flags"),
61            SectionType::Functions => write!(f, "Functions"),
62            SectionType::Strings => write!(f, "Strings"),
63            SectionType::Instructions => write!(f, "Instructions"),
64        }
65    }
66}
67
68/// Represents a section type in a module.
69#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)]
70#[repr(u32)]
71pub enum SectionType {
72    /// The section contains flags for the module.
73    Gs1Flags = 1,
74
75    /// The section contains the module's functions.
76    Functions = 2,
77
78    /// The section contains the module's strings.
79    Strings = 3,
80
81    /// The section contains the module's instructions.
82    Instructions = 4,
83}
84
85/// A builder for a BytecodeLoader.
86pub struct BytecodeLoaderBuilder<R> {
87    reader: R,
88}
89
90impl<R: std::io::Read> BytecodeLoaderBuilder<R> {
91    /// Creates a new BytecodeLoaderBuilder.
92    ///
93    /// # Arguments
94    /// - `reader`: The reader to read the bytecode from.
95    ///
96    /// # Returns
97    /// - A new `BytecodeLoaderBuilder` instance.
98    ///
99    /// # Example
100    /// ```
101    /// use gbf_core::bytecode_loader::BytecodeLoaderBuilder;
102    ///
103    /// let reader = std::io::Cursor::new(vec![0x00, 0x00, 0x00, 0x00]);
104    /// let builder = BytecodeLoaderBuilder::new(reader);
105    /// ```
106    pub fn new(reader: R) -> Self {
107        Self { reader }
108    }
109
110    /// Builds a `BytecodeLoader` from the builder.
111    ///
112    /// # Returns
113    /// - A `Result` containing the `BytecodeLoader` if successful.
114    ///
115    /// # Errors
116    /// - `BytecodeLoaderError::InvalidSectionType` if an invalid section type is encountered.
117    /// - `BytecodeLoaderError::InvalidSectionLength` if an invalid section length is encountered.
118    /// - `BytecodeLoaderError::StringIndexOutOfBounds` if a string index is out of bounds.
119    /// - `BytecodeLoaderError::NoPreviousInstruction` if there is no previous instruction when setting an operand.
120    /// - `BytecodeLoaderError::GraalIo` if an I/O error occurs.
121    /// - `BytecodeLoaderError::OpcodeError` if an invalid opcode is encountered.
122    pub fn build(self) -> Result<BytecodeLoader<R>, BytecodeLoaderError> {
123        let mut loader = BytecodeLoader {
124            block_breaks: BTreeSet::new(),
125            reader: GraalReader::new(self.reader),
126            function_map: HashMap::new(),
127            strings: Vec::new(),
128            instructions: Vec::new(),
129            raw_block_graph: DiGraph::new(),
130            raw_block_address_to_node: HashMap::new(),
131            block_address_to_function: HashMap::new(),
132        };
133        loader.load()?; // Load data during construction
134        Ok(loader)
135    }
136}
137
138/// A structure for loading bytecode from a reader.
139pub struct BytecodeLoader<R: Read> {
140    reader: GraalReader<R>,
141    strings: Vec<String>,
142
143    /// A map of function names to their addresses.
144    pub function_map: HashMap<Option<String>, Gs2BytecodeAddress>,
145
146    /// The instructions in the module.
147    pub instructions: Vec<Instruction>,
148
149    // A HashSet of where block breaks occur.
150    block_breaks: BTreeSet<Gs2BytecodeAddress>,
151
152    /// The relationship between each block start address and the next block start address.
153    raw_block_graph: DiGraph<Gs2BytecodeAddress, ()>,
154
155    /// A map of block start addresses to their corresponding node in the graph.
156    raw_block_address_to_node: HashMap<Gs2BytecodeAddress, NodeIndex>,
157
158    /// A map of block start addresses to their corresponding function name.
159    pub block_address_to_function: HashMap<Gs2BytecodeAddress, Option<String>>,
160}
161
162impl<R: Read> BytecodeLoader<R> {
163    /// Asserts that the section length is correct.
164    ///
165    /// # Arguments
166    /// - `section_type`: The type of the section.
167    /// - `expected_length`: The expected length of the section.
168    /// - `got_length`: The actual length of the section.
169    ///
170    /// # Returns
171    /// - A `Result` indicating success or failure.
172    ///
173    /// # Errors
174    /// - `BytecodeLoaderError::InvalidSectionLength` if the section length is incorrect.
175    fn expect_section_length(
176        section_type: SectionType,
177        expected_length: u32,
178        got_length: u32,
179    ) -> Result<(), BytecodeLoaderError> {
180        if expected_length != got_length {
181            return Err(BytecodeLoaderError::InvalidSectionLength(
182                section_type,
183                got_length,
184            ));
185        }
186        Ok(())
187    }
188
189    /// Reads the flags section from the reader.
190    ///
191    /// We don't actually need to do anything with the flags section, so we just read it and ignore it.
192    fn read_gs1_flags(&mut self) -> Result<(), BytecodeLoaderError> {
193        let section_length = self.reader.read_u32().map_err(BytecodeLoaderError::from)?;
194        let _flags = self.reader.read_u32().map_err(BytecodeLoaderError::from)?;
195
196        // assert that the section length is correct
197        Self::expect_section_length(SectionType::Gs1Flags, 4, section_length)?;
198
199        Ok(())
200    }
201
202    /// Insert a block start into the graph
203    ///
204    /// # Arguments
205    /// - `address`: The address of the block.
206    fn insert_block_start(&mut self, address: Gs2BytecodeAddress) {
207        self.block_breaks.insert(address);
208    }
209
210    /// Reads the functions section from the reader. This section contains the names of the functions
211    /// in the module.
212    ///
213    /// # Returns
214    /// - A `Result` indicating success or failure.
215    ///
216    /// # Errors
217    /// - `BytecodeLoaderError::InvalidSectionLength` if the section length is incorrect.
218    /// - `BytecodeLoaderError::GraalIo` if an I/O error occurs.
219    fn read_functions(&mut self) -> Result<(), BytecodeLoaderError> {
220        let section_length = self.reader.read_u32().map_err(BytecodeLoaderError::from)?;
221
222        // Insert the entry point function
223        self.function_map.insert(None, 0);
224
225        // For each function, use self.reader.read_u32() to get the location of the function,
226        // and then use self.reader.read_string() to get the name of the function. We should
227        // only read up to section_length bytes.
228        let mut bytes_read = 0;
229        while bytes_read < section_length {
230            let function_location =
231                self.reader.read_u32().map_err(BytecodeLoaderError::from)? as Gs2BytecodeAddress;
232            let function_name = self
233                .reader
234                .read_string()
235                .map_err(BytecodeLoaderError::from)?
236                .0;
237            self.function_map
238                .insert(Some(function_name.clone()), function_location);
239            bytes_read += 4 + function_name.len() as u32;
240            bytes_read += 1; // Null terminator
241
242            self.insert_block_start(function_location);
243        }
244
245        // assert that the section length is correct
246        Self::expect_section_length(SectionType::Functions, section_length, bytes_read)?;
247
248        Ok(())
249    }
250
251    /// Reads the strings section from the reader. This section contains the strings used in the module.
252    ///
253    /// # Returns
254    /// - A `Result` indicating success or failure.
255    ///
256    /// # Errors
257    /// - `BytecodeLoaderError::GraalIo` if an I/O error occurs.
258    /// - `BytecodeLoaderError::InvalidSectionLength` if the section length is incorrect.
259    fn read_strings(&mut self) -> Result<(), BytecodeLoaderError> {
260        let section_length = self.reader.read_u32().map_err(BytecodeLoaderError::from)?;
261
262        // For each string, use self.reader.read_string() to get the string. We should only read up to section_length bytes.
263        let mut bytes_read = 0;
264        while bytes_read < section_length {
265            let (string, raw_len) = self
266                .reader
267                .read_string()
268                .map_err(BytecodeLoaderError::from)?;
269            self.strings.push(string.clone());
270            bytes_read += raw_len;
271            bytes_read += 1; // for the null terminator
272        }
273
274        // assert that the section length is correct
275        Self::expect_section_length(SectionType::Strings, section_length, bytes_read)?;
276
277        Ok(())
278    }
279
280    /// Read one opcode from the reader and return it.
281    fn read_opcode(&mut self) -> Result<Opcode, BytecodeLoaderError> {
282        let opcode_byte = self.reader.read_u8().map_err(BytecodeLoaderError::from)?;
283        let opcode = Opcode::from_byte(opcode_byte)?;
284        Ok(opcode)
285    }
286
287    /// Read one operand from the reader and return it along with the number of bytes read.
288    fn read_operand(
289        &mut self,
290        opcode: Opcode,
291    ) -> Result<Option<(Operand, usize)>, BytecodeLoaderError> {
292        match opcode {
293            Opcode::ImmStringByte => {
294                let string_index = self.reader.read_u8().map_err(BytecodeLoaderError::from)?;
295                let string = self.strings.get(string_index as usize).ok_or(
296                    BytecodeLoaderError::StringIndexOutOfBounds(
297                        string_index as usize,
298                        self.strings.len(),
299                    ),
300                )?;
301                Ok(Some((Operand::new_string(string), 1)))
302            }
303            Opcode::ImmStringShort => {
304                let string_index = self.reader.read_u16().map_err(BytecodeLoaderError::from)?;
305                let string = self.strings.get(string_index as usize).ok_or(
306                    BytecodeLoaderError::StringIndexOutOfBounds(
307                        string_index as usize,
308                        self.strings.len(),
309                    ),
310                )?;
311                Ok(Some((Operand::new_string(string), 2)))
312            }
313            Opcode::ImmStringInt => {
314                let string_index = self.reader.read_u32().map_err(BytecodeLoaderError::from)?;
315                let string = self.strings.get(string_index as usize).ok_or(
316                    BytecodeLoaderError::StringIndexOutOfBounds(
317                        string_index as usize,
318                        self.strings.len(),
319                    ),
320                )?;
321                Ok(Some((Operand::new_string(string), 4)))
322            }
323            Opcode::ImmByte => {
324                let value = self.reader.read_u8().map_err(BytecodeLoaderError::from)?;
325                Ok(Some((Operand::new_number(value as i8 as i32), 1)))
326            }
327            Opcode::ImmShort => {
328                let value = self.reader.read_u16().map_err(BytecodeLoaderError::from)?;
329                Ok(Some((Operand::new_number(value as i16 as i32), 2)))
330            }
331            Opcode::ImmInt => {
332                let value = self.reader.read_u32().map_err(BytecodeLoaderError::from)?;
333                Ok(Some((Operand::new_number(value as i32), 4)))
334            }
335            Opcode::ImmFloat => {
336                let value = self
337                    .reader
338                    .read_string()
339                    .map_err(BytecodeLoaderError::from)?
340                    .0;
341                Ok(Some((Operand::new_float(value.clone()), value.len() + 1)))
342            }
343            _ => Ok(None),
344        }
345    }
346
347    /// Reads the instructions section from the reader. This section contains the bytecode instructions.
348    fn read_instructions(&mut self) -> Result<(), BytecodeLoaderError> {
349        // Add the first block start address
350        self.insert_block_start(0);
351
352        let section_length = self.reader.read_u32().map_err(BytecodeLoaderError::from)?;
353
354        let mut bytes_read = 0;
355        while bytes_read < section_length {
356            let opcode = self.read_opcode()?;
357            bytes_read += 1;
358
359            let operand = self.read_operand(opcode)?;
360
361            if let Some(operand) = operand {
362                // Separate scope for mutable borrow of instructions
363                {
364                    let last_instruction = self
365                        .instructions
366                        .last_mut()
367                        .ok_or(BytecodeLoaderError::NoPreviousInstruction)?;
368
369                    last_instruction.set_operand(operand.0.clone());
370                }
371
372                bytes_read += operand.1 as u32;
373
374                assert!(self.instructions.last().is_some());
375
376                // We can unwrap here because we know that the last instruction exists in the scope above
377                if self.instructions.last().unwrap().opcode.has_jump_target() {
378                    self.insert_block_start(operand.0.get_number_value()? as Gs2BytecodeAddress);
379                }
380            } else {
381                // Create a new instruction
382                let address = self.instructions.len();
383                self.instructions.push(Instruction::new(opcode, address));
384
385                if opcode.is_block_end() {
386                    let current_address = address as Gs2BytecodeAddress;
387                    self.insert_block_start(current_address + 1);
388                }
389            }
390        }
391
392        // Verify the section length
393        Self::expect_section_length(SectionType::Instructions, section_length, bytes_read)?;
394
395        // Handle the case of empty instructions
396        if self.instructions.is_empty() {
397            warn!("No instructions were loaded.");
398            self.block_breaks.clear();
399        }
400
401        // Validate all addresses
402        let instruction_count = self.instructions.len() as Gs2BytecodeAddress;
403        for address in self.block_breaks.iter() {
404            // It is legal to jump to the "end" of the instructions, but not past it.
405            if *address > instruction_count {
406                return Err(BytecodeLoaderError::InvalidOperand(
407                    OperandError::InvalidJumpTarget(*address),
408                ));
409            }
410        }
411
412        Ok(())
413    }
414
415    /// Loads the bytecode from the reader into the structure.
416    ///
417    /// # Returns
418    /// - A `Result` indicating success or failure.
419    ///
420    /// # Errors
421    /// - `BytecodeLoaderError::InvalidSectionType` if an invalid section type is encountered.
422    /// - `BytecodeLoaderError::InvalidSectionLength` if an invalid section length is encountered.
423    /// - `BytecodeLoaderError::StringIndexOutOfBounds` if a string index is out of bounds.
424    /// - `BytecodeLoaderError::NoPreviousInstruction` if there is no previous instruction when setting an operand.
425    /// - `BytecodeLoaderError::GraalIo` if an I/O error occurs.
426    /// - `BytecodeLoaderError::OpcodeError` if an invalid opcode is encountered.
427    /// - `BytecodeLoaderError::InvalidOperand` if an invalid operand is encountered.
428    fn load(&mut self) -> Result<(), BytecodeLoaderError> {
429        // TODO: I know there will only be 4 sections, but I'd like to make this more dynamic.
430        for _ in 0..4 {
431            let section_type = self.read_section_type()?;
432            match section_type {
433                SectionType::Gs1Flags => {
434                    self.read_gs1_flags()?;
435                }
436                SectionType::Functions => {
437                    self.read_functions()?;
438                }
439                SectionType::Strings => {
440                    self.read_strings()?;
441                }
442                SectionType::Instructions => {
443                    self.read_instructions()?;
444                }
445            }
446        }
447
448        // After reading in all of the block breaks, we can now create the graph.
449        for block_break in self.block_breaks.iter() {
450            let node = self.raw_block_graph.add_node(*block_break);
451            self.raw_block_address_to_node.insert(*block_break, node);
452        }
453
454        // Iterate through each instruction to figure out the edges
455        for instruction in self.instructions.iter() {
456            let current_instruction_address = instruction.address as Gs2BytecodeAddress;
457            let current_block_address = self.find_block_start_address(current_instruction_address);
458            // if the instruction is the last instruction in the block
459            let is_block_end = self
460                .block_breaks
461                .contains(&(current_instruction_address + 1));
462
463            // If the current instruction is a jump, then we need to add an edge to the target block start
464            if instruction.opcode.has_jump_target() {
465                let source_node = self
466                    .raw_block_address_to_node
467                    .get(&current_block_address)
468                    // We can unwrap here because we know that the current block address exists
469                    // If it doesn't, then there is a bug that needs to be fixed in the internal
470                    // logic of the loader.
471                    .unwrap();
472
473                // Unwrap here because we know that the operand exists due to a previous check in
474                // `read_instructions`
475                let target_address =
476                    instruction.operand.as_ref().unwrap().get_number_value()? as Gs2BytecodeAddress;
477
478                // Also unwrap here because we know that the target address exists in the block breaks
479                let target_node = self.raw_block_address_to_node.get(&target_address).unwrap();
480
481                self.raw_block_graph
482                    .add_edge(*source_node, *target_node, ());
483            }
484
485            // If the current opcode has a fallthrough, then we need to add an edge to the next block start
486            if is_block_end && instruction.opcode.connects_to_next_block() {
487                let source_node = self
488                    .raw_block_address_to_node
489                    .get(&current_block_address)
490                    // We can unwrap here because we know that the current block address exists
491                    // If it doesn't, then there is a bug that needs to be fixed in the internal
492                    // logic of the loader.
493                    .unwrap();
494
495                // Find the next block start address
496                let next_block_address = current_instruction_address + 1;
497
498                // Also unwrap here because we know that the target address exists in the block breaks
499                let target_node = self
500                    .raw_block_address_to_node
501                    .get(&next_block_address)
502                    .unwrap();
503
504                self.raw_block_graph
505                    .add_edge(*source_node, *target_node, ());
506            }
507        }
508
509        // Iterate through each function
510        for (function_name, function_address) in self.function_map.iter() {
511            assert_eq!(
512                self.raw_block_graph.node_count(),
513                self.raw_block_address_to_node.len(),
514                "Graph node count and block address map size do not match!"
515            );
516            for (&block_address, &node) in &self.raw_block_address_to_node {
517                assert!(
518                    self.raw_block_graph.node_indices().any(|n| n == node),
519                    "Node {:?} for block address {} is missing in the graph.",
520                    node,
521                    block_address
522                );
523            }
524
525            if let Some(function_node) = self.raw_block_address_to_node.get(function_address) {
526                let mut dfs = petgraph::visit::Dfs::new(&self.raw_block_graph, *function_node);
527
528                while let Some(node) = dfs.next(&self.raw_block_graph) {
529                    // Map node back to block address.
530                    if let Some(block_address) = self
531                        .raw_block_address_to_node
532                        .iter()
533                        .find_map(|(k, v)| if *v == node { Some(*k) } else { None })
534                    {
535                        self.block_address_to_function
536                            .insert(block_address, function_name.clone());
537                    } else {
538                        warn!("Node {:?} has no matching block address.", node);
539                    }
540                }
541            } else {
542                warn!(
543                    "Function '{:?}' at address {} has no corresponding node in raw_block_address_to_node.",
544                    function_name, function_address
545                );
546            }
547        }
548
549        Ok(())
550    }
551
552    /// Get the function name for a given address.
553    ///
554    /// # Arguments
555    /// - `address`: The address to get the function name for.
556    ///
557    /// # Returns
558    /// - The function name, if it exists.
559    ///
560    /// # Errors
561    /// - `BytecodeLoaderError::UnreachableBlock` if the block is unreachable, and therefore,
562    ///   the function name cannot be determined.
563    pub fn get_function_name_for_address(
564        &self,
565        address: Gs2BytecodeAddress,
566    ) -> Result<Option<String>, BytecodeLoaderError> {
567        let block_start = self.find_block_start_address(address);
568        Ok(self
569            .block_address_to_function
570            .get(&block_start)
571            // return error if the block is unreachable
572            .ok_or(BytecodeLoaderError::UnreachableBlock(block_start))?
573            .clone())
574    }
575
576    /// Checks if an instruction is reachable.
577    ///
578    /// # Arguments
579    /// - `address`: The address to check.
580    ///
581    /// # Returns
582    /// - `true` if the instruction is reachable, `false` otherwise.
583    pub fn is_instruction_reachable(&self, address: Gs2BytecodeAddress) -> bool {
584        let blk = self.find_block_start_address(address);
585        self.block_address_to_function.contains_key(&blk)
586    }
587
588    /// Helper function to figure out what block the address is in. This basically looks
589    /// at the argument, and finds the closest block start address that is less than or equal
590    ///
591    /// # Arguments
592    /// - `address`: The address to find the block for.
593    ///
594    /// # Returns
595    /// - The block start address.
596    pub fn find_block_start_address(&self, address: Gs2BytecodeAddress) -> Gs2BytecodeAddress {
597        let mut block_start = 0;
598        for block_break in self.block_breaks.iter() {
599            if *block_break > address {
600                break;
601            }
602            block_start = *block_break;
603        }
604        block_start
605    }
606
607    /// Reads a section type from the reader.
608    fn read_section_type(&mut self) -> Result<SectionType, BytecodeLoaderError> {
609        let section_type = self.reader.read_u32().map_err(BytecodeLoaderError::from)?;
610        match section_type {
611            1 => Ok(SectionType::Gs1Flags),
612            2 => Ok(SectionType::Functions),
613            3 => Ok(SectionType::Strings),
614            4 => Ok(SectionType::Instructions),
615            _ => Err(BytecodeLoaderError::InvalidSectionType(section_type)),
616        }
617    }
618}
619
620#[cfg(test)]
621mod tests {
622    use crate::{bytecode_loader::BytecodeLoaderBuilder, utils::Gs2BytecodeAddress};
623
624    #[test]
625    fn test_load() {
626        let reader = std::io::Cursor::new(vec![
627            0x00, 0x00, 0x00, 0x01, // Section type: Gs1Flags
628            0x00, 0x00, 0x00, 0x04, // Length: 4
629            0x00, 0x00, 0x00, 0x00, // Flags: 0
630            0x00, 0x00, 0x00, 0x02, // Section type: Functions
631            0x00, 0x00, 0x00, 0x09, // Length: 9
632            0x00, 0x00, 0x00, 0x00, // Function location: 0
633            0x6d, 0x61, 0x69, 0x6e, // Function name: "main"
634            0x00, // Null terminator
635            0x00, 0x00, 0x00, 0x03, // Section type: Strings
636            0x00, 0x00, 0x00, 0x04, // Length: 4
637            0x61, 0x62, 0x63, 0x00, // String: "abc"
638            0x00, 0x00, 0x00, 0x04, // Section type: Instructions
639            0x00, 0x00, 0x00, 0x0c, // Length: 12
640            0x01, // Opcode: Jmp
641            0xF3, // Opcode: ImmByte
642            0x01, // Operand: 1
643            0x14, // Opcode: PushNumber
644            0xF4, // Opcode: ImmShort
645            0x00, 0x01, // Operand: 1
646            0x15, // Opcode: PushString
647            0xF0, // Opcode: ImmStringByte
648            0x00, // Operand: 0
649            0x1b, // Opcode: PushPi
650            0x07, // Opcode: Ret
651        ]);
652        let loader = BytecodeLoaderBuilder::new(reader).build().unwrap();
653
654        assert_eq!(loader.function_map.len(), 2);
655        assert_eq!(loader.function_map.get(&Some("main".to_string())), Some(&0));
656        assert_eq!(loader.strings.len(), 1);
657        assert_eq!(loader.strings.first(), Some(&"abc".to_string()));
658        assert_eq!(loader.instructions.len(), 5);
659        assert_eq!(loader.instructions[0].opcode, crate::opcode::Opcode::Jmp);
660        assert_eq!(
661            loader.instructions[1].opcode,
662            crate::opcode::Opcode::PushNumber
663        );
664        assert_eq!(
665            loader.instructions[1].operand,
666            Some(crate::operand::Operand::new_number(1))
667        );
668        assert_eq!(
669            loader.instructions[2].opcode,
670            crate::opcode::Opcode::PushString
671        );
672        assert_eq!(
673            loader.instructions[2].operand,
674            Some(crate::operand::Operand::new_string("abc"))
675        );
676        assert_eq!(loader.instructions[3].opcode, crate::opcode::Opcode::Pi);
677        assert_eq!(loader.instructions[4].opcode, crate::opcode::Opcode::Ret);
678    }
679
680    #[test]
681    fn test_complex_load() {
682        let reader = std::io::Cursor::new(vec![
683            0x00, 0x00, 0x00, 0x01, // Section type: Gs1Flags
684            0x00, 0x00, 0x00, 0x04, // Length: 4
685            0x00, 0x00, 0x00, 0x00, // Flags: 0
686            0x00, 0x00, 0x00, 0x02, // Section type: Functions
687            0x00, 0x00, 0x00, 0x09, // Length: 9
688            0x00, 0x00, 0x00, 0x01, // Function location: 1
689            0x6d, 0x61, 0x69, 0x6e, // Function name: "main"
690            0x00, // Null terminator
691            0x00, 0x00, 0x00, 0x03, // Section type: Strings
692            0x00, 0x00, 0x00, 0x04, // Length: 4
693            0x61, 0x62, 0x63, 0x00, // String: "abc"
694            0x00, 0x00, 0x00, 0x04, // Section type: Instructions
695            0x00, 0x00, 0x00, 0x47, // Length: 71
696            // Instructions
697            0x01, 0xF3, 0x19, // Jmp 0x19
698            0x14, 0xF3, 0x00, // PushNumber 0
699            0x01, 0xF3, 0x0c, // Jmp 0x0c
700            0x14, 0xF3, 0x00, // PushNumber 0
701            0x01, 0xF3, 0x17, // Jmp 0x17
702            0x14, 0xF3, 0x00, // PushNumber 0
703            0x01, 0xF3, 0x17, // Jmp 0x17
704            0x14, 0xF3, 0x00, // PushNumber 0
705            0x01, 0xF3, 0x17, // Jmp 0x17
706            0x14, 0xF3, 0x00, // PushNumber 0 (unreachable)
707            0x01, 0xF3, 0x17, // Jmp 0x17
708            0x01, 0xF3, 0x17, // Jmp 0x17
709            0x14, 0xF3, 0x00, // PushNumber 0
710            0x02, 0xF3, 0x03, // Jeq 0x03
711            0x14, 0xF3, 0x00, // PushNumber 0
712            0x02, 0xF3, 0x03, // Jeq 0x03
713            0x14, 0xF3, 0x00, // PushNumber 0
714            0x02, 0xF3, 0x03, // Jeq 0x03
715            0x14, 0xF3, 0x00, // PushNumber 0
716            0x02, 0xF3, 0x05, // Jeq 0x05
717            0x14, 0xF3, 0x00, // PushNumber 0
718            0x02, 0xF3, 0x07, // Jeq 0x07
719            0x01, 0xF3, 0x0b, // Jmp 0x0b
720            0x20, // Pop
721            0x07, // Ret
722        ]);
723        let loader = BytecodeLoaderBuilder::new(reader).build().unwrap();
724
725        assert_eq!(loader.function_map.len(), 2);
726
727        // get all of the block start addresses
728        // There is a block that is unreachable. It will still appear in the block starts.
729        let block_starts: Vec<Gs2BytecodeAddress> = loader.block_breaks.iter().copied().collect();
730
731        // Ensure that the block at address 0 connects to the block at address 0x19
732        let block_0 = loader.find_block_start_address(0);
733        let block_0x19 = loader.find_block_start_address(0x19);
734        assert!(loader.raw_block_graph.contains_edge(
735            loader.raw_block_address_to_node[&block_0],
736            loader.raw_block_address_to_node[&block_0x19]
737        ));
738
739        // Ensure that the block at address 1 connects to the block at address 0x0c
740        let block_1 = loader.find_block_start_address(1);
741        let block_0x0c = loader.find_block_start_address(0x0c);
742        assert!(loader.raw_block_graph.contains_edge(
743            loader.raw_block_address_to_node[&block_1],
744            loader.raw_block_address_to_node[&block_0x0c]
745        ));
746
747        // Ensure that the block at address 0x03 connects to the block at address 0x17
748        let block_0x03 = loader.find_block_start_address(0x03);
749        let block_0x17 = loader.find_block_start_address(0x17);
750        assert!(loader.raw_block_graph.contains_edge(
751            loader.raw_block_address_to_node[&block_0x03],
752            loader.raw_block_address_to_node[&block_0x17]
753        ));
754
755        // 0x05 -> 0x17
756        let block_0x05 = loader.find_block_start_address(0x05);
757        assert!(loader.raw_block_graph.contains_edge(
758            loader.raw_block_address_to_node[&block_0x05],
759            loader.raw_block_address_to_node[&block_0x17]
760        ));
761        // 0x07 -> 0x17
762        let block_0x07 = loader.find_block_start_address(0x07);
763        assert!(loader.raw_block_graph.contains_edge(
764            loader.raw_block_address_to_node[&block_0x07],
765            loader.raw_block_address_to_node[&block_0x17]
766        ));
767
768        // 0x0b -> 0x17
769        let block_0x0b = loader.find_block_start_address(0x0b);
770        assert!(loader.raw_block_graph.contains_edge(
771            loader.raw_block_address_to_node[&block_0x0b],
772            loader.raw_block_address_to_node[&block_0x17]
773        ));
774
775        // 0x0c > 0x3
776        let block_0x0c = loader.find_block_start_address(0x0c);
777        let block_0x03 = loader.find_block_start_address(0x03);
778        assert!(loader.raw_block_graph.contains_edge(
779            loader.raw_block_address_to_node[&block_0x0c],
780            loader.raw_block_address_to_node[&block_0x03]
781        ));
782
783        // 0x0c -> 0x0e
784        let block_0x0e = loader.find_block_start_address(0x0e);
785        assert!(loader.raw_block_graph.contains_edge(
786            loader.raw_block_address_to_node[&block_0x0c],
787            loader.raw_block_address_to_node[&block_0x0e]
788        ));
789
790        // 0x0e -> 0x3
791        let block_0x0e = loader.find_block_start_address(0x0e);
792        assert!(loader.raw_block_graph.contains_edge(
793            loader.raw_block_address_to_node[&block_0x0e],
794            loader.raw_block_address_to_node[&block_0x03]
795        ));
796
797        // 0x0e -> 0x10
798        let block_0x10 = loader.find_block_start_address(0x10);
799        assert!(loader.raw_block_graph.contains_edge(
800            loader.raw_block_address_to_node[&block_0x0e],
801            loader.raw_block_address_to_node[&block_0x10]
802        ));
803
804        // 0x10 -> 0x3
805        let block_0x10 = loader.find_block_start_address(0x10);
806        assert!(loader.raw_block_graph.contains_edge(
807            loader.raw_block_address_to_node[&block_0x10],
808            loader.raw_block_address_to_node[&block_0x03]
809        ));
810
811        // 0x10 -> 0x12
812        let block_0x12 = loader.find_block_start_address(0x12);
813        assert!(loader.raw_block_graph.contains_edge(
814            loader.raw_block_address_to_node[&block_0x10],
815            loader.raw_block_address_to_node[&block_0x12]
816        ));
817
818        // 0x12 -> 0x5
819        let block_0x12 = loader.find_block_start_address(0x12);
820        let block_0x05 = loader.find_block_start_address(0x05);
821        assert!(loader.raw_block_graph.contains_edge(
822            loader.raw_block_address_to_node[&block_0x12],
823            loader.raw_block_address_to_node[&block_0x05]
824        ));
825
826        // 0x12 -> 0x14
827        let block_0x14 = loader.find_block_start_address(0x14);
828        assert!(loader.raw_block_graph.contains_edge(
829            loader.raw_block_address_to_node[&block_0x12],
830            loader.raw_block_address_to_node[&block_0x14]
831        ));
832
833        // 0x14 -> 0x7
834        let block_0x14 = loader.find_block_start_address(0x14);
835        let block_0x07 = loader.find_block_start_address(0x07);
836        assert!(loader.raw_block_graph.contains_edge(
837            loader.raw_block_address_to_node[&block_0x14],
838            loader.raw_block_address_to_node[&block_0x07]
839        ));
840
841        // 0x14 -> 0x16
842        let block_0x16 = loader.find_block_start_address(0x16);
843        assert!(loader.raw_block_graph.contains_edge(
844            loader.raw_block_address_to_node[&block_0x14],
845            loader.raw_block_address_to_node[&block_0x16]
846        ));
847
848        // 0x16 -> 0xb
849        let block_0x16 = loader.find_block_start_address(0x16);
850        let block_0x0b = loader.find_block_start_address(0x0b);
851        assert!(loader.raw_block_graph.contains_edge(
852            loader.raw_block_address_to_node[&block_0x16],
853            loader.raw_block_address_to_node[&block_0x0b]
854        ));
855
856        // Compare every block start address to the expected values
857        let expected_block_starts = vec![
858            0x0, 0x1, 0x3, 0x5, 0x7, 0x9, 0xb, 0xc, 0xe, 0x10, 0x12, 0x14, 0x16, 0x17, 0x19,
859        ];
860        assert_eq!(block_starts, expected_block_starts);
861
862        // The block at address 0x09 is unreachable, so it should not have any incoming edges
863        let block_0x09 = loader.find_block_start_address(0x09);
864        assert_eq!(
865            loader
866                .raw_block_graph
867                .neighbors_directed(
868                    loader.raw_block_address_to_node[&block_0x09],
869                    petgraph::Direction::Incoming
870                )
871                .count(),
872            0
873        );
874
875        assert_eq!(block_starts.len(), 15);
876
877        // Ensure that the function map is correct
878        assert_eq!(loader.function_map.len(), 2);
879
880        // For each address, ensure that the function name is correct
881        for address in expected_block_starts.iter() {
882            match address {
883                // Start of the module
884                0 => assert_eq!(loader.get_function_name_for_address(0).unwrap(), None),
885                // Unreachable node
886                0x09 => assert!(loader.get_function_name_for_address(9).is_err()),
887                // End of the module
888                0x19 => assert_eq!(loader.get_function_name_for_address(0x19).unwrap(), None),
889                _ => assert_eq!(
890                    loader.get_function_name_for_address(*address).unwrap(),
891                    Some("main".to_string())
892                ),
893            }
894        }
895    }
896
897    #[test]
898    fn test_load_invalid_section_type() {
899        let reader = std::io::Cursor::new(vec![0x00, 0x00, 0x00, 0x05]);
900        let result = BytecodeLoaderBuilder::new(reader).build();
901        assert!(result.is_err());
902    }
903
904    #[test]
905    fn test_load_invalid_section_length() {
906        let reader = std::io::Cursor::new(vec![
907            0x00, 0x00, 0x00, 0x01, // Section type: Gs1Flags
908            0x00, 0x00, 0x00, 0x05, // Length: 5
909            0x00, 0x00, 0x00, 0x00, // Flags: 0
910        ]);
911        let result = BytecodeLoaderBuilder::new(reader).build();
912        assert!(result.is_err());
913    }
914
915    #[test]
916    fn test_fmt_section_type() {
917        assert_eq!(
918            format!("{}", super::SectionType::Gs1Flags),
919            "Gs1Flags".to_string()
920        );
921        assert_eq!(
922            format!("{}", super::SectionType::Functions),
923            "Functions".to_string()
924        );
925        assert_eq!(
926            format!("{}", super::SectionType::Strings),
927            "Strings".to_string()
928        );
929        assert_eq!(
930            format!("{}", super::SectionType::Instructions),
931            "Instructions".to_string()
932        );
933    }
934
935    #[test]
936    fn test_load_string_index_out_of_bounds() {
937        let reader = std::io::Cursor::new(vec![
938            0x00, 0x00, 0x00, 0x01, // Section type: Gs1Flags
939            0x00, 0x00, 0x00, 0x04, // Length: 4
940            0x00, 0x00, 0x00, 0x00, // Flags: 0
941            0x00, 0x00, 0x00, 0x02, // Section type: Functions
942            0x00, 0x00, 0x00, 0x09, // Length: 9
943            0x00, 0x00, 0x00, 0x00, // Function location: 0
944            0x6d, 0x61, 0x69, 0x6e, // Function name: "main"
945            0x00, // Null terminator
946            0x00, 0x00, 0x00, 0x03, // Section type: Strings
947            0x00, 0x00, 0x00, 0x04, // Length: 4
948            0x61, 0x62, 0x63, 0x00, // String: "abc"
949            0x00, 0x00, 0x00, 0x04, // Section type: Instructions
950            0x00, 0x00, 0x00, 0x0c, // Length: 12
951            0x01, // Opcode: Jmp
952            0xF3, // Opcode: ImmByte
953            0x01, // Operand: 1
954            0x14, // Opcode: PushNumber
955            0xF4, // Opcode: ImmShort
956            0x00, 0x01, // Operand: 1
957            0x15, // Opcode: PushString
958            0xF0, // Opcode: ImmStringByte
959            0x01, // Operand: 1 (out of bounds)
960            0x1b, // Opcode: PushPi
961            0x07, // Opcode: Ret
962        ]);
963
964        let result = BytecodeLoaderBuilder::new(reader).build();
965
966        assert!(result.is_err());
967    }
968
969    #[test]
970    fn test_invalid_instruction() {
971        let reader = std::io::Cursor::new(vec![
972            0x00, 0x00, 0x00, 0x01, // Section type: Gs1Flags
973            0x00, 0x00, 0x00, 0x04, // Length: 4
974            0x00, 0x00, 0x00, 0x00, // Flags: 0
975            0x00, 0x00, 0x00, 0x02, // Section type: Functions
976            0x00, 0x00, 0x00, 0x09, // Length: 9
977            0x00, 0x00, 0x00, 0x00, // Function location: 0
978            0x6d, 0x61, 0x69, 0x6e, // Function name: "main"
979            0x00, // Null terminator
980            0x00, 0x00, 0x00, 0x03, // Section type: Strings
981            0x00, 0x00, 0x00, 0x04, // Length: 4
982            0x61, 0x62, 0x63, 0x00, // String: "abc"
983            0x00, 0x00, 0x00, 0x04, // Section type: Instructions
984            0x00, 0x00, 0x00, 0x02, // Length: 2
985            0xF3, // Opcode: ImmByte
986            0x01, // Operand: 1
987        ]);
988
989        let result = BytecodeLoaderBuilder::new(reader).build();
990
991        assert!(result.is_err());
992    }
993
994    #[test]
995    fn test_load_invalid_function_section_length() {
996        let reader = std::io::Cursor::new(vec![
997            0x00, 0x00, 0x00, 0x01, // Section type: Gs1Flags
998            0x00, 0x00, 0x00, 0x04, // Length: 4
999            0x00, 0x00, 0x00, 0x00, // Flags: 0
1000            0x00, 0x00, 0x00, 0x02, // Section type: Functions
1001            0x00, 0x00, 0x00, 0x09, // Length: 9 (invalid)
1002            0x00, 0x00, 0x00, 0x00, // Function location: 0
1003            0x6d, 0x61, 0x69, 0x6e, // Function name: "main"
1004            0x41, 0x00, // "A" and Null terminator
1005            0x00, 0x00, 0x00, 0x03, // Section type: Strings
1006            0x00, 0x00, 0x00, 0x04, // Length: 4
1007            0x61, 0x62, 0x63, 0x00, // String: "abc"
1008            0x00, 0x00, 0x00, 0x04, // Section type: Instructions
1009            0x00, 0x00, 0x00, 0x0c, // Length: 12
1010            0x01, // Opcode: Jmp
1011            0xF3, // Opcode: ImmByte
1012            0x01, // Operand: 1
1013            0x14, // Opcode: PushNumber
1014            0xF4, // Opcode: ImmShort
1015            0x00, 0x01, // Operand: 1
1016            0x15, // Opcode: PushString
1017            0xF0, // Opcode: ImmStringByte
1018            0x00, // Operand: 0
1019            0x1b, // Opcode: PushPi
1020            0x07, // Opcode: Ret
1021        ]);
1022
1023        let result = BytecodeLoaderBuilder::new(reader).build();
1024
1025        assert!(result.is_err());
1026    }
1027
1028    #[test]
1029    fn test_operands() {
1030        let reader = std::io::Cursor::new(vec![
1031            0x00, 0x00, 0x00, 0x01, // Section type: Gs1Flags
1032            0x00, 0x00, 0x00, 0x04, // Length: 4
1033            0x00, 0x00, 0x00, 0x00, // Flags: 0
1034            0x00, 0x00, 0x00, 0x02, // Section type: Functions
1035            0x00, 0x00, 0x00, 0x09, // Length: 9
1036            0x00, 0x00, 0x00, 0x00, // Function location: 0
1037            0x6d, 0x61, 0x69, 0x6e, // Function name: "main"
1038            0x00, // Null terminator
1039            0x00, 0x00, 0x00, 0x03, // Section type: Strings
1040            0x00, 0x00, 0x00, 0x04, // Length: 4
1041            0x61, 0x62, 0x63, 0x00, // String: "abc"
1042            0x00, 0x00, 0x00, 0x04, // Section type: Instructions
1043            0x00, 0x00, 0x00, 0x23, // Length: 35
1044            0x01, // Opcode: Jmp
1045            0xF3, // Opcode: ImmByte
1046            0x01, // Operand: 1
1047            0x14, // Opcode: PushNumber
1048            0xF4, // Opcode: ImmShort
1049            0x00, 0x01, // Operand: 1
1050            0x14, // Opcode: PushNumber
1051            0xF5, // Opcode: ImmInt
1052            0x00, 0x00, 0x00, 0x01, // Operand: 1
1053            0x14, // Opcode: PushNumber
1054            0xF6, // Opcode: ImmFloat
1055            0x33, 0x2e, 0x31, 0x34, 0x00, // Operand: "3.14"
1056            0x15, // Opcode: PushString
1057            0xF0, // Opcode: ImmStringByte
1058            0x00, // Operand: 0
1059            0x15, // Opcode: PushString
1060            0xF1, // Opcode: ImmStringShort
1061            0x00, 0x00, // Operand: 0
1062            0x15, // Opcode: PushString
1063            0xF2, // Opcode: ImmStringInt
1064            0x00, 0x00, 0x00, 0x00, // Operand: 0
1065            0x1b, // Opcode: PushPi
1066            0x07, // Opcode: Ret
1067        ]);
1068        let loader = BytecodeLoaderBuilder::new(reader).build().unwrap();
1069
1070        assert_eq!(loader.function_map.len(), 2);
1071        assert_eq!(loader.function_map.get(&Some("main".to_string())), Some(&0));
1072        assert_eq!(loader.strings.len(), 1);
1073        assert_eq!(loader.strings.first(), Some(&"abc".to_string()));
1074        assert_eq!(loader.instructions.len(), 9);
1075
1076        assert_eq!(loader.instructions[0].opcode, crate::opcode::Opcode::Jmp);
1077        assert_eq!(
1078            loader.instructions[0].operand,
1079            Some(crate::operand::Operand::new_number(1))
1080        );
1081        assert_eq!(
1082            loader.instructions[1].opcode,
1083            crate::opcode::Opcode::PushNumber
1084        );
1085        assert_eq!(
1086            loader.instructions[1].operand,
1087            Some(crate::operand::Operand::new_number(1))
1088        );
1089        assert_eq!(
1090            loader.instructions[2].opcode,
1091            crate::opcode::Opcode::PushNumber
1092        );
1093        assert_eq!(
1094            loader.instructions[2].operand,
1095            Some(crate::operand::Operand::new_number(1))
1096        );
1097        assert_eq!(
1098            loader.instructions[3].opcode,
1099            crate::opcode::Opcode::PushNumber
1100        );
1101        assert_eq!(
1102            loader.instructions[3].operand,
1103            Some(crate::operand::Operand::new_float("3.14".to_string()))
1104        );
1105        assert_eq!(
1106            loader.instructions[4].opcode,
1107            crate::opcode::Opcode::PushString
1108        );
1109        assert_eq!(
1110            loader.instructions[4].operand,
1111            Some(crate::operand::Operand::new_string("abc"))
1112        );
1113        assert_eq!(
1114            loader.instructions[5].opcode,
1115            crate::opcode::Opcode::PushString
1116        );
1117        assert_eq!(
1118            loader.instructions[5].operand,
1119            Some(crate::operand::Operand::new_string("abc"))
1120        );
1121        assert_eq!(
1122            loader.instructions[6].opcode,
1123            crate::opcode::Opcode::PushString
1124        );
1125        assert_eq!(
1126            loader.instructions[6].operand,
1127            Some(crate::operand::Operand::new_string("abc"))
1128        );
1129        assert_eq!(loader.instructions[7].opcode, crate::opcode::Opcode::Pi);
1130        assert_eq!(loader.instructions[7].operand, None);
1131        assert_eq!(loader.instructions[8].opcode, crate::opcode::Opcode::Ret);
1132        assert_eq!(loader.instructions[8].operand, None);
1133    }
1134
1135    #[test]
1136    fn test_start_block_addresses() {
1137        let reader = std::io::Cursor::new(vec![
1138            0x00, 0x00, 0x00, 0x01, // Section type: Gs1Flags
1139            0x00, 0x00, 0x00, 0x04, // Length: 4
1140            0x00, 0x00, 0x00, 0x00, // Flags: 0
1141            0x00, 0x00, 0x00, 0x02, // Section type: Functions
1142            0x00, 0x00, 0x00, 0x09, // Length: 9
1143            0x00, 0x00, 0x00, 0x03, // Function location: 3
1144            0x6d, 0x61, 0x69, 0x6e, // Function name: "main"
1145            0x00, // Null terminator
1146            0x00, 0x00, 0x00, 0x03, // Section type: Strings
1147            0x00, 0x00, 0x00, 0x04, // Length: 4
1148            0x41, 0x42, 0x43, 0x00, // String: "ABC"
1149            0x00, 0x00, 0x00, 0x04, // Section type: Instructions
1150            0x00, 0x00, 0x00, 0x26, // Length: 38
1151            0x01, // Opcode: Jmp
1152            0xF3, // Opcode: ImmByte
1153            0x05, // Operand: 5
1154            0x14, // Opcode: PushNumber
1155            0xF4, // Opcode: ImmShort
1156            0x00, 0x01, // Operand: 1
1157            0x14, // Opcode: PushNumber
1158            0xF5, // Opcode: ImmInt
1159            0x00, 0x00, 0x00, 0x01, // Operand: 1
1160            0x14, // Opcode: PushNumber
1161            0xF6, // Opcode: ImmFloat
1162            0x33, 0x2e, 0x31, 0x34, 0x00, // Operand: "3.14"
1163            0x15, // Opcode: PushString
1164            0xF0, // Opcode: ImmStringByte
1165            0x00, // Operand: 0
1166            0x15, // Opcode: PushString
1167            0xF1, // Opcode: ImmStringShort
1168            0x00, 0x00, // Operand: 0
1169            0x02, // Opcode: Jeq
1170            0xF3, // Opcode: ImmByte
1171            0x02, // Operand: 2
1172            0x15, // Opcode: PushString
1173            0xF2, // Opcode: ImmStringInt
1174            0x00, 0x00, 0x00, 0x00, // Operand: 0
1175            0x1b, // Opcode: PushPi
1176            0x07, // Opcode: Ret
1177        ]);
1178        let loader = BytecodeLoaderBuilder::new(reader).build().unwrap();
1179
1180        assert_eq!(loader.block_breaks.len(), 7);
1181        assert!(loader.block_breaks.contains(&0));
1182        assert!(loader.block_breaks.contains(&1));
1183        assert!(loader.block_breaks.contains(&2));
1184        assert!(loader.block_breaks.contains(&3));
1185        assert!(loader.block_breaks.contains(&5));
1186        assert!(loader.block_breaks.contains(&7));
1187        assert!(loader.block_breaks.contains(&10));
1188    }
1189
1190    #[test]
1191    fn test_invalid_blocks() {
1192        let reader = std::io::Cursor::new(vec![
1193            0x00, 0x00, 0x00, 0x01, // Section type: Gs1Flags
1194            0x00, 0x00, 0x00, 0x04, // Length: 4
1195            0x00, 0x00, 0x00, 0x00, // Flags: 0
1196            0x00, 0x00, 0x00, 0x02, // Section type: Functions
1197            0x00, 0x00, 0x00, 0x09, // Length: 9
1198            0x00, 0x00, 0x00, 0x00, // Function location: 0
1199            0x6d, 0x61, 0x69, 0x6e, // Function name: "main"
1200            0x00, // Null terminator
1201            0x00, 0x00, 0x00, 0x03, // Section type: Strings
1202            0x00, 0x00, 0x00, 0x04, // Length: 4
1203            0x41, 0x42, 0x43, 0x00, // String: "ABC"
1204            0x00, 0x00, 0x00, 0x04, // Section type: Instructions
1205            0x00, 0x00, 0x00, 0x26, // Length: 38
1206            0x01, // Opcode: Jmp
1207            0xF3, // Opcode: ImmByte
1208            0x05, // Operand: 5
1209            0x14, // Opcode: PushNumber
1210            0xF4, // Opcode: ImmShort
1211            0x00, 0x01, // Operand: 1
1212            0x14, // Opcode: PushNumber
1213            0xF5, // Opcode: ImmInt
1214            0x00, 0x00, 0x00, 0x01, // Operand: 1
1215            0x14, // Opcode: PushNumber
1216            0xF6, // Opcode: ImmFloat
1217            0x33, 0x2e, 0x31, 0x34, 0x00, // Operand: "3.14"
1218            0x15, // Opcode: PushString
1219            0xF0, // Opcode: ImmStringByte
1220            0x00, // Operand: 0
1221            0x15, // Opcode: PushString
1222            0xF1, // Opcode: ImmStringShort
1223            0x00, 0x00, // Operand: 0
1224            0x02, // Opcode: Jeq
1225            0xF3, // Opcode: ImmByte
1226            0xFF, // Operand: FF (invalid)
1227            0x15, // Opcode: PushString
1228            0xF2, // Opcode: ImmStringInt
1229            0x00, 0x00, 0x00, 0x00, // Operand: 0
1230            0x1b, // Opcode: PushPi
1231            0x07, // Opcode: Ret
1232        ]);
1233
1234        // print instructions
1235        let loader = BytecodeLoaderBuilder::new(reader).build();
1236        assert!(loader.is_err());
1237    }
1238}