sbpf_common/
validate.rs

1use crate::{errors::SBPFError, instruction::Instruction};
2
3pub fn validate_load_immediate(inst: &Instruction) -> Result<(), SBPFError> {
4    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
5        (Some(_dst), None, None, Some(_imm)) => Ok(()),
6        _ => Err(SBPFError::BytecodeError {
7            error: format!(
8                "{} instruction requires destination register and immediate value",
9                inst.opcode
10            ),
11            span: inst.span.clone(),
12            custom_label: None,
13        }),
14    }
15}
16
17pub fn validate_load_memory(inst: &Instruction) -> Result<(), SBPFError> {
18    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
19        (Some(_dst), Some(_src), Some(_off), None) => Ok(()),
20        _ => Err(SBPFError::BytecodeError {
21            error: format!(
22                "{} instruction requires destination register, source register, and offset",
23                inst.opcode
24            ),
25            span: inst.span.clone(),
26            custom_label: None,
27        }),
28    }
29}
30
31pub fn validate_store_immediate(inst: &Instruction) -> Result<(), SBPFError> {
32    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
33        (Some(_dst), None, Some(_off), Some(_imm)) => Ok(()),
34        _ => Err(SBPFError::BytecodeError {
35            error: format!(
36                "{} instruction requires destination register, offset, and immediate value",
37                inst.opcode
38            ),
39            span: inst.span.clone(),
40            custom_label: None,
41        }),
42    }
43}
44
45pub fn validate_store_register(inst: &Instruction) -> Result<(), SBPFError> {
46    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
47        (Some(_dst), Some(_src), Some(_off), None) => Ok(()),
48        _ => Err(SBPFError::BytecodeError {
49            error: format!(
50                "{} instruction requires destination register, source register, and offset",
51                inst.opcode
52            ),
53            span: inst.span.clone(),
54            custom_label: None,
55        }),
56    }
57}
58
59pub fn validate_unary(inst: &Instruction) -> Result<(), SBPFError> {
60    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
61        (Some(_dst), None, None, None) => Ok(()),
62        _ => Err(SBPFError::BytecodeError {
63            error: format!(
64                "{} instruction requires destination register only",
65                inst.opcode
66            ),
67            span: inst.span.clone(),
68            custom_label: None,
69        }),
70    }
71}
72
73pub fn validate_binary_immediate(inst: &Instruction) -> Result<(), SBPFError> {
74    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
75        (Some(_dst), None, None, Some(_imm)) => Ok(()),
76        _ => Err(SBPFError::BytecodeError {
77            error: format!(
78                "{} instruction requires destination register and immediate value",
79                inst.opcode
80            ),
81            span: inst.span.clone(),
82            custom_label: None,
83        }),
84    }
85}
86
87pub fn validate_binary_register(inst: &Instruction) -> Result<(), SBPFError> {
88    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
89        (Some(_dst), Some(_src), None, None) => Ok(()),
90        _ => Err(SBPFError::BytecodeError {
91            error: format!(
92                "{} instruction requires destination register and source register",
93                inst.opcode
94            ),
95            span: inst.span.clone(),
96            custom_label: None,
97        }),
98    }
99}
100
101pub fn validate_jump(inst: &Instruction) -> Result<(), SBPFError> {
102    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
103        (None, None, Some(_off), None) => Ok(()),
104        _ => Err(SBPFError::BytecodeError {
105            error: format!("{} instruction requires offset only", inst.opcode),
106            span: inst.span.clone(),
107            custom_label: None,
108        }),
109    }
110}
111
112pub fn validate_jump_immediate(inst: &Instruction) -> Result<(), SBPFError> {
113    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
114        (Some(_dst), None, Some(_off), Some(_imm)) => Ok(()),
115        _ => Err(SBPFError::BytecodeError {
116            error: format!(
117                "{} instruction requires destination register, offset and immediate value",
118                inst.opcode
119            ),
120            span: inst.span.clone(),
121            custom_label: None,
122        }),
123    }
124}
125
126pub fn validate_jump_register(inst: &Instruction) -> Result<(), SBPFError> {
127    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
128        (Some(_dst), Some(_src), Some(_off), None) => Ok(()),
129        _ => Err(SBPFError::BytecodeError {
130            error: format!(
131                "{} instruction requires destination register, source register, and offset",
132                inst.opcode
133            ),
134            span: inst.span.clone(),
135            custom_label: None,
136        }),
137    }
138}
139
140pub fn validate_call_immediate(inst: &Instruction) -> Result<(), SBPFError> {
141    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
142        (None, Some(_src), None, Some(_imm)) => Ok(()),
143        _ => Err(SBPFError::BytecodeError {
144            error: format!(
145                "{} instruction requires source register and immediate value",
146                inst.opcode
147            ),
148            span: inst.span.clone(),
149            custom_label: None,
150        }),
151    }
152}
153
154pub fn validate_call_register(inst: &Instruction) -> Result<(), SBPFError> {
155    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
156        (Some(_dst), None, None, None) => Ok(()),
157        _ => Err(SBPFError::BytecodeError {
158            error: format!(
159                "{} instruction requires destination register only",
160                inst.opcode
161            ),
162            span: inst.span.clone(),
163            custom_label: None,
164        }),
165    }
166}
167
168pub fn validate_exit(inst: &Instruction) -> Result<(), SBPFError> {
169    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
170        (None, None, None, None) => Ok(()),
171        _ => Err(SBPFError::BytecodeError {
172            error: format!("{} instruction requires no operands", inst.opcode),
173            span: inst.span.clone(),
174            custom_label: None,
175        }),
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    use {
182        super::*,
183        crate::{
184            inst_param::{Number, Register},
185            instruction::Instruction,
186            opcode::Opcode,
187        },
188        either::Either,
189    };
190
191    #[test]
192    fn test_validate_load_immediate_valid() {
193        let valid_inst = Instruction {
194            opcode: Opcode::Lddw,
195            dst: Some(Register { n: 0 }),
196            src: None,
197            off: None,
198            imm: Some(Either::Right(Number::Int(42))),
199            span: 0..8,
200        };
201        assert!(validate_load_immediate(&valid_inst).is_ok());
202    }
203
204    #[test]
205    fn test_validate_load_immediate_missing_dst() {
206        let inst = Instruction {
207            opcode: Opcode::Lddw,
208            dst: None,
209            src: None,
210            off: None,
211            imm: Some(Either::Right(Number::Int(42))),
212            span: 0..8,
213        };
214        let result = validate_load_immediate(&inst);
215        assert!(result.is_err());
216        if let Err(SBPFError::BytecodeError { error, .. }) = result {
217            assert_eq!(
218                error,
219                format!(
220                    "{} instruction requires destination register and immediate value",
221                    Opcode::Lddw
222                )
223            );
224        }
225    }
226
227    #[test]
228    fn test_validate_load_immediate_missing_imm() {
229        let inst = Instruction {
230            opcode: Opcode::Lddw,
231            dst: Some(Register { n: 0 }),
232            src: None,
233            off: None,
234            imm: None,
235            span: 0..8,
236        };
237        let result = validate_load_immediate(&inst);
238        assert!(result.is_err());
239        if let Err(SBPFError::BytecodeError { error, .. }) = result {
240            assert_eq!(
241                error,
242                format!(
243                    "{} instruction requires destination register and immediate value",
244                    Opcode::Lddw
245                )
246            );
247        }
248    }
249
250    #[test]
251    fn test_validate_load_immediate_has_src() {
252        let inst = Instruction {
253            opcode: Opcode::Lddw,
254            dst: Some(Register { n: 0 }),
255            src: Some(Register { n: 1 }),
256            off: None,
257            imm: Some(Either::Right(Number::Int(42))),
258            span: 0..8,
259        };
260        let result = validate_load_immediate(&inst);
261        assert!(result.is_err());
262        if let Err(SBPFError::BytecodeError { error, .. }) = result {
263            assert_eq!(
264                error,
265                format!(
266                    "{} instruction requires destination register and immediate value",
267                    Opcode::Lddw
268                )
269            );
270        }
271    }
272
273    #[test]
274    fn test_validate_load_immediate_has_offset() {
275        let inst = Instruction {
276            opcode: Opcode::Lddw,
277            dst: Some(Register { n: 0 }),
278            src: None,
279            off: Some(Either::Right(10)),
280            imm: Some(Either::Right(Number::Int(42))),
281            span: 0..8,
282        };
283        let result = validate_load_immediate(&inst);
284        assert!(result.is_err());
285        if let Err(SBPFError::BytecodeError { error, .. }) = result {
286            assert_eq!(
287                error,
288                format!(
289                    "{} instruction requires destination register and immediate value",
290                    Opcode::Lddw
291                )
292            );
293        }
294    }
295
296    #[test]
297    fn test_validate_load_memory_valid() {
298        let valid_inst = Instruction {
299            opcode: Opcode::Ldxw,
300            dst: Some(Register { n: 0 }),
301            src: Some(Register { n: 1 }),
302            off: Some(Either::Right(8)),
303            imm: None,
304            span: 0..8,
305        };
306        assert!(validate_load_memory(&valid_inst).is_ok());
307    }
308
309    #[test]
310    fn test_validate_load_memory_missing_dst() {
311        let inst = Instruction {
312            opcode: Opcode::Ldxw,
313            dst: None,
314            src: Some(Register { n: 1 }),
315            off: Some(Either::Right(8)),
316            imm: None,
317            span: 0..8,
318        };
319        let result = validate_load_memory(&inst);
320        assert!(result.is_err());
321        if let Err(SBPFError::BytecodeError { error, .. }) = result {
322            assert_eq!(
323                error,
324                format!(
325                    "{} instruction requires destination register, source register, and offset",
326                    Opcode::Ldxw
327                )
328            );
329        }
330    }
331
332    #[test]
333    fn test_validate_load_memory_has_imm() {
334        let inst = Instruction {
335            opcode: Opcode::Ldxw,
336            dst: Some(Register { n: 0 }),
337            src: Some(Register { n: 1 }),
338            off: Some(Either::Right(8)),
339            imm: Some(Either::Right(Number::Int(42))),
340            span: 0..8,
341        };
342        let result = validate_load_memory(&inst);
343        assert!(result.is_err());
344        if let Err(SBPFError::BytecodeError { error, .. }) = result {
345            assert_eq!(
346                error,
347                format!(
348                    "{} instruction requires destination register, source register, and offset",
349                    Opcode::Ldxw
350                )
351            );
352        }
353    }
354
355    #[test]
356    fn test_validate_load_memory_missing_src() {
357        let inst = Instruction {
358            opcode: Opcode::Ldxw,
359            dst: Some(Register { n: 0 }),
360            src: None,
361            off: Some(Either::Right(8)),
362            imm: None,
363            span: 0..8,
364        };
365        let result = validate_load_memory(&inst);
366        assert!(result.is_err());
367        if let Err(SBPFError::BytecodeError { error, .. }) = result {
368            assert_eq!(
369                error,
370                format!(
371                    "{} instruction requires destination register, source register, and offset",
372                    Opcode::Ldxw
373                )
374            );
375        }
376    }
377
378    #[test]
379    fn test_validate_store_immediate_valid() {
380        let valid_inst = Instruction {
381            opcode: Opcode::Stw,
382            dst: Some(Register { n: 0 }),
383            src: None,
384            off: Some(Either::Right(8)),
385            imm: Some(Either::Right(Number::Int(42))),
386            span: 0..8,
387        };
388        assert!(validate_store_immediate(&valid_inst).is_ok());
389    }
390
391    #[test]
392    fn test_validate_store_immediate_missing_imm() {
393        let inst = Instruction {
394            opcode: Opcode::Stw,
395            dst: Some(Register { n: 0 }),
396            src: None,
397            off: Some(Either::Right(8)),
398            imm: None,
399            span: 0..8,
400        };
401        let result = validate_store_immediate(&inst);
402        assert!(result.is_err());
403        if let Err(SBPFError::BytecodeError { error, .. }) = result {
404            assert_eq!(
405                error,
406                format!(
407                    "{} instruction requires destination register, offset, and immediate value",
408                    Opcode::Stw
409                )
410            );
411        }
412    }
413
414    #[test]
415    fn test_validate_store_immediate_has_src() {
416        let inst = Instruction {
417            opcode: Opcode::Stw,
418            dst: Some(Register { n: 0 }),
419            src: Some(Register { n: 1 }),
420            off: Some(Either::Right(8)),
421            imm: Some(Either::Right(Number::Int(42))),
422            span: 0..8,
423        };
424        let result = validate_store_immediate(&inst);
425        assert!(result.is_err());
426        if let Err(SBPFError::BytecodeError { error, .. }) = result {
427            assert_eq!(
428                error,
429                format!(
430                    "{} instruction requires destination register, offset, and immediate value",
431                    Opcode::Stw
432                )
433            );
434        }
435    }
436
437    #[test]
438    fn test_validate_store_register_valid() {
439        let valid_inst = Instruction {
440            opcode: Opcode::Stxw,
441            dst: Some(Register { n: 0 }),
442            src: Some(Register { n: 1 }),
443            off: Some(Either::Right(8)),
444            imm: None,
445            span: 0..8,
446        };
447        assert!(validate_store_register(&valid_inst).is_ok());
448    }
449
450    #[test]
451    fn test_validate_store_register_missing_src() {
452        let inst = Instruction {
453            opcode: Opcode::Stxw,
454            dst: Some(Register { n: 0 }),
455            src: None,
456            off: Some(Either::Right(8)),
457            imm: None,
458            span: 0..8,
459        };
460        let result = validate_store_register(&inst);
461        assert!(result.is_err());
462        if let Err(SBPFError::BytecodeError { error, .. }) = result {
463            assert_eq!(
464                error,
465                format!(
466                    "{} instruction requires destination register, source register, and offset",
467                    Opcode::Stxw
468                )
469            );
470        }
471    }
472
473    #[test]
474    fn test_validate_store_register_has_imm() {
475        let inst = Instruction {
476            opcode: Opcode::Stxw,
477            dst: Some(Register { n: 0 }),
478            src: Some(Register { n: 1 }),
479            off: Some(Either::Right(8)),
480            imm: Some(Either::Right(Number::Int(42))),
481            span: 0..8,
482        };
483        let result = validate_store_register(&inst);
484        assert!(result.is_err());
485        if let Err(SBPFError::BytecodeError { error, .. }) = result {
486            assert_eq!(
487                error,
488                format!(
489                    "{} instruction requires destination register, source register, and offset",
490                    Opcode::Stxw
491                )
492            );
493        }
494    }
495
496    #[test]
497    fn test_validate_unary_valid() {
498        let valid_inst = Instruction {
499            opcode: Opcode::Neg64,
500            dst: Some(Register { n: 0 }),
501            src: None,
502            off: None,
503            imm: None,
504            span: 0..8,
505        };
506        assert!(validate_unary(&valid_inst).is_ok());
507    }
508
509    #[test]
510    fn test_validate_unary_missing_dst() {
511        let inst = Instruction {
512            opcode: Opcode::Neg64,
513            dst: None,
514            src: None,
515            off: None,
516            imm: None,
517            span: 0..8,
518        };
519        let result = validate_unary(&inst);
520        assert!(result.is_err());
521        if let Err(SBPFError::BytecodeError { error, .. }) = result {
522            assert_eq!(
523                error,
524                format!(
525                    "{} instruction requires destination register only",
526                    Opcode::Neg64
527                )
528            );
529        }
530    }
531
532    #[test]
533    fn test_validate_unary_has_src() {
534        let inst = Instruction {
535            opcode: Opcode::Neg64,
536            dst: Some(Register { n: 0 }),
537            src: Some(Register { n: 1 }),
538            off: None,
539            imm: None,
540            span: 0..8,
541        };
542        let result = validate_unary(&inst);
543        assert!(result.is_err());
544        if let Err(SBPFError::BytecodeError { error, .. }) = result {
545            assert_eq!(
546                error,
547                format!(
548                    "{} instruction requires destination register only",
549                    Opcode::Neg64
550                )
551            );
552        }
553    }
554
555    #[test]
556    fn test_validate_unary_has_offset() {
557        let inst = Instruction {
558            opcode: Opcode::Neg64,
559            dst: Some(Register { n: 0 }),
560            src: None,
561            off: Some(Either::Right(10)),
562            imm: None,
563            span: 0..8,
564        };
565        let result = validate_unary(&inst);
566        assert!(result.is_err());
567        if let Err(SBPFError::BytecodeError { error, .. }) = result {
568            assert_eq!(
569                error,
570                format!(
571                    "{} instruction requires destination register only",
572                    Opcode::Neg64
573                )
574            );
575        }
576    }
577
578    #[test]
579    fn test_validate_unary_has_imm() {
580        let inst = Instruction {
581            opcode: Opcode::Neg64,
582            dst: Some(Register { n: 0 }),
583            src: None,
584            off: None,
585            imm: Some(Either::Right(Number::Int(42))),
586            span: 0..8,
587        };
588        let result = validate_unary(&inst);
589        assert!(result.is_err());
590        if let Err(SBPFError::BytecodeError { error, .. }) = result {
591            assert_eq!(
592                error,
593                format!(
594                    "{} instruction requires destination register only",
595                    Opcode::Neg64
596                )
597            );
598        }
599    }
600
601    #[test]
602    fn test_validate_binary_immediate_valid() {
603        let valid_inst = Instruction {
604            opcode: Opcode::Le,
605            dst: Some(Register { n: 0 }),
606            src: None,
607            off: None,
608            imm: Some(Either::Right(Number::Int(16))),
609            span: 0..8,
610        };
611        assert!(validate_binary_immediate(&valid_inst).is_ok());
612    }
613
614    #[test]
615    fn test_validate_binary_immediate_missing_dst() {
616        let inst = Instruction {
617            opcode: Opcode::Le,
618            dst: None,
619            src: None,
620            off: None,
621            imm: Some(Either::Right(Number::Int(16))),
622            span: 0..8,
623        };
624        let result = validate_binary_immediate(&inst);
625        assert!(result.is_err());
626        if let Err(SBPFError::BytecodeError { error, .. }) = result {
627            assert_eq!(
628                error,
629                format!(
630                    "{} instruction requires destination register and immediate value",
631                    Opcode::Le
632                )
633            );
634        }
635    }
636
637    #[test]
638    fn test_validate_binary_immediate_missing_imm() {
639        let inst = Instruction {
640            opcode: Opcode::Le,
641            dst: Some(Register { n: 0 }),
642            src: None,
643            off: None,
644            imm: None,
645            span: 0..8,
646        };
647        let result = validate_binary_immediate(&inst);
648        assert!(result.is_err());
649        if let Err(SBPFError::BytecodeError { error, .. }) = result {
650            assert_eq!(
651                error,
652                format!(
653                    "{} instruction requires destination register and immediate value",
654                    Opcode::Le
655                )
656            );
657        }
658    }
659
660    #[test]
661    fn test_validate_binary_immediate_has_src() {
662        let inst = Instruction {
663            opcode: Opcode::Le,
664            dst: Some(Register { n: 0 }),
665            src: Some(Register { n: 1 }),
666            off: None,
667            imm: Some(Either::Right(Number::Int(16))),
668            span: 0..8,
669        };
670        let result = validate_binary_immediate(&inst);
671        assert!(result.is_err());
672        if let Err(SBPFError::BytecodeError { error, .. }) = result {
673            assert_eq!(
674                error,
675                format!(
676                    "{} instruction requires destination register and immediate value",
677                    Opcode::Le
678                )
679            );
680        }
681    }
682
683    #[test]
684    fn test_validate_binary_immediate_has_offset() {
685        let inst = Instruction {
686            opcode: Opcode::Le,
687            dst: Some(Register { n: 0 }),
688            src: None,
689            off: Some(Either::Right(10)),
690            imm: Some(Either::Right(Number::Int(16))),
691            span: 0..8,
692        };
693        let result = validate_binary_immediate(&inst);
694        assert!(result.is_err());
695        if let Err(SBPFError::BytecodeError { error, .. }) = result {
696            assert_eq!(
697                error,
698                format!(
699                    "{} instruction requires destination register and immediate value",
700                    Opcode::Le
701                )
702            );
703        }
704    }
705
706    #[test]
707    fn test_validate_binary_register_valid() {
708        let valid_inst = Instruction {
709            opcode: Opcode::Add64Reg,
710            dst: Some(Register { n: 0 }),
711            src: Some(Register { n: 1 }),
712            off: None,
713            imm: None,
714            span: 0..8,
715        };
716        assert!(validate_binary_register(&valid_inst).is_ok());
717    }
718
719    #[test]
720    fn test_validate_binary_register_missing_dst() {
721        let inst = Instruction {
722            opcode: Opcode::Add64Reg,
723            dst: None,
724            src: Some(Register { n: 1 }),
725            off: None,
726            imm: None,
727            span: 0..8,
728        };
729        let result = validate_binary_register(&inst);
730        assert!(result.is_err());
731        if let Err(SBPFError::BytecodeError { error, .. }) = result {
732            assert_eq!(
733                error,
734                format!(
735                    "{} instruction requires destination register and source register",
736                    Opcode::Add64Reg
737                )
738            );
739        }
740    }
741
742    #[test]
743    fn test_validate_binary_register_missing_src() {
744        let inst = Instruction {
745            opcode: Opcode::Add64Reg,
746            dst: Some(Register { n: 0 }),
747            src: None,
748            off: None,
749            imm: None,
750            span: 0..8,
751        };
752        let result = validate_binary_register(&inst);
753        assert!(result.is_err());
754        if let Err(SBPFError::BytecodeError { error, .. }) = result {
755            assert_eq!(
756                error,
757                format!(
758                    "{} instruction requires destination register and source register",
759                    Opcode::Add64Reg
760                )
761            );
762        }
763    }
764
765    #[test]
766    fn test_validate_binary_register_has_offset() {
767        let inst = Instruction {
768            opcode: Opcode::Add64Reg,
769            dst: Some(Register { n: 0 }),
770            src: Some(Register { n: 1 }),
771            off: Some(Either::Right(10)),
772            imm: None,
773            span: 0..8,
774        };
775        let result = validate_binary_register(&inst);
776        assert!(result.is_err());
777        if let Err(SBPFError::BytecodeError { error, .. }) = result {
778            assert_eq!(
779                error,
780                format!(
781                    "{} instruction requires destination register and source register",
782                    Opcode::Add64Reg
783                )
784            );
785        }
786    }
787
788    #[test]
789    fn test_validate_binary_register_has_imm() {
790        let inst = Instruction {
791            opcode: Opcode::Add64Reg,
792            dst: Some(Register { n: 0 }),
793            src: Some(Register { n: 1 }),
794            off: None,
795            imm: Some(Either::Right(Number::Int(42))),
796            span: 0..8,
797        };
798        let result = validate_binary_register(&inst);
799        assert!(result.is_err());
800        if let Err(SBPFError::BytecodeError { error, .. }) = result {
801            assert_eq!(
802                error,
803                format!(
804                    "{} instruction requires destination register and source register",
805                    Opcode::Add64Reg
806                )
807            );
808        }
809    }
810
811    #[test]
812    fn test_validate_jump_valid() {
813        let valid_inst = Instruction {
814            opcode: Opcode::Ja,
815            dst: None,
816            src: None,
817            off: Some(Either::Right(10)),
818            imm: None,
819            span: 0..8,
820        };
821        assert!(validate_jump(&valid_inst).is_ok());
822    }
823
824    #[test]
825    fn test_validate_jump_valid_has_dst() {
826        let inst = Instruction {
827            opcode: Opcode::Ja,
828            dst: Some(Register { n: 0 }),
829            src: None,
830            off: Some(Either::Right(10)),
831            imm: None,
832            span: 0..8,
833        };
834        let result = validate_jump(&inst);
835        assert!(result.is_err());
836        if let Err(SBPFError::BytecodeError { error, .. }) = result {
837            assert_eq!(
838                error,
839                format!("{} instruction requires offset only", Opcode::Ja)
840            );
841        }
842    }
843
844    #[test]
845    fn test_validate_jump_has_src() {
846        let inst = Instruction {
847            opcode: Opcode::Ja,
848            dst: None,
849            src: Some(Register { n: 1 }),
850            off: Some(Either::Right(10)),
851            imm: None,
852            span: 0..8,
853        };
854        let result = validate_jump(&inst);
855        assert!(result.is_err());
856        if let Err(SBPFError::BytecodeError { error, .. }) = result {
857            assert_eq!(
858                error,
859                format!("{} instruction requires offset only", Opcode::Ja)
860            );
861        }
862    }
863
864    #[test]
865    fn test_validate_jump_has_imm() {
866        let inst = Instruction {
867            opcode: Opcode::Ja,
868            dst: None,
869            src: None,
870            off: Some(Either::Right(10)),
871            imm: Some(Either::Right(Number::Int(42))),
872            span: 0..8,
873        };
874        let result = validate_jump(&inst);
875        assert!(result.is_err());
876        if let Err(SBPFError::BytecodeError { error, .. }) = result {
877            assert_eq!(
878                error,
879                format!("{} instruction requires offset only", Opcode::Ja)
880            );
881        }
882    }
883
884    #[test]
885    fn test_validate_jump_immediate_valid() {
886        let valid_inst = Instruction {
887            opcode: Opcode::JeqImm,
888            dst: Some(Register { n: 0 }),
889            src: None,
890            off: Some(Either::Right(10)),
891            imm: Some(Either::Right(Number::Int(42))),
892            span: 0..8,
893        };
894        assert!(validate_jump_immediate(&valid_inst).is_ok());
895    }
896
897    #[test]
898    fn test_validate_jump_immediate_missing_dst() {
899        let inst = Instruction {
900            opcode: Opcode::JeqImm,
901            dst: None,
902            src: None,
903            off: Some(Either::Right(10)),
904            imm: Some(Either::Right(Number::Int(42))),
905            span: 0..8,
906        };
907        let result = validate_jump_immediate(&inst);
908        assert!(result.is_err());
909        if let Err(SBPFError::BytecodeError { error, .. }) = result {
910            assert_eq!(
911                error,
912                format!(
913                    "{} instruction requires destination register, offset and immediate value",
914                    Opcode::JeqImm
915                )
916            );
917        }
918    }
919
920    #[test]
921    fn test_validate_jump_immediate_missing_imm() {
922        let inst = Instruction {
923            opcode: Opcode::JeqImm,
924            dst: Some(Register { n: 0 }),
925            src: None,
926            off: Some(Either::Right(10)),
927            imm: None,
928            span: 0..8,
929        };
930        let result = validate_jump_immediate(&inst);
931        assert!(result.is_err());
932        if let Err(SBPFError::BytecodeError { error, .. }) = result {
933            assert_eq!(
934                error,
935                format!(
936                    "{} instruction requires destination register, offset and immediate value",
937                    Opcode::JeqImm
938                )
939            );
940        }
941    }
942
943    #[test]
944    fn test_validate_jump_register_valid() {
945        let valid_inst = Instruction {
946            opcode: Opcode::JeqReg,
947            dst: Some(Register { n: 0 }),
948            src: Some(Register { n: 1 }),
949            off: Some(Either::Right(10)),
950            imm: None,
951            span: 0..8,
952        };
953        assert!(validate_jump_register(&valid_inst).is_ok());
954    }
955
956    #[test]
957    fn test_validate_jump_register_missing_dst() {
958        let inst = Instruction {
959            opcode: Opcode::JeqReg,
960            dst: None,
961            src: Some(Register { n: 1 }),
962            off: Some(Either::Right(10)),
963            imm: None,
964            span: 0..8,
965        };
966        let result = validate_jump_register(&inst);
967        assert!(result.is_err());
968        if let Err(SBPFError::BytecodeError { error, .. }) = result {
969            assert_eq!(
970                error,
971                format!(
972                    "{} instruction requires destination register, source register, and offset",
973                    Opcode::JeqReg
974                )
975            );
976        }
977    }
978
979    #[test]
980    fn test_validate_jump_register_missing_src() {
981        let inst = Instruction {
982            opcode: Opcode::JeqReg,
983            dst: Some(Register { n: 0 }),
984            src: None,
985            off: Some(Either::Right(10)),
986            imm: None,
987            span: 0..8,
988        };
989        let result = validate_jump_register(&inst);
990        assert!(result.is_err());
991        if let Err(SBPFError::BytecodeError { error, .. }) = result {
992            assert_eq!(
993                error,
994                format!(
995                    "{} instruction requires destination register, source register, and offset",
996                    Opcode::JeqReg
997                )
998            );
999        }
1000    }
1001
1002    #[test]
1003    fn test_validate_jump_register_missing_offset() {
1004        let inst = Instruction {
1005            opcode: Opcode::JeqReg,
1006            dst: Some(Register { n: 0 }),
1007            src: Some(Register { n: 1 }),
1008            off: None,
1009            imm: None,
1010            span: 0..8,
1011        };
1012        let result = validate_jump_register(&inst);
1013        assert!(result.is_err());
1014        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1015            assert_eq!(
1016                error,
1017                format!(
1018                    "{} instruction requires destination register, source register, and offset",
1019                    Opcode::JeqReg
1020                )
1021            );
1022        }
1023    }
1024
1025    #[test]
1026    fn test_validate_jump_register_has_imm() {
1027        let inst = Instruction {
1028            opcode: Opcode::JeqReg,
1029            dst: Some(Register { n: 0 }),
1030            src: Some(Register { n: 1 }),
1031            off: Some(Either::Right(10)),
1032            imm: Some(Either::Right(Number::Int(42))),
1033            span: 0..8,
1034        };
1035        let result = validate_jump_register(&inst);
1036        assert!(result.is_err());
1037        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1038            assert_eq!(
1039                error,
1040                format!(
1041                    "{} instruction requires destination register, source register, and offset",
1042                    Opcode::JeqReg
1043                )
1044            );
1045        }
1046    }
1047
1048    #[test]
1049    fn test_validate_call_immediate_valid() {
1050        let valid_inst = Instruction {
1051            opcode: Opcode::Call,
1052            dst: None,
1053            src: Some(Register { n: 1 }),
1054            off: None,
1055            imm: Some(Either::Right(Number::Int(100))),
1056            span: 0..8,
1057        };
1058        assert!(validate_call_immediate(&valid_inst).is_ok());
1059    }
1060
1061    #[test]
1062    fn test_validate_call_immediate_missing_imm() {
1063        let inst = Instruction {
1064            opcode: Opcode::Call,
1065            dst: None,
1066            src: None,
1067            off: None,
1068            imm: None,
1069            span: 0..8,
1070        };
1071        let result = validate_call_immediate(&inst);
1072        assert!(result.is_err());
1073        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1074            assert_eq!(
1075                error,
1076                format!(
1077                    "{} instruction requires source register and immediate value",
1078                    Opcode::Call
1079                )
1080            );
1081        }
1082    }
1083
1084    #[test]
1085    fn test_validate_call_immediate_has_dst() {
1086        let inst = Instruction {
1087            opcode: Opcode::Call,
1088            dst: Some(Register { n: 0 }),
1089            src: None,
1090            off: None,
1091            imm: Some(Either::Right(Number::Int(100))),
1092            span: 0..8,
1093        };
1094        let result = validate_call_immediate(&inst);
1095        assert!(result.is_err());
1096        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1097            assert_eq!(
1098                error,
1099                format!(
1100                    "{} instruction requires source register and immediate value",
1101                    Opcode::Call
1102                )
1103            );
1104        }
1105    }
1106
1107    #[test]
1108    fn test_validate_call_immediate_has_offset() {
1109        let inst = Instruction {
1110            opcode: Opcode::Call,
1111            dst: None,
1112            src: None,
1113            off: Some(Either::Right(10)),
1114            imm: Some(Either::Right(Number::Int(100))),
1115            span: 0..8,
1116        };
1117        let result = validate_call_immediate(&inst);
1118        assert!(result.is_err());
1119        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1120            assert_eq!(
1121                error,
1122                format!(
1123                    "{} instruction requires source register and immediate value",
1124                    Opcode::Call
1125                )
1126            );
1127        }
1128    }
1129
1130    #[test]
1131    fn test_validate_call_register_valid() {
1132        let valid_inst = Instruction {
1133            opcode: Opcode::Callx,
1134            dst: Some(Register { n: 1 }),
1135            src: None,
1136            off: None,
1137            imm: None,
1138            span: 0..8,
1139        };
1140        assert!(validate_call_register(&valid_inst).is_ok());
1141    }
1142
1143    #[test]
1144    fn test_validate_call_register_missing_dst() {
1145        let inst = Instruction {
1146            opcode: Opcode::Callx,
1147            dst: None,
1148            src: None,
1149            off: None,
1150            imm: None,
1151            span: 0..8,
1152        };
1153        let result = validate_call_register(&inst);
1154        assert!(result.is_err());
1155        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1156            assert_eq!(
1157                error,
1158                format!(
1159                    "{} instruction requires destination register only",
1160                    Opcode::Callx
1161                )
1162            );
1163        }
1164    }
1165
1166    #[test]
1167    fn test_validate_call_register_has_src() {
1168        let inst = Instruction {
1169            opcode: Opcode::Callx,
1170            dst: Some(Register { n: 0 }),
1171            src: Some(Register { n: 1 }),
1172            off: None,
1173            imm: None,
1174            span: 0..8,
1175        };
1176        let result = validate_call_register(&inst);
1177        assert!(result.is_err());
1178        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1179            assert_eq!(
1180                error,
1181                format!(
1182                    "{} instruction requires destination register only",
1183                    Opcode::Callx
1184                )
1185            );
1186        }
1187    }
1188
1189    #[test]
1190    fn test_validate_call_register_has_offset() {
1191        let inst = Instruction {
1192            opcode: Opcode::Callx,
1193            dst: Some(Register { n: 1 }),
1194            src: None,
1195            off: Some(Either::Right(10)),
1196            imm: None,
1197            span: 0..8,
1198        };
1199        let result = validate_call_register(&inst);
1200        assert!(result.is_err());
1201        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1202            assert_eq!(
1203                error,
1204                format!(
1205                    "{} instruction requires destination register only",
1206                    Opcode::Callx
1207                )
1208            );
1209        }
1210    }
1211
1212    #[test]
1213    fn test_validate_call_register_has_imm() {
1214        let inst = Instruction {
1215            opcode: Opcode::Callx,
1216            dst: Some(Register { n: 1 }),
1217            src: None,
1218            off: None,
1219            imm: Some(Either::Right(Number::Int(100))),
1220            span: 0..8,
1221        };
1222        let result = validate_call_register(&inst);
1223        assert!(result.is_err());
1224        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1225            assert_eq!(
1226                error,
1227                format!(
1228                    "{} instruction requires destination register only",
1229                    Opcode::Callx
1230                )
1231            );
1232        }
1233    }
1234
1235    #[test]
1236    fn test_validate_exit_valid() {
1237        let valid_inst = Instruction {
1238            opcode: Opcode::Exit,
1239            dst: None,
1240            src: None,
1241            off: None,
1242            imm: None,
1243            span: 0..8,
1244        };
1245        assert!(validate_exit(&valid_inst).is_ok());
1246    }
1247
1248    #[test]
1249    fn test_validate_exit_has_dst() {
1250        let inst = Instruction {
1251            opcode: Opcode::Exit,
1252            dst: Some(Register { n: 0 }),
1253            src: None,
1254            off: None,
1255            imm: None,
1256            span: 0..8,
1257        };
1258        let result = validate_exit(&inst);
1259        assert!(result.is_err());
1260        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1261            assert_eq!(
1262                error,
1263                format!("{} instruction requires no operands", Opcode::Exit)
1264            );
1265        }
1266    }
1267
1268    #[test]
1269    fn test_validate_exit_has_src() {
1270        let inst = Instruction {
1271            opcode: Opcode::Exit,
1272            dst: None,
1273            src: Some(Register { n: 1 }),
1274            off: None,
1275            imm: None,
1276            span: 0..8,
1277        };
1278        let result = validate_exit(&inst);
1279        assert!(result.is_err());
1280        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1281            assert_eq!(
1282                error,
1283                format!("{} instruction requires no operands", Opcode::Exit)
1284            );
1285        }
1286    }
1287
1288    #[test]
1289    fn test_validate_exit_has_offset() {
1290        let inst = Instruction {
1291            opcode: Opcode::Exit,
1292            dst: None,
1293            src: None,
1294            off: Some(Either::Right(10)),
1295            imm: None,
1296            span: 0..8,
1297        };
1298        let result = validate_exit(&inst);
1299        assert!(result.is_err());
1300        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1301            assert_eq!(
1302                error,
1303                format!("{} instruction requires no operands", Opcode::Exit)
1304            );
1305        }
1306    }
1307
1308    #[test]
1309    fn test_validate_exit_has_imm() {
1310        let inst = Instruction {
1311            opcode: Opcode::Exit,
1312            dst: None,
1313            src: None,
1314            off: None,
1315            imm: Some(Either::Right(Number::Int(0))),
1316            span: 0..8,
1317        };
1318        let result = validate_exit(&inst);
1319        assert!(result.is_err());
1320        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1321            assert_eq!(
1322                error,
1323                format!("{} instruction requires no operands", Opcode::Exit)
1324            );
1325        }
1326    }
1327}