1use 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, CompositeNode};
10use std::collections::HashSet;
11
12pub 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 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 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 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 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 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 bytecode.extend_from_slice(
77 &((debug_symbol_table_encoder.bytecode.len() + HEADER_SZ) as u32).to_le_bytes(),
78 );
79 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_node(
90 bytecode: &mut Vec<u8>,
91 node: CompositeNode,
92 node_type: RawNodeType,
93 symbol_table_encoder: &mut SymbolTableEncoder,
94 debug_symbol_table_encoder: &mut Option<SymbolTableEncoder>,
95) -> Result<(), BindRulesEncodeError> {
96 if node.name.is_empty() {
97 return Err(BindRulesEncodeError::MissingCompositeNodeName);
98 }
99
100 bytecode.push(node_type as u8);
101 bytecode.extend_from_slice(&symbol_table_encoder.get_key(node.name)?.to_le_bytes());
102
103 let mut inst_bytecode =
104 encode_instructions(node.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 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 node_names = HashSet::new();
126
127 node_names.insert(bind_rules.primary_node.name.clone());
129 append_composite_node(
130 &mut inst_bytecode,
131 bind_rules.primary_node,
132 RawNodeType::Primary,
133 &mut symbol_table_encoder,
134 &mut debug_symbol_table_encoder_option,
135 )?;
136
137 for node in bind_rules.additional_nodes.into_iter() {
139 if node_names.contains(&node.name) {
140 return Err(BindRulesEncodeError::DuplicateCompositeNodeName(node.name));
141 }
142 node_names.insert(node.name.clone());
143 append_composite_node(
144 &mut inst_bytecode,
145 node,
146 RawNodeType::Additional,
147 &mut symbol_table_encoder,
148 &mut debug_symbol_table_encoder_option,
149 )?;
150 }
151
152 for node in bind_rules.optional_nodes.into_iter() {
154 if node_names.contains(&node.name) {
155 return Err(BindRulesEncodeError::DuplicateCompositeNodeName(node.name));
156 }
157 node_names.insert(node.name.clone());
158 append_composite_node(
159 &mut inst_bytecode,
160 node,
161 RawNodeType::Optional,
162 &mut symbol_table_encoder,
163 &mut debug_symbol_table_encoder_option,
164 )?;
165 }
166
167 let mut bytecode: Vec<u8> = vec![];
169
170 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 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 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 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 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_node<'a>(
217 name: String,
218 instructions: Vec<SymbolicInstruction>,
219 ) -> CompositeNode<'a> {
220 CompositeNode { 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 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 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 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 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 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 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_node = vec![
1005 SymbolicInstruction::AbortIfEqual {
1006 lhs: Symbol::DeprecatedKey(1),
1007 rhs: Symbol::BoolValue(false),
1008 },
1009 SymbolicInstruction::UnconditionalAbort,
1010 ];
1011
1012 let additional_nodes = 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_node: composite_node("stilt".to_string(), primary_node),
1030 additional_nodes: vec![composite_node("avocet".to_string(), additional_nodes)],
1031 optional_nodes: 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_node_bytes = COND_ABORT_BYTES + UNCOND_ABORT_BYTES;
1042 let additional_node_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_node_bytes
1048 + additional_node_bytes,
1049 );
1050
1051 checker.verify_node_header(RawNodeType::Primary, 2, primary_node_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 checker.verify_node_header(RawNodeType::Additional, 3, additional_node_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_node_inst = vec![
1078 SymbolicInstruction::AbortIfEqual {
1079 lhs: Symbol::DeprecatedKey(1),
1080 rhs: Symbol::BoolValue(false),
1081 },
1082 SymbolicInstruction::UnconditionalAbort,
1083 ];
1084
1085 let optional_node_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_node: composite_node("stilt".to_string(), primary_node_inst),
1103 additional_nodes: vec![],
1104 optional_nodes: vec![composite_node("avocet".to_string(), optional_node_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_node_bytes = COND_ABORT_BYTES + UNCOND_ABORT_BYTES;
1115 let optional_node_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_node_bytes
1121 + optional_node_bytes,
1122 );
1123
1124 checker.verify_node_header(RawNodeType::Primary, 2, primary_node_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 checker.verify_node_header(RawNodeType::Optional, 3, optional_node_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_node_inst = vec![
1151 SymbolicInstruction::AbortIfEqual {
1152 lhs: Symbol::DeprecatedKey(1),
1153 rhs: Symbol::BoolValue(false),
1154 },
1155 SymbolicInstruction::UnconditionalAbort,
1156 ];
1157
1158 let additional_node_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_node_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_node: composite_node("stilt".to_string(), primary_node_inst),
1187 additional_nodes: vec![composite_node("avocet".to_string(), additional_node_inst)],
1188 optional_nodes: vec![composite_node("mockingbird".to_string(), optional_node_inst)],
1189 enable_debug: false,
1190 };
1191
1192 let mut checker = BytecodeChecker::new(encode_composite_to_bytecode(bind_rules).unwrap());
1193 checker.verify_bind_rules_header(false);
1194 checker.verify_sym_table_header(92);
1195
1196 checker.verify_symbol_table(&[
1197 "wader",
1198 "stilt",
1199 "avocet",
1200 "thrasher",
1201 "catbird",
1202 "mockingbird",
1203 "ruff",
1204 "plover",
1205 ]);
1206
1207 let primary_node_bytes = COND_ABORT_BYTES + UNCOND_ABORT_BYTES;
1208 let additional_node_bytes = COND_ABORT_BYTES * 2;
1209 let optional_node_bytes =
1210 COND_JMP_BYTES + COND_ABORT_BYTES + JMP_PAD_BYTES + UNCOND_ABORT_BYTES;
1211 checker.verify_composite_header(
1212 (NODE_HEADER_BYTES * 3)
1213 + COMPOSITE_NAME_ID_BYTES
1214 + primary_node_bytes
1215 + additional_node_bytes
1216 + optional_node_bytes,
1217 );
1218
1219 checker.verify_node_header(RawNodeType::Primary, 2, primary_node_bytes);
1221 checker.verify_abort_equal(
1222 EncodedValue { value_type: RawValueType::NumberValue, value: 1 },
1223 EncodedValue { value_type: RawValueType::BoolValue, value: 0 },
1224 );
1225 checker.verify_unconditional_abort();
1226
1227 checker.verify_node_header(RawNodeType::Additional, 3, additional_node_bytes);
1229 checker.verify_abort_equal(
1230 EncodedValue { value_type: RawValueType::Key, value: 4 },
1231 EncodedValue { value_type: RawValueType::StringValue, value: 5 },
1232 );
1233 checker.verify_abort_equal(
1234 EncodedValue { value_type: RawValueType::Key, value: 5 },
1235 EncodedValue { value_type: RawValueType::NumberValue, value: 1 },
1236 );
1237
1238 checker.verify_node_header(RawNodeType::Optional, 6, optional_node_bytes);
1240 checker.verify_jmp_if_equal(
1241 COND_ABORT_BYTES,
1242 EncodedValue { value_type: RawValueType::NumberValue, value: 15 },
1243 EncodedValue { value_type: RawValueType::StringValue, value: 7 },
1244 );
1245 checker.verify_abort_equal(
1246 EncodedValue { value_type: RawValueType::Key, value: 8 },
1247 EncodedValue { value_type: RawValueType::NumberValue, value: 1 },
1248 );
1249 checker.verify_jmp_pad();
1250 checker.verify_unconditional_abort();
1251 checker.verify_end();
1252 }
1253
1254 #[test]
1255 fn test_composite_enable_debug_missing_ast_location() {
1256 let primary_node = vec![
1257 SymbolicInstruction::AbortIfEqual {
1258 lhs: Symbol::DeprecatedKey(1),
1259 rhs: Symbol::BoolValue(false),
1260 },
1261 SymbolicInstruction::UnconditionalAbort,
1262 ];
1263
1264 let additional_nodes = vec![
1265 SymbolicInstruction::JumpIfEqual {
1266 lhs: Symbol::DeprecatedKey(15),
1267 rhs: Symbol::StringValue("ruff".to_string()),
1268 label: 1,
1269 },
1270 SymbolicInstruction::AbortIfEqual {
1271 lhs: Symbol::Key("plover".to_string(), ValueType::Number),
1272 rhs: Symbol::NumberValue(1),
1273 },
1274 SymbolicInstruction::Label(1),
1275 SymbolicInstruction::UnconditionalAbort,
1276 ];
1277
1278 let bind_rules = CompositeBindRules {
1279 symbol_table: HashMap::new(),
1280 device_name: "wader".to_string(),
1281 primary_node: composite_node("stilt".to_string(), primary_node),
1282 additional_nodes: vec![composite_node("avocet".to_string(), additional_nodes)],
1283 optional_nodes: vec![],
1284 enable_debug: true,
1285 };
1286
1287 assert_eq!(
1288 Err(BindRulesEncodeError::MissingAstLocation),
1289 encode_composite_to_bytecode(bind_rules)
1290 );
1291 }
1292
1293 #[test]
1294 fn test_composite_sharing_symbols() {
1295 let primary_node_inst = vec![SymbolicInstruction::AbortIfEqual {
1296 lhs: Symbol::Key("trembler".to_string(), ValueType::Str),
1297 rhs: Symbol::StringValue("thrasher".to_string()),
1298 }];
1299
1300 let additional_node_inst = vec![
1301 SymbolicInstruction::AbortIfEqual {
1302 lhs: Symbol::Key("thrasher".to_string(), ValueType::Str),
1303 rhs: Symbol::StringValue("catbird".to_string()),
1304 },
1305 SymbolicInstruction::AbortIfEqual {
1306 lhs: Symbol::Key("catbird".to_string(), ValueType::Number),
1307 rhs: Symbol::NumberValue(1),
1308 },
1309 ];
1310
1311 let bind_rules = CompositeBindRules {
1312 device_name: "mimid".to_string(),
1313 symbol_table: HashMap::new(),
1314 primary_node: composite_node("catbird".to_string(), primary_node_inst),
1315 additional_nodes: vec![composite_node("mockingbird".to_string(), additional_node_inst)],
1316 optional_nodes: vec![],
1317 enable_debug: false,
1318 };
1319
1320 let mut checker = BytecodeChecker::new(encode_composite_to_bytecode(bind_rules).unwrap());
1321 checker.verify_bind_rules_header(false);
1322
1323 checker.verify_sym_table_header(64);
1324 checker.verify_symbol_table(&["mimid", "catbird", "trembler", "thrasher", "mockingbird"]);
1325
1326 let primary_node_bytes = COND_ABORT_BYTES;
1327 let additional_node_bytes = COND_ABORT_BYTES * 2;
1328 checker.verify_composite_header(
1329 (NODE_HEADER_BYTES * 2)
1330 + COMPOSITE_NAME_ID_BYTES
1331 + primary_node_bytes
1332 + additional_node_bytes,
1333 );
1334
1335 checker.verify_node_header(RawNodeType::Primary, 2, primary_node_bytes);
1336 checker.verify_abort_equal(
1337 EncodedValue { value_type: RawValueType::Key, value: 3 },
1338 EncodedValue { value_type: RawValueType::StringValue, value: 4 },
1339 );
1340
1341 checker.verify_node_header(RawNodeType::Additional, 5, additional_node_bytes);
1342 checker.verify_abort_equal(
1343 EncodedValue { value_type: RawValueType::Key, value: 4 },
1344 EncodedValue { value_type: RawValueType::StringValue, value: 2 },
1345 );
1346 checker.verify_abort_equal(
1347 EncodedValue { value_type: RawValueType::Key, value: 2 },
1348 EncodedValue { value_type: RawValueType::NumberValue, value: 1 },
1349 );
1350 checker.verify_end();
1351 }
1352
1353 #[test]
1354 fn test_composite_same_label_id() {
1355 let primary_node_inst = vec![
1356 SymbolicInstruction::JumpIfEqual {
1357 lhs: Symbol::DeprecatedKey(15),
1358 rhs: Symbol::NumberValue(5),
1359 label: 1,
1360 },
1361 SymbolicInstruction::UnconditionalAbort,
1362 SymbolicInstruction::Label(1),
1363 ];
1364
1365 let additional_node_inst = vec![
1366 SymbolicInstruction::JumpIfEqual {
1367 lhs: Symbol::DeprecatedKey(10),
1368 rhs: Symbol::NumberValue(2),
1369 label: 1,
1370 },
1371 SymbolicInstruction::UnconditionalAbort,
1372 SymbolicInstruction::Label(1),
1373 ];
1374
1375 let bind_rules = CompositeBindRules {
1376 device_name: "currawong".to_string(),
1377 symbol_table: HashMap::new(),
1378 primary_node: composite_node("butcherbird".to_string(), primary_node_inst),
1379 additional_nodes: vec![composite_node("bushshrike".to_string(), additional_node_inst)],
1380 optional_nodes: vec![],
1381 enable_debug: false,
1382 };
1383
1384 let mut checker = BytecodeChecker::new(encode_composite_to_bytecode(bind_rules).unwrap());
1385 checker.verify_bind_rules_header(false);
1386
1387 checker.verify_sym_table_header(45);
1388 checker.verify_symbol_table(&["currawong", "butcherbird", "bushshrike"]);
1389
1390 let primary_node_bytes = COND_JMP_BYTES + UNCOND_ABORT_BYTES + JMP_PAD_BYTES;
1391 let additional_node_bytes = COND_JMP_BYTES + UNCOND_ABORT_BYTES + JMP_PAD_BYTES;
1392 checker.verify_composite_header(
1393 (NODE_HEADER_BYTES * 2)
1394 + COMPOSITE_NAME_ID_BYTES
1395 + primary_node_bytes
1396 + additional_node_bytes,
1397 );
1398
1399 checker.verify_node_header(RawNodeType::Primary, 2, primary_node_bytes);
1400 checker.verify_jmp_if_equal(
1401 UNCOND_ABORT_BYTES,
1402 EncodedValue { value_type: RawValueType::NumberValue, value: 15 },
1403 EncodedValue { value_type: RawValueType::NumberValue, value: 5 },
1404 );
1405 checker.verify_unconditional_abort();
1406 checker.verify_jmp_pad();
1407
1408 checker.verify_node_header(RawNodeType::Additional, 3, additional_node_bytes);
1409 checker.verify_jmp_if_equal(
1410 UNCOND_ABORT_BYTES,
1411 EncodedValue { value_type: RawValueType::NumberValue, value: 10 },
1412 EncodedValue { value_type: RawValueType::NumberValue, value: 2 },
1413 );
1414 checker.verify_unconditional_abort();
1415 checker.verify_jmp_pad();
1416
1417 checker.verify_end();
1418 }
1419
1420 #[test]
1421 fn test_composite_invalid_label() {
1422 let primary_node_inst = vec![
1423 SymbolicInstruction::JumpIfEqual {
1424 lhs: Symbol::DeprecatedKey(15),
1425 rhs: Symbol::NumberValue(5),
1426 label: 1,
1427 },
1428 SymbolicInstruction::UnconditionalAbort,
1429 SymbolicInstruction::Label(1),
1430 ];
1431
1432 let additional_node_inst = vec![
1433 SymbolicInstruction::JumpIfEqual {
1434 lhs: Symbol::DeprecatedKey(10),
1435 rhs: Symbol::NumberValue(2),
1436 label: 1,
1437 },
1438 SymbolicInstruction::UnconditionalAbort,
1439 SymbolicInstruction::Label(2),
1440 ];
1441
1442 let bind_rules = CompositeBindRules {
1443 device_name: "currawong".to_string(),
1444 symbol_table: HashMap::new(),
1445 primary_node: composite_node("butcherbird".to_string(), primary_node_inst),
1446 additional_nodes: vec![composite_node("bushshrike".to_string(), additional_node_inst)],
1447 optional_nodes: vec![],
1448 enable_debug: false,
1449 };
1450
1451 assert_eq!(
1452 Err(BindRulesEncodeError::MissingLabel(1)),
1453 encode_composite_to_bytecode(bind_rules)
1454 );
1455 }
1456
1457 #[test]
1458 fn test_composite_primary_node_only() {
1459 let primary_node_inst = vec![SymbolicInstruction::UnconditionalAbort];
1460
1461 let bind_rules = CompositeBindRules {
1462 device_name: "treehunter".to_string(),
1463 symbol_table: HashMap::new(),
1464 additional_nodes: vec![],
1465 optional_nodes: vec![],
1466 primary_node: composite_node("bananaquit".to_string(), primary_node_inst),
1467 enable_debug: false,
1468 };
1469
1470 let mut checker = BytecodeChecker::new(encode_composite_to_bytecode(bind_rules).unwrap());
1471 checker.verify_bind_rules_header(false);
1472
1473 checker.verify_sym_table_header(30);
1474 checker.verify_symbol_table(&["treehunter", "bananaquit"]);
1475
1476 let primary_node_bytes = UNCOND_ABORT_BYTES;
1477 checker.verify_composite_header(
1478 NODE_HEADER_BYTES + COMPOSITE_NAME_ID_BYTES + primary_node_bytes,
1479 );
1480
1481 checker.verify_node_header(RawNodeType::Primary, 2, primary_node_bytes);
1482 checker.verify_unconditional_abort();
1483
1484 checker.verify_end();
1485 }
1486
1487 #[test]
1488 fn test_composite_empty_node_instructions() {
1489 let bind_rules = CompositeBindRules {
1490 device_name: "spiderhunter".to_string(),
1491 symbol_table: HashMap::new(),
1492 primary_node: composite_node("sunbird".to_string(), vec![]),
1493 additional_nodes: vec![],
1494 optional_nodes: vec![],
1495 enable_debug: false,
1496 };
1497
1498 let mut checker = BytecodeChecker::new(encode_composite_to_bytecode(bind_rules).unwrap());
1499 checker.verify_bind_rules_header(false);
1500
1501 checker.verify_sym_table_header(29);
1502 checker.verify_symbol_table(&["spiderhunter", "sunbird"]);
1503
1504 checker.verify_composite_header(NODE_HEADER_BYTES + COMPOSITE_NAME_ID_BYTES);
1505 checker.verify_node_header(RawNodeType::Primary, 2, 0);
1506
1507 checker.verify_end();
1508 }
1509
1510 #[test]
1511 fn test_composite_missing_device_name() {
1512 let bind_rules = CompositeBindRules {
1513 device_name: "".to_string(),
1514 symbol_table: HashMap::new(),
1515 primary_node: composite_node("pewee".to_string(), vec![]),
1516 additional_nodes: vec![],
1517 optional_nodes: vec![],
1518 enable_debug: false,
1519 };
1520
1521 assert_eq!(
1522 Err(BindRulesEncodeError::MissingCompositeDeviceName),
1523 encode_composite_to_bytecode(bind_rules)
1524 );
1525 }
1526
1527 #[test]
1528 fn test_composite_missing_node_name() {
1529 let bind_rules = CompositeBindRules {
1530 device_name: "pewee".to_string(),
1531 symbol_table: HashMap::new(),
1532 primary_node: composite_node("".to_string(), vec![]),
1533 additional_nodes: vec![],
1534 optional_nodes: vec![],
1535 enable_debug: false,
1536 };
1537
1538 assert_eq!(
1539 Err(BindRulesEncodeError::MissingCompositeNodeName),
1540 encode_composite_to_bytecode(bind_rules)
1541 );
1542 }
1543
1544 #[test]
1545 fn test_duplicate_nodes() {
1546 let bind_rules = CompositeBindRules {
1547 device_name: "flycatcher".to_string(),
1548 symbol_table: HashMap::new(),
1549 primary_node: composite_node("pewee".to_string(), vec![]),
1550 additional_nodes: vec![composite_node("pewee".to_string(), vec![])],
1551 optional_nodes: vec![],
1552 enable_debug: false,
1553 };
1554
1555 assert_eq!(
1556 Err(BindRulesEncodeError::DuplicateCompositeNodeName("pewee".to_string())),
1557 encode_composite_to_bytecode(bind_rules)
1558 );
1559
1560 let bind_rules = CompositeBindRules {
1561 device_name: "flycatcher".to_string(),
1562 symbol_table: HashMap::new(),
1563 primary_node: composite_node("pewee".to_string(), vec![]),
1564 additional_nodes: vec![
1565 composite_node("phoebe".to_string(), vec![]),
1566 composite_node("kingbird".to_string(), vec![]),
1567 composite_node("phoebe".to_string(), vec![]),
1568 ],
1569 optional_nodes: vec![],
1570 enable_debug: false,
1571 };
1572
1573 assert_eq!(
1574 Err(BindRulesEncodeError::DuplicateCompositeNodeName("phoebe".to_string())),
1575 encode_composite_to_bytecode(bind_rules)
1576 );
1577 }
1578}