Skip to main content

bind/bytecode_encoder/
encode_v2.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::bytecode_constants::*;
6use crate::bytecode_encoder::error::BindRulesEncodeError;
7use crate::bytecode_encoder::instruction_encoder::encode_instructions;
8use crate::bytecode_encoder::symbol_table_encoder::SymbolTableEncoder;
9use crate::compiler::{BindRules, CompositeBindRules, CompositeParent};
10use std::collections::HashSet;
11
12/// Functions for encoding the new bytecode format. When the
13/// old bytecode format is deleted, the "v2" should be removed from the names.
14
15pub fn encode_to_bytecode_v2(bind_rules: BindRules) -> Result<Vec<u8>, BindRulesEncodeError> {
16    let mut debug_symbol_table_encoder_option =
17        if bind_rules.enable_debug { Some(SymbolTableEncoder::new()) } else { None };
18
19    let mut symbol_table_encoder = SymbolTableEncoder::new();
20    let mut instruction_bytecode = encode_instructions(
21        bind_rules.instructions,
22        &mut symbol_table_encoder,
23        &mut debug_symbol_table_encoder_option,
24    )?;
25
26    let mut bytecode: Vec<u8> = vec![];
27
28    // Encode the header.
29    bytecode.extend_from_slice(&BIND_MAGIC_NUM.to_be_bytes());
30    bytecode.extend_from_slice(&BYTECODE_VERSION.to_le_bytes());
31    if bind_rules.enable_debug {
32        bytecode.push(BYTECODE_ENABLE_DEBUG);
33    } else {
34        bytecode.push(BYTECODE_DISABLE_DEBUG);
35    }
36
37    // Encode the symbol table.
38    bytecode.extend_from_slice(&SYMB_MAGIC_NUM.to_be_bytes());
39    bytecode.extend_from_slice(&(symbol_table_encoder.bytecode.len() as u32).to_le_bytes());
40    bytecode.append(&mut symbol_table_encoder.bytecode);
41
42    // Encode the instruction section.
43    bytecode.extend_from_slice(&INSTRUCTION_MAGIC_NUM.to_be_bytes());
44    bytecode.extend_from_slice(&(instruction_bytecode.len() as u32).to_le_bytes());
45    bytecode.append(&mut instruction_bytecode);
46
47    // Encode the debug section when enable_debug flag is true.
48    if let Some(mut debug_symbol_table_encoder) = debug_symbol_table_encoder_option {
49        encode_debug_section(&mut bytecode, &mut debug_symbol_table_encoder)?;
50    }
51
52    Ok(bytecode)
53}
54
55pub fn encode_to_string_v2(bind_rules: BindRules) -> Result<(String, usize), BindRulesEncodeError> {
56    let result = encode_to_bytecode_v2(bind_rules)?;
57    let byte_count = result.len();
58    Ok((
59        result.into_iter().map(|byte| format!("{:#x}", byte)).collect::<Vec<String>>().join(","),
60        byte_count,
61    ))
62}
63
64fn encode_debug_section(
65    bytecode: &mut Vec<u8>,
66    debug_symbol_table_encoder: &mut SymbolTableEncoder,
67) -> Result<(), BindRulesEncodeError> {
68    // Debug information header
69    bytecode.extend_from_slice(&DEBG_MAGIC_NUM.to_be_bytes());
70    if debug_symbol_table_encoder.bytecode.len() == 0 {
71        bytecode
72            .extend_from_slice(&(debug_symbol_table_encoder.bytecode.len() as u32).to_le_bytes());
73    } else {
74        // Extend the debug information section to include the
75        // length of the debug symbol table bytecode and header
76        bytecode.extend_from_slice(
77            &((debug_symbol_table_encoder.bytecode.len() + HEADER_SZ) as u32).to_le_bytes(),
78        );
79        // Debug symbol table section
80        bytecode.extend_from_slice(&DBSY_MAGIC_NUM.to_be_bytes());
81        bytecode
82            .extend_from_slice(&(debug_symbol_table_encoder.bytecode.len() as u32).to_le_bytes());
83        bytecode.append(&mut debug_symbol_table_encoder.bytecode);
84    }
85
86    Ok(())
87}
88
89fn append_composite_parent(
90    bytecode: &mut Vec<u8>,
91    parent: CompositeParent,
92    parent_type: RawParentType,
93    symbol_table_encoder: &mut SymbolTableEncoder,
94    debug_symbol_table_encoder: &mut Option<SymbolTableEncoder>,
95) -> Result<(), BindRulesEncodeError> {
96    if parent.name.is_empty() {
97        return Err(BindRulesEncodeError::MissingCompositeParentName);
98    }
99
100    bytecode.push(parent_type as u8);
101    bytecode.extend_from_slice(&symbol_table_encoder.get_key(parent.name)?.to_le_bytes());
102
103    let mut inst_bytecode =
104        encode_instructions(parent.instructions, symbol_table_encoder, debug_symbol_table_encoder)?;
105    bytecode.extend_from_slice(&(inst_bytecode.len() as u32).to_le_bytes());
106    bytecode.append(&mut inst_bytecode);
107    Ok(())
108}
109
110pub fn encode_composite_to_bytecode(
111    bind_rules: CompositeBindRules,
112) -> Result<Vec<u8>, BindRulesEncodeError> {
113    let mut symbol_table_encoder = SymbolTableEncoder::new();
114    let mut debug_symbol_table_encoder_option =
115        if bind_rules.enable_debug { Some(SymbolTableEncoder::new()) } else { None };
116
117    if bind_rules.device_name.is_empty() {
118        return Err(BindRulesEncodeError::MissingCompositeDeviceName);
119    }
120
121    // Instruction bytecode begins with the device name ID.
122    let device_name_id = symbol_table_encoder.get_key(bind_rules.device_name)?;
123    let mut inst_bytecode = device_name_id.to_le_bytes().to_vec();
124
125    let mut parent_names = HashSet::new();
126
127    // Add instructions from the primary parent.
128    parent_names.insert(bind_rules.primary_parent.name.clone());
129    append_composite_parent(
130        &mut inst_bytecode,
131        bind_rules.primary_parent,
132        RawParentType::Primary,
133        &mut symbol_table_encoder,
134        &mut debug_symbol_table_encoder_option,
135    )?;
136
137    // Add instructions from additional parents.
138    for parent in bind_rules.additional_parents.into_iter() {
139        if parent_names.contains(&parent.name) {
140            return Err(BindRulesEncodeError::DuplicateCompositeParentName(parent.name));
141        }
142        parent_names.insert(parent.name.clone());
143        append_composite_parent(
144            &mut inst_bytecode,
145            parent,
146            RawParentType::Additional,
147            &mut symbol_table_encoder,
148            &mut debug_symbol_table_encoder_option,
149        )?;
150    }
151
152    // Add instructions from optional parents.
153    for parent in bind_rules.optional_parents.into_iter() {
154        if parent_names.contains(&parent.name) {
155            return Err(BindRulesEncodeError::DuplicateCompositeParentName(parent.name));
156        }
157        parent_names.insert(parent.name.clone());
158        append_composite_parent(
159            &mut inst_bytecode,
160            parent,
161            RawParentType::Optional,
162            &mut symbol_table_encoder,
163            &mut debug_symbol_table_encoder_option,
164        )?;
165    }
166
167    // Put all of the sections together.
168    let mut bytecode: Vec<u8> = vec![];
169
170    // Encode the header.
171    bytecode.extend_from_slice(&BIND_MAGIC_NUM.to_be_bytes());
172    bytecode.extend_from_slice(&BYTECODE_VERSION.to_le_bytes());
173    if bind_rules.enable_debug {
174        bytecode.push(BYTECODE_ENABLE_DEBUG);
175    } else {
176        bytecode.push(BYTECODE_DISABLE_DEBUG);
177    }
178
179    // Encode the symbol table.
180    bytecode.extend_from_slice(&SYMB_MAGIC_NUM.to_be_bytes());
181    bytecode.extend_from_slice(&(symbol_table_encoder.bytecode.len() as u32).to_le_bytes());
182    bytecode.append(&mut symbol_table_encoder.bytecode);
183
184    // Encode the instruction section.
185    bytecode.extend_from_slice(&COMPOSITE_MAGIC_NUM.to_be_bytes());
186    bytecode.extend_from_slice(&(inst_bytecode.len() as u32).to_le_bytes());
187    bytecode.append(&mut inst_bytecode);
188
189    // Encode the debug section when enable_debug flag is true.
190    if let Some(mut debug_symbol_table_encoder) = debug_symbol_table_encoder_option {
191        encode_debug_section(&mut bytecode, &mut debug_symbol_table_encoder)?;
192    }
193
194    Ok(bytecode)
195}
196
197#[cfg(test)]
198mod test {
199    use super::*;
200    use crate::bytecode_encoder::bytecode_checker::*;
201    use crate::compiler::{Symbol, SymbolicInstruction, SymbolicInstructionInfo};
202    use crate::parser::bind_library::ValueType;
203    use std::collections::HashMap;
204
205    // Converts a vector of SymbolicInstruction into a vector of SymbolicInstructionInfo.
206    // The location value for each element is set to None.
207    fn to_symbolic_inst_info<'a>(
208        instructions: Vec<SymbolicInstruction>,
209    ) -> Vec<SymbolicInstructionInfo<'a>> {
210        instructions
211            .into_iter()
212            .map(|inst| SymbolicInstructionInfo { location: None, instruction: inst })
213            .collect()
214    }
215
216    fn composite_parent<'a>(
217        name: String,
218        instructions: Vec<SymbolicInstruction>,
219    ) -> CompositeParent<'a> {
220        CompositeParent { name: name, instructions: to_symbolic_inst_info(instructions) }
221    }
222
223    #[test]
224    fn test_string_values() {
225        let instructions = vec![
226            SymbolicInstruction::AbortIfNotEqual {
227                lhs: Symbol::DeprecatedKey(5),
228                rhs: Symbol::StringValue("shoveler".to_string()),
229            },
230            SymbolicInstruction::AbortIfEqual {
231                lhs: Symbol::DeprecatedKey(1),
232                rhs: Symbol::BoolValue(false),
233            },
234            SymbolicInstruction::JumpIfEqual {
235                lhs: Symbol::DeprecatedKey(15),
236                rhs: Symbol::StringValue("canvasback".to_string()),
237                label: 1,
238            },
239            SymbolicInstruction::AbortIfEqual {
240                lhs: Symbol::DeprecatedKey(2),
241                rhs: Symbol::StringValue("canvasback".to_string()),
242            },
243            SymbolicInstruction::Label(1),
244            SymbolicInstruction::AbortIfEqual {
245                lhs: Symbol::Key("bufflehead".to_string(), ValueType::Number),
246                rhs: Symbol::NumberValue(1),
247            },
248            SymbolicInstruction::AbortIfEqual {
249                lhs: Symbol::Key("pintail".to_string(), ValueType::Enum),
250                rhs: Symbol::EnumValue("mallard".to_string()),
251            },
252        ];
253
254        let bind_rules = BindRules {
255            instructions: to_symbolic_inst_info(instructions),
256            symbol_table: HashMap::new(),
257            use_new_bytecode: true,
258            enable_debug: false,
259        };
260
261        let mut checker = BytecodeChecker::new(encode_to_bytecode_v2(bind_rules).unwrap());
262        checker.verify_bind_rules_header(false);
263        checker.verify_sym_table_header(67);
264
265        checker.verify_symbol_table(&[
266            "shoveler",
267            "canvasback",
268            "bufflehead",
269            "pintail",
270            "mallard",
271        ]);
272
273        checker.verify_instructions_header((COND_ABORT_BYTES * 5) + COND_JMP_BYTES + JMP_PAD_BYTES);
274        checker.verify_abort_not_equal(
275            EncodedValue { value_type: RawValueType::NumberValue, value: 5 },
276            EncodedValue { value_type: RawValueType::StringValue, value: 1 },
277        );
278        checker.verify_abort_equal(
279            EncodedValue { value_type: RawValueType::NumberValue, value: 1 },
280            EncodedValue { value_type: RawValueType::BoolValue, value: 0 },
281        );
282        checker.verify_jmp_if_equal(
283            COND_ABORT_BYTES,
284            EncodedValue { value_type: RawValueType::NumberValue, value: 15 },
285            EncodedValue { value_type: RawValueType::StringValue, value: 2 },
286        );
287        checker.verify_abort_equal(
288            EncodedValue { value_type: RawValueType::NumberValue, value: 2 },
289            EncodedValue { value_type: RawValueType::StringValue, value: 2 },
290        );
291        checker.verify_jmp_pad();
292        checker.verify_abort_equal(
293            EncodedValue { value_type: RawValueType::Key, value: 3 },
294            EncodedValue { value_type: RawValueType::NumberValue, value: 1 },
295        );
296        checker.verify_abort_equal(
297            EncodedValue { value_type: RawValueType::Key, value: 4 },
298            EncodedValue { value_type: RawValueType::EnumValue, value: 5 },
299        );
300        checker.verify_end();
301    }
302
303    #[test]
304    fn test_empty_symbol_table() {
305        let bind_rules = BindRules {
306            instructions: to_symbolic_inst_info(vec![SymbolicInstruction::UnconditionalAbort]),
307            symbol_table: HashMap::new(),
308            use_new_bytecode: true,
309            enable_debug: false,
310        };
311
312        let mut checker = BytecodeChecker::new(encode_to_bytecode_v2(bind_rules).unwrap());
313
314        checker.verify_bind_rules_header(false);
315        checker.verify_sym_table_header(0);
316        checker.verify_instructions_header(UNCOND_ABORT_BYTES);
317        checker.verify_unconditional_abort();
318        checker.verify_end();
319    }
320
321    #[test]
322    fn test_enable_debug_flag_missing_ast_location() {
323        let bind_rules = BindRules {
324            instructions: to_symbolic_inst_info(vec![SymbolicInstruction::UnconditionalAbort]),
325            symbol_table: HashMap::new(),
326            use_new_bytecode: true,
327            enable_debug: true,
328        };
329
330        assert_eq!(
331            Err(BindRulesEncodeError::MissingAstLocation),
332            encode_to_bytecode_v2(bind_rules)
333        );
334    }
335
336    #[test]
337    fn test_enable_debug_flag_false() {
338        let bind_rules = BindRules {
339            instructions: to_symbolic_inst_info(vec![SymbolicInstruction::UnconditionalAbort]),
340            symbol_table: HashMap::new(),
341            use_new_bytecode: true,
342            enable_debug: false,
343        };
344
345        let mut checker = BytecodeChecker::new(encode_to_bytecode_v2(bind_rules).unwrap());
346
347        checker.verify_bind_rules_header(false);
348        checker.verify_sym_table_header(0);
349        checker.verify_instructions_header(UNCOND_ABORT_BYTES);
350        checker.verify_unconditional_abort();
351        checker.verify_end();
352    }
353
354    #[test]
355    fn test_duplicate_symbols() {
356        let instructions = vec![
357            SymbolicInstruction::AbortIfNotEqual {
358                lhs: Symbol::DeprecatedKey(5),
359                rhs: Symbol::StringValue("puffleg".to_string()),
360            },
361            SymbolicInstruction::AbortIfEqual {
362                lhs: Symbol::Key("sunangel".to_string(), ValueType::Number),
363                rhs: Symbol::NumberValue(1),
364            },
365            SymbolicInstruction::AbortIfEqual {
366                lhs: Symbol::Key("puffleg".to_string(), ValueType::Number),
367                rhs: Symbol::NumberValue(1),
368            },
369            SymbolicInstruction::AbortIfNotEqual {
370                lhs: Symbol::DeprecatedKey(5),
371                rhs: Symbol::StringValue("sunangel".to_string()),
372            },
373            SymbolicInstruction::AbortIfEqual {
374                lhs: Symbol::Key("puffleg".to_string(), ValueType::Str),
375                rhs: Symbol::StringValue("sunangel".to_string()),
376            },
377            SymbolicInstruction::AbortIfEqual {
378                lhs: Symbol::Key("mountaingem".to_string(), ValueType::Str),
379                rhs: Symbol::StringValue("mountaingem".to_string()),
380            },
381        ];
382
383        let bind_rules = BindRules {
384            instructions: to_symbolic_inst_info(instructions),
385            symbol_table: HashMap::new(),
386            use_new_bytecode: true,
387            enable_debug: false,
388        };
389
390        let mut checker = BytecodeChecker::new(encode_to_bytecode_v2(bind_rules).unwrap());
391        checker.verify_bind_rules_header(false);
392        checker.verify_sym_table_header(41);
393
394        checker.verify_symbol_table(&["puffleg", "sunangel", "mountaingem"]);
395
396        checker.verify_instructions_header(COND_ABORT_BYTES * 6);
397        checker.verify_abort_not_equal(
398            EncodedValue { value_type: RawValueType::NumberValue, value: 5 },
399            EncodedValue { value_type: RawValueType::StringValue, value: 1 },
400        );
401        checker.verify_abort_equal(
402            EncodedValue { value_type: RawValueType::Key, value: 2 },
403            EncodedValue { value_type: RawValueType::NumberValue, value: 1 },
404        );
405        checker.verify_abort_equal(
406            EncodedValue { value_type: RawValueType::Key, value: 1 },
407            EncodedValue { value_type: RawValueType::NumberValue, value: 1 },
408        );
409        checker.verify_abort_not_equal(
410            EncodedValue { value_type: RawValueType::NumberValue, value: 5 },
411            EncodedValue { value_type: RawValueType::StringValue, value: 2 },
412        );
413        checker.verify_abort_equal(
414            EncodedValue { value_type: RawValueType::Key, value: 1 },
415            EncodedValue { value_type: RawValueType::StringValue, value: 2 },
416        );
417        checker.verify_abort_equal(
418            EncodedValue { value_type: RawValueType::Key, value: 3 },
419            EncodedValue { value_type: RawValueType::StringValue, value: 3 },
420        );
421    }
422
423    #[test]
424    fn test_long_string() {
425        let long_str = "loooooooooooooooooooooooooo\
426            oooooooooooooooooooooooooooo\
427            ooooooooooooooooooooooooooo\
428            ooooooooooooooooooooooong, \
429            loooooooooooooooooooooooooo\
430            ooooooooooooooooooooooooooo\
431            ooooooooooooooooooooooooooo\
432            ooooooooooooooooooooooooooo\
433            ooooooooooooooooooooooooooo\
434            oooooooong string";
435
436        let instructions = vec![SymbolicInstruction::AbortIfNotEqual {
437            lhs: Symbol::DeprecatedKey(5),
438            rhs: Symbol::StringValue(long_str.to_string()),
439        }];
440
441        let bind_rules = BindRules {
442            instructions: to_symbolic_inst_info(instructions),
443            symbol_table: HashMap::new(),
444            use_new_bytecode: true,
445            enable_debug: false,
446        };
447
448        assert_eq!(
449            Err(BindRulesEncodeError::InvalidStringLength(long_str.to_string())),
450            encode_to_bytecode_v2(bind_rules)
451        );
452    }
453
454    #[test]
455    fn test_abort_instructions() {
456        let instructions = vec![
457            SymbolicInstruction::AbortIfNotEqual {
458                lhs: Symbol::DeprecatedKey(5),
459                rhs: Symbol::NumberValue(100),
460            },
461            SymbolicInstruction::AbortIfEqual {
462                lhs: Symbol::DeprecatedKey(1),
463                rhs: Symbol::BoolValue(false),
464            },
465        ];
466
467        let bind_rules = BindRules {
468            instructions: to_symbolic_inst_info(instructions),
469            symbol_table: HashMap::new(),
470            use_new_bytecode: true,
471            enable_debug: false,
472        };
473
474        let mut checker = BytecodeChecker::new(encode_to_bytecode_v2(bind_rules).unwrap());
475        checker.verify_bind_rules_header(false);
476        checker.verify_sym_table_header(0);
477
478        checker.verify_instructions_header(COND_ABORT_BYTES + COND_ABORT_BYTES);
479        checker.verify_abort_not_equal(
480            EncodedValue { value_type: RawValueType::NumberValue, value: 5 },
481            EncodedValue { value_type: RawValueType::NumberValue, value: 100 },
482        );
483        checker.verify_abort_equal(
484            EncodedValue { value_type: RawValueType::NumberValue, value: 1 },
485            EncodedValue { value_type: RawValueType::BoolValue, value: 0 },
486        );
487        checker.verify_end();
488    }
489
490    #[test]
491    fn test_unconditional_jump_statement() {
492        let instructions = vec![
493            SymbolicInstruction::UnconditionalJump { label: 1 },
494            SymbolicInstruction::UnconditionalAbort,
495            SymbolicInstruction::Label(1),
496        ];
497
498        let bind_rules = BindRules {
499            instructions: to_symbolic_inst_info(instructions),
500            symbol_table: HashMap::new(),
501            use_new_bytecode: true,
502            enable_debug: false,
503        };
504
505        let mut checker = BytecodeChecker::new(encode_to_bytecode_v2(bind_rules).unwrap());
506        checker.verify_bind_rules_header(false);
507        checker.verify_sym_table_header(0);
508
509        checker.verify_instructions_header(UNCOND_JMP_BYTES + UNCOND_ABORT_BYTES + JMP_PAD_BYTES);
510        checker.verify_unconditional_jmp(UNCOND_ABORT_BYTES);
511        checker.verify_unconditional_abort();
512        checker.verify_jmp_pad();
513        checker.verify_end();
514    }
515
516    #[test]
517    fn test_jump_if_equal_statement() {
518        let instructions = vec![
519            SymbolicInstruction::JumpIfEqual {
520                lhs: Symbol::DeprecatedKey(15),
521                rhs: Symbol::NumberValue(12),
522                label: 1,
523            },
524            SymbolicInstruction::UnconditionalAbort,
525            SymbolicInstruction::AbortIfEqual {
526                lhs: Symbol::DeprecatedKey(10),
527                rhs: Symbol::NumberValue(10),
528            },
529            SymbolicInstruction::Label(1),
530        ];
531
532        let bind_rules = BindRules {
533            instructions: to_symbolic_inst_info(instructions),
534            symbol_table: HashMap::new(),
535            use_new_bytecode: true,
536            enable_debug: false,
537        };
538
539        let mut checker = BytecodeChecker::new(encode_to_bytecode_v2(bind_rules).unwrap());
540        checker.verify_bind_rules_header(false);
541        checker.verify_sym_table_header(0);
542
543        // Verify the instructions.
544        checker.verify_instructions_header(
545            COND_JMP_BYTES + UNCOND_ABORT_BYTES + COND_ABORT_BYTES + JMP_PAD_BYTES,
546        );
547        checker.verify_jmp_if_equal(
548            UNCOND_ABORT_BYTES + COND_ABORT_BYTES,
549            EncodedValue { value_type: RawValueType::NumberValue, value: 15 },
550            EncodedValue { value_type: RawValueType::NumberValue, value: 12 },
551        );
552        checker.verify_unconditional_abort();
553        checker.verify_abort_equal(
554            EncodedValue { value_type: RawValueType::NumberValue, value: 10 },
555            EncodedValue { value_type: RawValueType::NumberValue, value: 10 },
556        );
557        checker.verify_jmp_pad();
558
559        checker.verify_end();
560    }
561
562    #[test]
563    fn test_jump_if_not_equal_statement() {
564        let instructions = vec![
565            SymbolicInstruction::JumpIfNotEqual {
566                lhs: Symbol::DeprecatedKey(15),
567                rhs: Symbol::BoolValue(true),
568                label: 2,
569            },
570            SymbolicInstruction::UnconditionalAbort,
571            SymbolicInstruction::AbortIfEqual {
572                lhs: Symbol::DeprecatedKey(5),
573                rhs: Symbol::NumberValue(7),
574            },
575            SymbolicInstruction::AbortIfNotEqual {
576                lhs: Symbol::DeprecatedKey(2),
577                rhs: Symbol::BoolValue(true),
578            },
579            SymbolicInstruction::UnconditionalAbort,
580            SymbolicInstruction::Label(2),
581        ];
582
583        let bind_rules = BindRules {
584            instructions: to_symbolic_inst_info(instructions),
585            symbol_table: HashMap::new(),
586            use_new_bytecode: true,
587            enable_debug: false,
588        };
589
590        let mut checker = BytecodeChecker::new(encode_to_bytecode_v2(bind_rules).unwrap());
591        checker.verify_bind_rules_header(false);
592        checker.verify_sym_table_header(0);
593
594        let expected_bytes =
595            COND_JMP_BYTES + (UNCOND_ABORT_BYTES * 2) + (COND_ABORT_BYTES * 2) + JMP_PAD_BYTES;
596        checker.verify_instructions_header(expected_bytes);
597
598        // Verify Jump If Not Equal.
599        let expected_offset = (UNCOND_ABORT_BYTES * 2) + (COND_ABORT_BYTES * 2);
600        checker.verify_jmp_if_not_equal(
601            expected_offset,
602            EncodedValue { value_type: RawValueType::NumberValue, value: 15 },
603            EncodedValue { value_type: RawValueType::BoolValue, value: 1 },
604        );
605
606        // Verify abort statements.
607        checker.verify_unconditional_abort();
608        checker.verify_abort_equal(
609            EncodedValue { value_type: RawValueType::NumberValue, value: 5 },
610            EncodedValue { value_type: RawValueType::NumberValue, value: 7 },
611        );
612        checker.verify_abort_not_equal(
613            EncodedValue { value_type: RawValueType::NumberValue, value: 2 },
614            EncodedValue { value_type: RawValueType::BoolValue, value: 1 },
615        );
616        checker.verify_unconditional_abort();
617
618        checker.verify_jmp_pad();
619        checker.verify_end();
620    }
621
622    #[test]
623    fn test_nested_jump_statement() {
624        let instructions = vec![
625            SymbolicInstruction::JumpIfEqual {
626                lhs: Symbol::DeprecatedKey(10),
627                rhs: Symbol::NumberValue(11),
628                label: 1,
629            },
630            SymbolicInstruction::UnconditionalAbort,
631            SymbolicInstruction::UnconditionalJump { label: 2 },
632            SymbolicInstruction::AbortIfEqual {
633                lhs: Symbol::DeprecatedKey(5),
634                rhs: Symbol::NumberValue(7),
635            },
636            SymbolicInstruction::UnconditionalAbort,
637            SymbolicInstruction::Label(2),
638            SymbolicInstruction::UnconditionalAbort,
639            SymbolicInstruction::Label(1),
640        ];
641
642        let bind_rules = BindRules {
643            instructions: to_symbolic_inst_info(instructions),
644            symbol_table: HashMap::new(),
645            use_new_bytecode: true,
646            enable_debug: false,
647        };
648
649        let mut checker = BytecodeChecker::new(encode_to_bytecode_v2(bind_rules).unwrap());
650
651        checker.verify_bind_rules_header(false);
652        checker.verify_sym_table_header(0);
653
654        // Verify the instructions.
655        let nested_jmp_block_bytes =
656            UNCOND_JMP_BYTES + COND_ABORT_BYTES + UNCOND_ABORT_BYTES + JMP_PAD_BYTES;
657        let instructions_bytes = COND_JMP_BYTES
658            + UNCOND_ABORT_BYTES
659            + nested_jmp_block_bytes
660            + UNCOND_ABORT_BYTES
661            + JMP_PAD_BYTES;
662        checker.verify_instructions_header(instructions_bytes);
663
664        // Verify Jump If Equal.
665        let jmp_offset = UNCOND_ABORT_BYTES + nested_jmp_block_bytes + UNCOND_ABORT_BYTES;
666        checker.verify_jmp_if_equal(
667            jmp_offset,
668            EncodedValue { value_type: RawValueType::NumberValue, value: 10 },
669            EncodedValue { value_type: RawValueType::NumberValue, value: 11 },
670        );
671        checker.verify_unconditional_abort();
672
673        // Verify the nested jump block.
674        checker.verify_unconditional_jmp(COND_ABORT_BYTES + UNCOND_ABORT_BYTES);
675        checker.verify_abort_equal(
676            EncodedValue { value_type: RawValueType::NumberValue, value: 5 },
677            EncodedValue { value_type: RawValueType::NumberValue, value: 7 },
678        );
679        checker.verify_unconditional_abort();
680        checker.verify_jmp_pad();
681
682        checker.verify_unconditional_abort();
683        checker.verify_jmp_pad();
684        checker.verify_end();
685    }
686
687    #[test]
688    fn test_overlapping_jump_statements() {
689        let instructions = vec![
690            SymbolicInstruction::JumpIfEqual {
691                lhs: Symbol::DeprecatedKey(10),
692                rhs: Symbol::NumberValue(11),
693                label: 1,
694            },
695            SymbolicInstruction::UnconditionalAbort,
696            SymbolicInstruction::UnconditionalJump { label: 2 },
697            SymbolicInstruction::AbortIfEqual {
698                lhs: Symbol::DeprecatedKey(5),
699                rhs: Symbol::NumberValue(7),
700            },
701            SymbolicInstruction::Label(1),
702            SymbolicInstruction::UnconditionalAbort,
703            SymbolicInstruction::Label(2),
704        ];
705
706        let bind_rules = BindRules {
707            instructions: to_symbolic_inst_info(instructions),
708            symbol_table: HashMap::new(),
709            use_new_bytecode: true,
710            enable_debug: false,
711        };
712
713        let mut checker = BytecodeChecker::new(encode_to_bytecode_v2(bind_rules).unwrap());
714        checker.verify_bind_rules_header(false);
715        checker.verify_sym_table_header(0);
716
717        let instructions_bytes = COND_JMP_BYTES
718            + UNCOND_ABORT_BYTES
719            + UNCOND_JMP_BYTES
720            + COND_ABORT_BYTES
721            + JMP_PAD_BYTES
722            + UNCOND_ABORT_BYTES
723            + JMP_PAD_BYTES;
724        checker.verify_instructions_header(instructions_bytes);
725
726        let jmp_offset = UNCOND_ABORT_BYTES + UNCOND_JMP_BYTES + COND_ABORT_BYTES;
727        checker.verify_jmp_if_equal(
728            jmp_offset,
729            EncodedValue { value_type: RawValueType::NumberValue, value: 10 },
730            EncodedValue { value_type: RawValueType::NumberValue, value: 11 },
731        );
732        checker.verify_unconditional_abort();
733
734        let jmp_offset = COND_ABORT_BYTES + JMP_PAD_BYTES + UNCOND_ABORT_BYTES;
735        checker.verify_unconditional_jmp(jmp_offset);
736        checker.verify_abort_equal(
737            EncodedValue { value_type: RawValueType::NumberValue, value: 5 },
738            EncodedValue { value_type: RawValueType::NumberValue, value: 7 },
739        );
740
741        checker.verify_jmp_pad();
742        checker.verify_unconditional_abort();
743        checker.verify_jmp_pad();
744
745        checker.verify_end();
746    }
747
748    #[test]
749    fn test_same_label_statements() {
750        let instructions = vec![
751            SymbolicInstruction::UnconditionalJump { label: 1 },
752            SymbolicInstruction::AbortIfEqual {
753                lhs: Symbol::DeprecatedKey(5),
754                rhs: Symbol::NumberValue(7),
755            },
756            SymbolicInstruction::JumpIfEqual {
757                lhs: Symbol::DeprecatedKey(10),
758                rhs: Symbol::NumberValue(11),
759                label: 1,
760            },
761            SymbolicInstruction::UnconditionalAbort,
762            SymbolicInstruction::Label(1),
763        ];
764
765        let bind_rules = BindRules {
766            instructions: to_symbolic_inst_info(instructions),
767            symbol_table: HashMap::new(),
768            use_new_bytecode: true,
769            enable_debug: false,
770        };
771
772        let mut checker = BytecodeChecker::new(encode_to_bytecode_v2(bind_rules).unwrap());
773        checker.verify_bind_rules_header(false);
774        checker.verify_sym_table_header(0);
775
776        let instructions_bytes = UNCOND_JMP_BYTES
777            + COND_ABORT_BYTES
778            + COND_JMP_BYTES
779            + UNCOND_ABORT_BYTES
780            + JMP_PAD_BYTES;
781        checker.verify_instructions_header(instructions_bytes);
782
783        checker.verify_unconditional_jmp(COND_ABORT_BYTES + COND_JMP_BYTES + UNCOND_ABORT_BYTES);
784        checker.verify_abort_equal(
785            EncodedValue { value_type: RawValueType::NumberValue, value: 5 },
786            EncodedValue { value_type: RawValueType::NumberValue, value: 7 },
787        );
788        checker.verify_jmp_if_equal(
789            UNCOND_ABORT_BYTES,
790            EncodedValue { value_type: RawValueType::NumberValue, value: 10 },
791            EncodedValue { value_type: RawValueType::NumberValue, value: 11 },
792        );
793        checker.verify_unconditional_abort();
794        checker.verify_jmp_pad();
795        checker.verify_end();
796    }
797
798    #[test]
799    fn test_duplicate_label() {
800        let instructions = vec![
801            SymbolicInstruction::UnconditionalJump { label: 1 },
802            SymbolicInstruction::AbortIfEqual {
803                lhs: Symbol::DeprecatedKey(5),
804                rhs: Symbol::NumberValue(7),
805            },
806            SymbolicInstruction::Label(1),
807            SymbolicInstruction::UnconditionalAbort,
808            SymbolicInstruction::Label(1),
809        ];
810
811        let bind_rules = BindRules {
812            instructions: to_symbolic_inst_info(instructions),
813            symbol_table: HashMap::new(),
814            use_new_bytecode: true,
815            enable_debug: false,
816        };
817
818        assert_eq!(Err(BindRulesEncodeError::DuplicateLabel(1)), encode_to_bytecode_v2(bind_rules));
819    }
820
821    #[test]
822    fn test_unused_label() {
823        let instructions = vec![
824            SymbolicInstruction::UnconditionalAbort,
825            SymbolicInstruction::Label(1),
826            SymbolicInstruction::UnconditionalAbort,
827        ];
828
829        let bind_rules = BindRules {
830            instructions: to_symbolic_inst_info(instructions),
831            symbol_table: HashMap::new(),
832            use_new_bytecode: true,
833            enable_debug: false,
834        };
835
836        let mut checker = BytecodeChecker::new(encode_to_bytecode_v2(bind_rules).unwrap());
837
838        checker.verify_bind_rules_header(false);
839        checker.verify_sym_table_header(0);
840
841        checker.verify_instructions_header(UNCOND_ABORT_BYTES + JMP_PAD_BYTES + UNCOND_ABORT_BYTES);
842        checker.verify_unconditional_abort();
843        checker.verify_jmp_pad();
844        checker.verify_unconditional_abort();
845    }
846
847    #[test]
848    fn test_label_appears_before_jmp() {
849        let instructions = vec![
850            SymbolicInstruction::Label(1),
851            SymbolicInstruction::UnconditionalJump { label: 1 },
852            SymbolicInstruction::UnconditionalAbort,
853        ];
854
855        let bind_rules = BindRules {
856            instructions: to_symbolic_inst_info(instructions),
857            symbol_table: HashMap::new(),
858            use_new_bytecode: true,
859            enable_debug: false,
860        };
861
862        assert_eq!(
863            Err(BindRulesEncodeError::InvalidGotoLocation(1)),
864            encode_to_bytecode_v2(bind_rules)
865        );
866    }
867
868    #[test]
869    fn test_missing_label() {
870        let instructions = vec![
871            SymbolicInstruction::UnconditionalJump { label: 2 },
872            SymbolicInstruction::UnconditionalAbort,
873            SymbolicInstruction::Label(1),
874        ];
875
876        let bind_rules = BindRules {
877            instructions: to_symbolic_inst_info(instructions),
878            symbol_table: HashMap::new(),
879            use_new_bytecode: true,
880            enable_debug: false,
881        };
882
883        assert_eq!(Err(BindRulesEncodeError::MissingLabel(2)), encode_to_bytecode_v2(bind_rules));
884    }
885
886    #[test]
887    fn test_mismatch_value_types() {
888        let instructions = vec![SymbolicInstruction::AbortIfNotEqual {
889            lhs: Symbol::Key("waxwing".to_string(), ValueType::Number),
890            rhs: Symbol::BoolValue(true),
891        }];
892
893        let bind_rules = BindRules {
894            instructions: to_symbolic_inst_info(instructions),
895            symbol_table: HashMap::new(),
896            use_new_bytecode: true,
897            enable_debug: false,
898        };
899
900        assert_eq!(
901            Err(BindRulesEncodeError::MismatchValueTypes(ValueType::Number, ValueType::Bool)),
902            encode_to_bytecode_v2(bind_rules)
903        );
904    }
905
906    #[test]
907    fn test_string_enum_match_value_types() {
908        let instructions = vec![SymbolicInstruction::AbortIfNotEqual {
909            lhs: Symbol::Key("waxwing".to_string(), ValueType::Enum),
910            rhs: Symbol::StringValue("bohemian".to_string()),
911        }];
912
913        let bind_rules = BindRules {
914            instructions: to_symbolic_inst_info(instructions),
915            symbol_table: HashMap::new(),
916            use_new_bytecode: true,
917            enable_debug: false,
918        };
919
920        let mut checker = BytecodeChecker::new(encode_to_bytecode_v2(bind_rules).unwrap());
921
922        checker.verify_bind_rules_header(false);
923        checker.verify_sym_table_header(25);
924        checker.verify_symbol_table(&["waxwing", "bohemian"]);
925
926        checker.verify_instructions_header(COND_ABORT_BYTES);
927        checker.verify_abort_not_equal(
928            EncodedValue { value_type: RawValueType::Key, value: 1 },
929            EncodedValue { value_type: RawValueType::StringValue, value: 2 },
930        );
931        checker.verify_end();
932    }
933
934    #[test]
935    fn test_invalid_lhs_symbol() {
936        let instructions = vec![SymbolicInstruction::AbortIfNotEqual {
937            lhs: Symbol::NumberValue(5),
938            rhs: Symbol::BoolValue(true),
939        }];
940
941        let bind_rules = BindRules {
942            instructions: to_symbolic_inst_info(instructions),
943            symbol_table: HashMap::new(),
944            use_new_bytecode: true,
945            enable_debug: false,
946        };
947
948        assert_eq!(
949            Err(BindRulesEncodeError::IncorrectTypesInValueComparison),
950            encode_to_bytecode_v2(bind_rules)
951        );
952    }
953
954    #[test]
955    fn test_invalid_rhs_symbol() {
956        let instructions = vec![SymbolicInstruction::AbortIfNotEqual {
957            lhs: Symbol::DeprecatedKey(5),
958            rhs: Symbol::DeprecatedKey(6),
959        }];
960        let bind_rules = BindRules {
961            instructions: to_symbolic_inst_info(instructions),
962            symbol_table: HashMap::new(),
963            use_new_bytecode: true,
964            enable_debug: false,
965        };
966        assert_eq!(
967            Err(BindRulesEncodeError::IncorrectTypesInValueComparison),
968            encode_to_bytecode_v2(bind_rules)
969        );
970
971        let instructions = vec![SymbolicInstruction::AbortIfNotEqual {
972            lhs: Symbol::DeprecatedKey(5),
973            rhs: Symbol::Key("wagtail".to_string(), ValueType::Number),
974        }];
975        let bind_rules = BindRules {
976            instructions: to_symbolic_inst_info(instructions),
977            symbol_table: HashMap::new(),
978            use_new_bytecode: true,
979            enable_debug: false,
980        };
981        assert_eq!(
982            Err(BindRulesEncodeError::IncorrectTypesInValueComparison),
983            encode_to_bytecode_v2(bind_rules)
984        );
985    }
986
987    #[test]
988    fn test_missing_match_instruction() {
989        let instructions =
990            vec![SymbolicInstruction::UnconditionalAbort, SymbolicInstruction::UnconditionalBind];
991
992        let bind_rules = BindRules {
993            instructions: to_symbolic_inst_info(instructions),
994            symbol_table: HashMap::new(),
995            use_new_bytecode: true,
996            enable_debug: false,
997        };
998
999        assert_eq!(Err(BindRulesEncodeError::MatchNotSupported), encode_to_bytecode_v2(bind_rules));
1000    }
1001
1002    #[test]
1003    fn test_composite() {
1004        let primary_parent = vec![
1005            SymbolicInstruction::AbortIfEqual {
1006                lhs: Symbol::DeprecatedKey(1),
1007                rhs: Symbol::BoolValue(false),
1008            },
1009            SymbolicInstruction::UnconditionalAbort,
1010        ];
1011
1012        let additional_parents = vec![
1013            SymbolicInstruction::JumpIfEqual {
1014                lhs: Symbol::DeprecatedKey(15),
1015                rhs: Symbol::StringValue("ruff".to_string()),
1016                label: 1,
1017            },
1018            SymbolicInstruction::AbortIfEqual {
1019                lhs: Symbol::Key("plover".to_string(), ValueType::Number),
1020                rhs: Symbol::NumberValue(1),
1021            },
1022            SymbolicInstruction::Label(1),
1023            SymbolicInstruction::UnconditionalAbort,
1024        ];
1025
1026        let bind_rules = CompositeBindRules {
1027            symbol_table: HashMap::new(),
1028            device_name: "wader".to_string(),
1029            primary_parent: composite_parent("stilt".to_string(), primary_parent),
1030            additional_parents: vec![composite_parent("avocet".to_string(), additional_parents)],
1031            optional_parents: vec![],
1032            enable_debug: false,
1033        };
1034
1035        let mut checker = BytecodeChecker::new(encode_composite_to_bytecode(bind_rules).unwrap());
1036        checker.verify_bind_rules_header(false);
1037        checker.verify_sym_table_header(51);
1038
1039        checker.verify_symbol_table(&["wader", "stilt", "avocet", "ruff", "plover"]);
1040
1041        let primary_parent_bytes = COND_ABORT_BYTES + UNCOND_ABORT_BYTES;
1042        let additional_parent_bytes =
1043            COND_JMP_BYTES + COND_ABORT_BYTES + JMP_PAD_BYTES + UNCOND_ABORT_BYTES;
1044        checker.verify_composite_header(
1045            (NODE_HEADER_BYTES * 2)
1046                + COMPOSITE_NAME_ID_BYTES
1047                + primary_parent_bytes
1048                + additional_parent_bytes,
1049        );
1050
1051        // Verify primary parent.
1052        checker.verify_parent_header(RawParentType::Primary, 2, primary_parent_bytes);
1053        checker.verify_abort_equal(
1054            EncodedValue { value_type: RawValueType::NumberValue, value: 1 },
1055            EncodedValue { value_type: RawValueType::BoolValue, value: 0 },
1056        );
1057        checker.verify_unconditional_abort();
1058
1059        // Verify additional parent.
1060        checker.verify_parent_header(RawParentType::Additional, 3, additional_parent_bytes);
1061        checker.verify_jmp_if_equal(
1062            COND_ABORT_BYTES,
1063            EncodedValue { value_type: RawValueType::NumberValue, value: 15 },
1064            EncodedValue { value_type: RawValueType::StringValue, value: 4 },
1065        );
1066        checker.verify_abort_equal(
1067            EncodedValue { value_type: RawValueType::Key, value: 5 },
1068            EncodedValue { value_type: RawValueType::NumberValue, value: 1 },
1069        );
1070        checker.verify_jmp_pad();
1071        checker.verify_unconditional_abort();
1072        checker.verify_end();
1073    }
1074
1075    #[test]
1076    fn test_composite_optional() {
1077        let primary_parent_inst = vec![
1078            SymbolicInstruction::AbortIfEqual {
1079                lhs: Symbol::DeprecatedKey(1),
1080                rhs: Symbol::BoolValue(false),
1081            },
1082            SymbolicInstruction::UnconditionalAbort,
1083        ];
1084
1085        let optional_parent_inst = vec![
1086            SymbolicInstruction::JumpIfEqual {
1087                lhs: Symbol::DeprecatedKey(15),
1088                rhs: Symbol::StringValue("ruff".to_string()),
1089                label: 1,
1090            },
1091            SymbolicInstruction::AbortIfEqual {
1092                lhs: Symbol::Key("plover".to_string(), ValueType::Number),
1093                rhs: Symbol::NumberValue(1),
1094            },
1095            SymbolicInstruction::Label(1),
1096            SymbolicInstruction::UnconditionalAbort,
1097        ];
1098
1099        let bind_rules = CompositeBindRules {
1100            symbol_table: HashMap::new(),
1101            device_name: "wader".to_string(),
1102            primary_parent: composite_parent("stilt".to_string(), primary_parent_inst),
1103            additional_parents: vec![],
1104            optional_parents: vec![composite_parent("avocet".to_string(), optional_parent_inst)],
1105            enable_debug: false,
1106        };
1107
1108        let mut checker = BytecodeChecker::new(encode_composite_to_bytecode(bind_rules).unwrap());
1109        checker.verify_bind_rules_header(false);
1110        checker.verify_sym_table_header(51);
1111
1112        checker.verify_symbol_table(&["wader", "stilt", "avocet", "ruff", "plover"]);
1113
1114        let primary_parent_bytes = COND_ABORT_BYTES + UNCOND_ABORT_BYTES;
1115        let optional_parent_bytes =
1116            COND_JMP_BYTES + COND_ABORT_BYTES + JMP_PAD_BYTES + UNCOND_ABORT_BYTES;
1117        checker.verify_composite_header(
1118            (NODE_HEADER_BYTES * 2)
1119                + COMPOSITE_NAME_ID_BYTES
1120                + primary_parent_bytes
1121                + optional_parent_bytes,
1122        );
1123
1124        // Verify primary parent.
1125        checker.verify_parent_header(RawParentType::Primary, 2, primary_parent_bytes);
1126        checker.verify_abort_equal(
1127            EncodedValue { value_type: RawValueType::NumberValue, value: 1 },
1128            EncodedValue { value_type: RawValueType::BoolValue, value: 0 },
1129        );
1130        checker.verify_unconditional_abort();
1131
1132        // Verify optional parent.
1133        checker.verify_parent_header(RawParentType::Optional, 3, optional_parent_bytes);
1134        checker.verify_jmp_if_equal(
1135            COND_ABORT_BYTES,
1136            EncodedValue { value_type: RawValueType::NumberValue, value: 15 },
1137            EncodedValue { value_type: RawValueType::StringValue, value: 4 },
1138        );
1139        checker.verify_abort_equal(
1140            EncodedValue { value_type: RawValueType::Key, value: 5 },
1141            EncodedValue { value_type: RawValueType::NumberValue, value: 1 },
1142        );
1143        checker.verify_jmp_pad();
1144        checker.verify_unconditional_abort();
1145        checker.verify_end();
1146    }
1147
1148    #[test]
1149    fn test_composite_additional_and_optional() {
1150        let primary_parent_inst = vec![
1151            SymbolicInstruction::AbortIfEqual {
1152                lhs: Symbol::DeprecatedKey(1),
1153                rhs: Symbol::BoolValue(false),
1154            },
1155            SymbolicInstruction::UnconditionalAbort,
1156        ];
1157
1158        let additional_parent_inst = vec![
1159            SymbolicInstruction::AbortIfEqual {
1160                lhs: Symbol::Key("thrasher".to_string(), ValueType::Str),
1161                rhs: Symbol::StringValue("catbird".to_string()),
1162            },
1163            SymbolicInstruction::AbortIfEqual {
1164                lhs: Symbol::Key("catbird".to_string(), ValueType::Number),
1165                rhs: Symbol::NumberValue(1),
1166            },
1167        ];
1168
1169        let optional_parent_inst = vec![
1170            SymbolicInstruction::JumpIfEqual {
1171                lhs: Symbol::DeprecatedKey(15),
1172                rhs: Symbol::StringValue("ruff".to_string()),
1173                label: 1,
1174            },
1175            SymbolicInstruction::AbortIfEqual {
1176                lhs: Symbol::Key("plover".to_string(), ValueType::Number),
1177                rhs: Symbol::NumberValue(1),
1178            },
1179            SymbolicInstruction::Label(1),
1180            SymbolicInstruction::UnconditionalAbort,
1181        ];
1182
1183        let bind_rules = CompositeBindRules {
1184            symbol_table: HashMap::new(),
1185            device_name: "wader".to_string(),
1186            primary_parent: composite_parent("stilt".to_string(), primary_parent_inst),
1187            additional_parents: vec![composite_parent(
1188                "avocet".to_string(),
1189                additional_parent_inst,
1190            )],
1191            optional_parents: vec![composite_parent(
1192                "mockingbird".to_string(),
1193                optional_parent_inst,
1194            )],
1195            enable_debug: false,
1196        };
1197
1198        let mut checker = BytecodeChecker::new(encode_composite_to_bytecode(bind_rules).unwrap());
1199        checker.verify_bind_rules_header(false);
1200        checker.verify_sym_table_header(92);
1201
1202        checker.verify_symbol_table(&[
1203            "wader",
1204            "stilt",
1205            "avocet",
1206            "thrasher",
1207            "catbird",
1208            "mockingbird",
1209            "ruff",
1210            "plover",
1211        ]);
1212
1213        let primary_parent_bytes = COND_ABORT_BYTES + UNCOND_ABORT_BYTES;
1214        let additional_parent_bytes = COND_ABORT_BYTES * 2;
1215        let optional_parent_bytes =
1216            COND_JMP_BYTES + COND_ABORT_BYTES + JMP_PAD_BYTES + UNCOND_ABORT_BYTES;
1217        checker.verify_composite_header(
1218            (NODE_HEADER_BYTES * 3)
1219                + COMPOSITE_NAME_ID_BYTES
1220                + primary_parent_bytes
1221                + additional_parent_bytes
1222                + optional_parent_bytes,
1223        );
1224
1225        // Verify primary parent.
1226        checker.verify_parent_header(RawParentType::Primary, 2, primary_parent_bytes);
1227        checker.verify_abort_equal(
1228            EncodedValue { value_type: RawValueType::NumberValue, value: 1 },
1229            EncodedValue { value_type: RawValueType::BoolValue, value: 0 },
1230        );
1231        checker.verify_unconditional_abort();
1232
1233        // Verify additional parent.
1234        checker.verify_parent_header(RawParentType::Additional, 3, additional_parent_bytes);
1235        checker.verify_abort_equal(
1236            EncodedValue { value_type: RawValueType::Key, value: 4 },
1237            EncodedValue { value_type: RawValueType::StringValue, value: 5 },
1238        );
1239        checker.verify_abort_equal(
1240            EncodedValue { value_type: RawValueType::Key, value: 5 },
1241            EncodedValue { value_type: RawValueType::NumberValue, value: 1 },
1242        );
1243
1244        // Verify optional parent.
1245        checker.verify_parent_header(RawParentType::Optional, 6, optional_parent_bytes);
1246        checker.verify_jmp_if_equal(
1247            COND_ABORT_BYTES,
1248            EncodedValue { value_type: RawValueType::NumberValue, value: 15 },
1249            EncodedValue { value_type: RawValueType::StringValue, value: 7 },
1250        );
1251        checker.verify_abort_equal(
1252            EncodedValue { value_type: RawValueType::Key, value: 8 },
1253            EncodedValue { value_type: RawValueType::NumberValue, value: 1 },
1254        );
1255        checker.verify_jmp_pad();
1256        checker.verify_unconditional_abort();
1257        checker.verify_end();
1258    }
1259
1260    #[test]
1261    fn test_composite_enable_debug_missing_ast_location() {
1262        let primary_parent = vec![
1263            SymbolicInstruction::AbortIfEqual {
1264                lhs: Symbol::DeprecatedKey(1),
1265                rhs: Symbol::BoolValue(false),
1266            },
1267            SymbolicInstruction::UnconditionalAbort,
1268        ];
1269
1270        let additional_parents = vec![
1271            SymbolicInstruction::JumpIfEqual {
1272                lhs: Symbol::DeprecatedKey(15),
1273                rhs: Symbol::StringValue("ruff".to_string()),
1274                label: 1,
1275            },
1276            SymbolicInstruction::AbortIfEqual {
1277                lhs: Symbol::Key("plover".to_string(), ValueType::Number),
1278                rhs: Symbol::NumberValue(1),
1279            },
1280            SymbolicInstruction::Label(1),
1281            SymbolicInstruction::UnconditionalAbort,
1282        ];
1283
1284        let bind_rules = CompositeBindRules {
1285            symbol_table: HashMap::new(),
1286            device_name: "wader".to_string(),
1287            primary_parent: composite_parent("stilt".to_string(), primary_parent),
1288            additional_parents: vec![composite_parent("avocet".to_string(), additional_parents)],
1289            optional_parents: vec![],
1290            enable_debug: true,
1291        };
1292
1293        assert_eq!(
1294            Err(BindRulesEncodeError::MissingAstLocation),
1295            encode_composite_to_bytecode(bind_rules)
1296        );
1297    }
1298
1299    #[test]
1300    fn test_composite_sharing_symbols() {
1301        let primary_parent_inst = vec![SymbolicInstruction::AbortIfEqual {
1302            lhs: Symbol::Key("trembler".to_string(), ValueType::Str),
1303            rhs: Symbol::StringValue("thrasher".to_string()),
1304        }];
1305
1306        let additional_parent_inst = vec![
1307            SymbolicInstruction::AbortIfEqual {
1308                lhs: Symbol::Key("thrasher".to_string(), ValueType::Str),
1309                rhs: Symbol::StringValue("catbird".to_string()),
1310            },
1311            SymbolicInstruction::AbortIfEqual {
1312                lhs: Symbol::Key("catbird".to_string(), ValueType::Number),
1313                rhs: Symbol::NumberValue(1),
1314            },
1315        ];
1316
1317        let bind_rules = CompositeBindRules {
1318            device_name: "mimid".to_string(),
1319            symbol_table: HashMap::new(),
1320            primary_parent: composite_parent("catbird".to_string(), primary_parent_inst),
1321            additional_parents: vec![composite_parent(
1322                "mockingbird".to_string(),
1323                additional_parent_inst,
1324            )],
1325            optional_parents: vec![],
1326            enable_debug: false,
1327        };
1328
1329        let mut checker = BytecodeChecker::new(encode_composite_to_bytecode(bind_rules).unwrap());
1330        checker.verify_bind_rules_header(false);
1331
1332        checker.verify_sym_table_header(64);
1333        checker.verify_symbol_table(&["mimid", "catbird", "trembler", "thrasher", "mockingbird"]);
1334
1335        let primary_parent_bytes = COND_ABORT_BYTES;
1336        let additional_parent_bytes = COND_ABORT_BYTES * 2;
1337        checker.verify_composite_header(
1338            (NODE_HEADER_BYTES * 2)
1339                + COMPOSITE_NAME_ID_BYTES
1340                + primary_parent_bytes
1341                + additional_parent_bytes,
1342        );
1343
1344        checker.verify_parent_header(RawParentType::Primary, 2, primary_parent_bytes);
1345        checker.verify_abort_equal(
1346            EncodedValue { value_type: RawValueType::Key, value: 3 },
1347            EncodedValue { value_type: RawValueType::StringValue, value: 4 },
1348        );
1349
1350        checker.verify_parent_header(RawParentType::Additional, 5, additional_parent_bytes);
1351        checker.verify_abort_equal(
1352            EncodedValue { value_type: RawValueType::Key, value: 4 },
1353            EncodedValue { value_type: RawValueType::StringValue, value: 2 },
1354        );
1355        checker.verify_abort_equal(
1356            EncodedValue { value_type: RawValueType::Key, value: 2 },
1357            EncodedValue { value_type: RawValueType::NumberValue, value: 1 },
1358        );
1359        checker.verify_end();
1360    }
1361
1362    #[test]
1363    fn test_composite_same_label_id() {
1364        let primary_parent_inst = vec![
1365            SymbolicInstruction::JumpIfEqual {
1366                lhs: Symbol::DeprecatedKey(15),
1367                rhs: Symbol::NumberValue(5),
1368                label: 1,
1369            },
1370            SymbolicInstruction::UnconditionalAbort,
1371            SymbolicInstruction::Label(1),
1372        ];
1373
1374        let additional_parent_inst = vec![
1375            SymbolicInstruction::JumpIfEqual {
1376                lhs: Symbol::DeprecatedKey(10),
1377                rhs: Symbol::NumberValue(2),
1378                label: 1,
1379            },
1380            SymbolicInstruction::UnconditionalAbort,
1381            SymbolicInstruction::Label(1),
1382        ];
1383
1384        let bind_rules = CompositeBindRules {
1385            device_name: "currawong".to_string(),
1386            symbol_table: HashMap::new(),
1387            primary_parent: composite_parent("butcherbird".to_string(), primary_parent_inst),
1388            additional_parents: vec![composite_parent(
1389                "bushshrike".to_string(),
1390                additional_parent_inst,
1391            )],
1392            optional_parents: vec![],
1393            enable_debug: false,
1394        };
1395
1396        let mut checker = BytecodeChecker::new(encode_composite_to_bytecode(bind_rules).unwrap());
1397        checker.verify_bind_rules_header(false);
1398
1399        checker.verify_sym_table_header(45);
1400        checker.verify_symbol_table(&["currawong", "butcherbird", "bushshrike"]);
1401
1402        let primary_parent_bytes = COND_JMP_BYTES + UNCOND_ABORT_BYTES + JMP_PAD_BYTES;
1403        let additional_parent_bytes = COND_JMP_BYTES + UNCOND_ABORT_BYTES + JMP_PAD_BYTES;
1404        checker.verify_composite_header(
1405            (NODE_HEADER_BYTES * 2)
1406                + COMPOSITE_NAME_ID_BYTES
1407                + primary_parent_bytes
1408                + additional_parent_bytes,
1409        );
1410
1411        checker.verify_parent_header(RawParentType::Primary, 2, primary_parent_bytes);
1412        checker.verify_jmp_if_equal(
1413            UNCOND_ABORT_BYTES,
1414            EncodedValue { value_type: RawValueType::NumberValue, value: 15 },
1415            EncodedValue { value_type: RawValueType::NumberValue, value: 5 },
1416        );
1417        checker.verify_unconditional_abort();
1418        checker.verify_jmp_pad();
1419
1420        checker.verify_parent_header(RawParentType::Additional, 3, additional_parent_bytes);
1421        checker.verify_jmp_if_equal(
1422            UNCOND_ABORT_BYTES,
1423            EncodedValue { value_type: RawValueType::NumberValue, value: 10 },
1424            EncodedValue { value_type: RawValueType::NumberValue, value: 2 },
1425        );
1426        checker.verify_unconditional_abort();
1427        checker.verify_jmp_pad();
1428
1429        checker.verify_end();
1430    }
1431
1432    #[test]
1433    fn test_composite_invalid_label() {
1434        let primary_parent_inst = vec![
1435            SymbolicInstruction::JumpIfEqual {
1436                lhs: Symbol::DeprecatedKey(15),
1437                rhs: Symbol::NumberValue(5),
1438                label: 1,
1439            },
1440            SymbolicInstruction::UnconditionalAbort,
1441            SymbolicInstruction::Label(1),
1442        ];
1443
1444        let additional_parent_inst = vec![
1445            SymbolicInstruction::JumpIfEqual {
1446                lhs: Symbol::DeprecatedKey(10),
1447                rhs: Symbol::NumberValue(2),
1448                label: 1,
1449            },
1450            SymbolicInstruction::UnconditionalAbort,
1451            SymbolicInstruction::Label(2),
1452        ];
1453
1454        let bind_rules = CompositeBindRules {
1455            device_name: "currawong".to_string(),
1456            symbol_table: HashMap::new(),
1457            primary_parent: composite_parent("butcherbird".to_string(), primary_parent_inst),
1458            additional_parents: vec![composite_parent(
1459                "bushshrike".to_string(),
1460                additional_parent_inst,
1461            )],
1462            optional_parents: vec![],
1463            enable_debug: false,
1464        };
1465
1466        assert_eq!(
1467            Err(BindRulesEncodeError::MissingLabel(1)),
1468            encode_composite_to_bytecode(bind_rules)
1469        );
1470    }
1471
1472    #[test]
1473    fn test_composite_primary_parent_only() {
1474        let primary_parent_inst = vec![SymbolicInstruction::UnconditionalAbort];
1475
1476        let bind_rules = CompositeBindRules {
1477            device_name: "treehunter".to_string(),
1478            symbol_table: HashMap::new(),
1479            additional_parents: vec![],
1480            optional_parents: vec![],
1481            primary_parent: composite_parent("bananaquit".to_string(), primary_parent_inst),
1482            enable_debug: false,
1483        };
1484
1485        let mut checker = BytecodeChecker::new(encode_composite_to_bytecode(bind_rules).unwrap());
1486        checker.verify_bind_rules_header(false);
1487
1488        checker.verify_sym_table_header(30);
1489        checker.verify_symbol_table(&["treehunter", "bananaquit"]);
1490
1491        let primary_parent_bytes = UNCOND_ABORT_BYTES;
1492        checker.verify_composite_header(
1493            NODE_HEADER_BYTES + COMPOSITE_NAME_ID_BYTES + primary_parent_bytes,
1494        );
1495
1496        checker.verify_parent_header(RawParentType::Primary, 2, primary_parent_bytes);
1497        checker.verify_unconditional_abort();
1498
1499        checker.verify_end();
1500    }
1501
1502    #[test]
1503    fn test_composite_empty_parent_instructions() {
1504        let bind_rules = CompositeBindRules {
1505            device_name: "spiderhunter".to_string(),
1506            symbol_table: HashMap::new(),
1507            primary_parent: composite_parent("sunbird".to_string(), vec![]),
1508            additional_parents: vec![],
1509            optional_parents: vec![],
1510            enable_debug: false,
1511        };
1512
1513        let mut checker = BytecodeChecker::new(encode_composite_to_bytecode(bind_rules).unwrap());
1514        checker.verify_bind_rules_header(false);
1515
1516        checker.verify_sym_table_header(29);
1517        checker.verify_symbol_table(&["spiderhunter", "sunbird"]);
1518
1519        checker.verify_composite_header(NODE_HEADER_BYTES + COMPOSITE_NAME_ID_BYTES);
1520        checker.verify_parent_header(RawParentType::Primary, 2, 0);
1521
1522        checker.verify_end();
1523    }
1524
1525    #[test]
1526    fn test_composite_missing_device_name() {
1527        let bind_rules = CompositeBindRules {
1528            device_name: "".to_string(),
1529            symbol_table: HashMap::new(),
1530            primary_parent: composite_parent("pewee".to_string(), vec![]),
1531            additional_parents: vec![],
1532            optional_parents: vec![],
1533            enable_debug: false,
1534        };
1535
1536        assert_eq!(
1537            Err(BindRulesEncodeError::MissingCompositeDeviceName),
1538            encode_composite_to_bytecode(bind_rules)
1539        );
1540    }
1541
1542    #[test]
1543    fn test_composite_missing_parent_name() {
1544        let bind_rules = CompositeBindRules {
1545            device_name: "pewee".to_string(),
1546            symbol_table: HashMap::new(),
1547            primary_parent: composite_parent("".to_string(), vec![]),
1548            additional_parents: vec![],
1549            optional_parents: vec![],
1550            enable_debug: false,
1551        };
1552
1553        assert_eq!(
1554            Err(BindRulesEncodeError::MissingCompositeParentName),
1555            encode_composite_to_bytecode(bind_rules)
1556        );
1557    }
1558
1559    #[test]
1560    fn test_duplicate_parents() {
1561        let bind_rules = CompositeBindRules {
1562            device_name: "flycatcher".to_string(),
1563            symbol_table: HashMap::new(),
1564            primary_parent: composite_parent("pewee".to_string(), vec![]),
1565            additional_parents: vec![composite_parent("pewee".to_string(), vec![])],
1566            optional_parents: vec![],
1567            enable_debug: false,
1568        };
1569
1570        assert_eq!(
1571            Err(BindRulesEncodeError::DuplicateCompositeParentName("pewee".to_string())),
1572            encode_composite_to_bytecode(bind_rules)
1573        );
1574
1575        let bind_rules = CompositeBindRules {
1576            device_name: "flycatcher".to_string(),
1577            symbol_table: HashMap::new(),
1578            primary_parent: composite_parent("pewee".to_string(), vec![]),
1579            additional_parents: vec![
1580                composite_parent("phoebe".to_string(), vec![]),
1581                composite_parent("kingbird".to_string(), vec![]),
1582                composite_parent("phoebe".to_string(), vec![]),
1583            ],
1584            optional_parents: vec![],
1585            enable_debug: false,
1586        };
1587
1588        assert_eq!(
1589            Err(BindRulesEncodeError::DuplicateCompositeParentName("phoebe".to_string())),
1590            encode_composite_to_bytecode(bind_rules)
1591        );
1592    }
1593}