1#![allow(
14 clippy::cast_possible_truncation,
15 clippy::cast_possible_wrap,
16 clippy::cast_sign_loss
17)]
18
19use serde::Serialize;
20
21use crate::instruction::{Instruction, OpCode, Operand};
22use crate::manifest::ContractManifest;
23
24use super::{MethodRef, MethodTable};
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
28#[non_exhaustive]
29pub enum ValueType {
30 #[serde(rename = "unknown")]
32 Unknown,
33 #[serde(rename = "any")]
35 Any,
36 #[serde(rename = "null")]
38 Null,
39 #[serde(rename = "bool")]
41 Boolean,
42 #[serde(rename = "integer")]
44 Integer,
45 #[serde(rename = "bytestring")]
47 ByteString,
48 #[serde(rename = "buffer")]
50 Buffer,
51 #[serde(rename = "array")]
53 Array,
54 #[serde(rename = "struct")]
56 Struct,
57 #[serde(rename = "map")]
59 Map,
60 #[serde(rename = "interopinterface")]
62 InteropInterface,
63 #[serde(rename = "pointer")]
65 Pointer,
66}
67
68impl ValueType {
69 fn join(self, other: Self) -> Self {
70 use ValueType::*;
71 if self == other {
72 return self;
73 }
74 match (self, other) {
75 (Unknown, x) | (x, Unknown) => x,
76 (Null, _) | (_, Null) => Any,
77 _ => Any,
78 }
79 }
80}
81
82#[derive(Debug, Clone, Copy)]
83struct StackValue {
84 ty: ValueType,
85 int_literal: Option<i64>,
86}
87
88impl StackValue {
89 fn unknown() -> Self {
90 Self {
91 ty: ValueType::Unknown,
92 int_literal: None,
93 }
94 }
95
96 fn with_type(ty: ValueType) -> Self {
97 Self {
98 ty,
99 int_literal: None,
100 }
101 }
102
103 fn integer_literal(value: i64) -> Self {
104 Self {
105 ty: ValueType::Integer,
106 int_literal: Some(value),
107 }
108 }
109}
110
111#[derive(Debug, Clone, Serialize)]
113pub struct MethodTypes {
114 pub method: MethodRef,
116 pub arguments: Vec<ValueType>,
118 pub locals: Vec<ValueType>,
120}
121
122#[derive(Debug, Clone, Default, Serialize)]
124pub struct TypeInfo {
125 pub methods: Vec<MethodTypes>,
127 pub statics: Vec<ValueType>,
129}
130
131#[must_use]
133pub fn infer_types(instructions: &[Instruction], manifest: Option<&ContractManifest>) -> TypeInfo {
134 let table = MethodTable::new(instructions, manifest);
135 let static_count = scan_static_slot_count(instructions).unwrap_or(0);
136 let mut statics = vec![ValueType::Unknown; static_count];
137
138 let mut methods = Vec::new();
139 let mut cursor = 0usize;
144 for span in table.spans() {
145 while cursor < instructions.len() && instructions[cursor].offset < span.start {
146 cursor += 1;
147 }
148 let begin = cursor;
149 while cursor < instructions.len() && instructions[cursor].offset < span.end {
150 cursor += 1;
151 }
152 let slice: Vec<&Instruction> = instructions[begin..cursor].iter().collect();
153
154 let (locals_count, args_count) = scan_slot_counts(&slice).unwrap_or((0, 0));
155 let mut locals = vec![ValueType::Unknown; locals_count];
156 let mut arguments = vec![ValueType::Unknown; args_count];
157
158 if let Some(manifest) = manifest {
159 if let Some(index) = table.manifest_index_for_start(span.start) {
160 if let Some(method) = manifest.abi.methods.get(index) {
161 if arguments.len() < method.parameters.len() {
162 arguments.resize(method.parameters.len(), ValueType::Unknown);
163 }
164 for (idx, param) in method.parameters.iter().enumerate() {
165 arguments[idx] = arguments[idx].join(type_from_manifest(¶m.kind));
166 }
167 }
168 }
169 }
170
171 infer_types_in_slice(&slice, &mut locals, &mut arguments, &mut statics);
172
173 methods.push(MethodTypes {
174 method: span.method.clone(),
175 arguments,
176 locals,
177 });
178 }
179
180 TypeInfo { methods, statics }
181}
182
183fn infer_types_in_slice(
184 instructions: &[&Instruction],
185 locals: &mut Vec<ValueType>,
186 arguments: &mut Vec<ValueType>,
187 statics: &mut Vec<ValueType>,
188) {
189 let mut stack: Vec<StackValue> = Vec::new();
190
191 for instr in instructions {
192 match instr.opcode {
193 OpCode::Initsslot => {
194 if let Some(Operand::U8(count)) = &instr.operand {
195 let need = *count as usize;
196 if statics.len() < need {
197 statics.resize(need, ValueType::Unknown);
198 }
199 }
200 }
201 OpCode::Initslot => {
202 if let Some(Operand::Bytes(bytes)) = &instr.operand {
204 if bytes.len() == 2 {
205 let locals_count = bytes[0] as usize;
206 let args_count = bytes[1] as usize;
207 if locals.len() < locals_count {
208 locals.resize(locals_count, ValueType::Unknown);
209 }
210 if arguments.len() < args_count {
211 arguments.resize(args_count, ValueType::Unknown);
212 }
213 }
214 }
215 }
216
217 OpCode::PushNull => stack.push(StackValue::with_type(ValueType::Null)),
219 OpCode::PushT | OpCode::PushF => stack.push(StackValue::with_type(ValueType::Boolean)),
220 OpCode::Pushdata1 | OpCode::Pushdata2 | OpCode::Pushdata4 => {
221 stack.push(StackValue::with_type(ValueType::ByteString));
222 }
223 OpCode::Pushint8
224 | OpCode::Pushint16
225 | OpCode::Pushint32
226 | OpCode::Pushint64
227 | OpCode::Pushint128
228 | OpCode::Pushint256
229 | OpCode::PushM1
230 | OpCode::Push0
231 | OpCode::Push1
232 | OpCode::Push2
233 | OpCode::Push3
234 | OpCode::Push4
235 | OpCode::Push5
236 | OpCode::Push6
237 | OpCode::Push7
238 | OpCode::Push8
239 | OpCode::Push9
240 | OpCode::Push10
241 | OpCode::Push11
242 | OpCode::Push12
243 | OpCode::Push13
244 | OpCode::Push14
245 | OpCode::Push15
246 | OpCode::Push16 => {
247 if let Some(lit) = int_literal_from_operand(instr.operand.as_ref()) {
248 stack.push(StackValue::integer_literal(lit));
249 } else {
250 stack.push(StackValue::with_type(ValueType::Integer));
251 }
252 }
253 OpCode::PushA => stack.push(StackValue::with_type(ValueType::Pointer)),
254
255 OpCode::Clear => stack.clear(),
257 OpCode::Depth => stack.push(StackValue::integer_literal(stack.len() as i64)),
258 OpCode::Drop => {
259 let _ = pop_or_unknown(&mut stack);
260 }
261 OpCode::Dup => {
262 let value = stack.last().copied().unwrap_or_else(StackValue::unknown);
263 stack.push(value);
264 }
265 OpCode::Swap if stack.len() >= 2 => {
266 let len = stack.len();
267 stack.swap(len - 1, len - 2);
268 }
269 OpCode::Over => {
270 let value = stack
271 .get(stack.len().saturating_sub(2))
272 .copied()
273 .unwrap_or_else(StackValue::unknown);
274 stack.push(value);
275 }
276 OpCode::Nip if stack.len() >= 2 => {
277 let len = stack.len();
278 stack.remove(len - 2);
279 }
280 OpCode::Rot => {
281 if let (Some(top), Some(mid), Some(bottom)) =
282 (stack.pop(), stack.pop(), stack.pop())
283 {
284 stack.push(mid);
285 stack.push(top);
286 stack.push(bottom);
287 }
288 }
289 OpCode::Tuck => {
290 if let (Some(top), Some(second)) = (stack.pop(), stack.pop()) {
291 stack.push(top);
292 stack.push(second);
293 stack.push(top);
294 }
295 }
296 OpCode::Pick => {
297 let index = pop_or_unknown(&mut stack);
298 if let Some(depth) = index.int_literal.and_then(|v| usize::try_from(v).ok()) {
299 let pos = stack.len().checked_sub(1 + depth);
300 if let Some(pos) = pos {
301 if let Some(value) = stack.get(pos).copied() {
302 stack.push(value);
303 continue;
304 }
305 }
306 }
307 stack.push(StackValue::unknown());
308 }
309 OpCode::Roll => {
310 let index = pop_or_unknown(&mut stack);
311 if let Some(depth) = index.int_literal.and_then(|v| usize::try_from(v).ok()) {
312 if depth < stack.len() {
313 let pos = stack.len() - 1 - depth;
314 let value = stack.remove(pos);
315 stack.push(value);
316 }
317 }
318 }
319 OpCode::Xdrop => {
320 let index = pop_or_unknown(&mut stack);
321 if let Some(depth) = index.int_literal.and_then(|v| usize::try_from(v).ok()) {
322 if depth < stack.len() {
323 let pos = stack.len() - 1 - depth;
324 stack.remove(pos);
325 continue;
326 }
327 }
328 let _ = pop_or_unknown(&mut stack);
329 }
330 OpCode::Reverse3 => reverse_top(&mut stack, 3),
331 OpCode::Reverse4 => reverse_top(&mut stack, 4),
332 OpCode::Reversen => {
333 let count = pop_or_unknown(&mut stack);
334 if let Some(depth) = count.int_literal.and_then(|v| usize::try_from(v).ok()) {
335 reverse_top(&mut stack, depth);
336 }
337 }
338
339 OpCode::Ldloc0 => push_fixed_slot(&mut stack, locals, 0),
341 OpCode::Ldloc1 => push_fixed_slot(&mut stack, locals, 1),
342 OpCode::Ldloc2 => push_fixed_slot(&mut stack, locals, 2),
343 OpCode::Ldloc3 => push_fixed_slot(&mut stack, locals, 3),
344 OpCode::Ldloc4 => push_fixed_slot(&mut stack, locals, 4),
345 OpCode::Ldloc5 => push_fixed_slot(&mut stack, locals, 5),
346 OpCode::Ldloc6 => push_fixed_slot(&mut stack, locals, 6),
347 OpCode::Ldloc => push_indexed_slot(&mut stack, locals, instr.operand.as_ref()),
348 OpCode::Stloc0 => store_slot(&mut stack, locals, 0),
349 OpCode::Stloc1 => store_slot(&mut stack, locals, 1),
350 OpCode::Stloc2 => store_slot(&mut stack, locals, 2),
351 OpCode::Stloc3 => store_slot(&mut stack, locals, 3),
352 OpCode::Stloc4 => store_slot(&mut stack, locals, 4),
353 OpCode::Stloc5 => store_slot(&mut stack, locals, 5),
354 OpCode::Stloc6 => store_slot(&mut stack, locals, 6),
355 OpCode::Stloc => store_indexed_slot(&mut stack, locals, instr.operand.as_ref()),
356
357 OpCode::Ldarg0 => push_fixed_slot(&mut stack, arguments, 0),
358 OpCode::Ldarg1 => push_fixed_slot(&mut stack, arguments, 1),
359 OpCode::Ldarg2 => push_fixed_slot(&mut stack, arguments, 2),
360 OpCode::Ldarg3 => push_fixed_slot(&mut stack, arguments, 3),
361 OpCode::Ldarg4 => push_fixed_slot(&mut stack, arguments, 4),
362 OpCode::Ldarg5 => push_fixed_slot(&mut stack, arguments, 5),
363 OpCode::Ldarg6 => push_fixed_slot(&mut stack, arguments, 6),
364 OpCode::Ldarg => push_indexed_slot(&mut stack, arguments, instr.operand.as_ref()),
365 OpCode::Starg0 => store_slot(&mut stack, arguments, 0),
366 OpCode::Starg1 => store_slot(&mut stack, arguments, 1),
367 OpCode::Starg2 => store_slot(&mut stack, arguments, 2),
368 OpCode::Starg3 => store_slot(&mut stack, arguments, 3),
369 OpCode::Starg4 => store_slot(&mut stack, arguments, 4),
370 OpCode::Starg5 => store_slot(&mut stack, arguments, 5),
371 OpCode::Starg6 => store_slot(&mut stack, arguments, 6),
372 OpCode::Starg => store_indexed_slot(&mut stack, arguments, instr.operand.as_ref()),
373
374 OpCode::Ldsfld0 => push_fixed_slot(&mut stack, statics, 0),
375 OpCode::Ldsfld1 => push_fixed_slot(&mut stack, statics, 1),
376 OpCode::Ldsfld2 => push_fixed_slot(&mut stack, statics, 2),
377 OpCode::Ldsfld3 => push_fixed_slot(&mut stack, statics, 3),
378 OpCode::Ldsfld4 => push_fixed_slot(&mut stack, statics, 4),
379 OpCode::Ldsfld5 => push_fixed_slot(&mut stack, statics, 5),
380 OpCode::Ldsfld6 => push_fixed_slot(&mut stack, statics, 6),
381 OpCode::Ldsfld => push_indexed_slot(&mut stack, statics, instr.operand.as_ref()),
382 OpCode::Stsfld0 => store_slot(&mut stack, statics, 0),
383 OpCode::Stsfld1 => store_slot(&mut stack, statics, 1),
384 OpCode::Stsfld2 => store_slot(&mut stack, statics, 2),
385 OpCode::Stsfld3 => store_slot(&mut stack, statics, 3),
386 OpCode::Stsfld4 => store_slot(&mut stack, statics, 4),
387 OpCode::Stsfld5 => store_slot(&mut stack, statics, 5),
388 OpCode::Stsfld6 => store_slot(&mut stack, statics, 6),
389 OpCode::Stsfld => store_indexed_slot(&mut stack, statics, instr.operand.as_ref()),
390
391 OpCode::Newarray0 => stack.push(StackValue::with_type(ValueType::Array)),
393 OpCode::Newarray | OpCode::NewarrayT => {
394 let _ = pop_or_unknown(&mut stack); stack.push(StackValue::with_type(ValueType::Array));
396 }
397 OpCode::Newmap => stack.push(StackValue::with_type(ValueType::Map)),
398 OpCode::Newstruct0 => stack.push(StackValue::with_type(ValueType::Struct)),
399 OpCode::Newstruct => {
400 let _ = pop_or_unknown(&mut stack); stack.push(StackValue::with_type(ValueType::Struct));
402 }
403 OpCode::Newbuffer => {
404 let _ = pop_or_unknown(&mut stack); stack.push(StackValue::with_type(ValueType::Buffer));
406 }
407 OpCode::Pack => {
408 let count = pop_or_unknown(&mut stack);
409 if let Some(count) = count.int_literal.and_then(|v| usize::try_from(v).ok()) {
410 for _ in 0..count.min(stack.len()) {
415 let _ = pop_or_unknown(&mut stack);
416 }
417 }
418 stack.push(StackValue::with_type(ValueType::Array));
419 }
420 OpCode::Packmap => {
421 let count = pop_or_unknown(&mut stack);
422 if let Some(count) = count.int_literal.and_then(|v| usize::try_from(v).ok()) {
423 for _ in 0..count.saturating_mul(2).min(stack.len()) {
427 let _ = pop_or_unknown(&mut stack);
428 }
429 }
430 stack.push(StackValue::with_type(ValueType::Map));
431 }
432 OpCode::Packstruct => {
433 let count = pop_or_unknown(&mut stack);
434 if let Some(count) = count.int_literal.and_then(|v| usize::try_from(v).ok()) {
435 for _ in 0..count.min(stack.len()) {
437 let _ = pop_or_unknown(&mut stack);
438 }
439 }
440 stack.push(StackValue::with_type(ValueType::Struct));
441 }
442 OpCode::Unpack => {
443 let _ = pop_or_unknown(&mut stack);
444 stack.push(StackValue::unknown());
445 }
446 OpCode::Pickitem => {
447 let _ = pop_or_unknown(&mut stack);
448 let _ = pop_or_unknown(&mut stack);
449 stack.push(StackValue::unknown());
450 }
451 OpCode::Setitem => {
452 let _ = pop_or_unknown(&mut stack);
453 let _ = pop_or_unknown(&mut stack);
454 let _ = pop_or_unknown(&mut stack);
455 }
456 OpCode::Append => {
457 let _ = pop_or_unknown(&mut stack);
458 let _ = pop_or_unknown(&mut stack);
459 }
460 OpCode::Remove => {
461 let _ = pop_or_unknown(&mut stack);
462 let _ = pop_or_unknown(&mut stack);
463 }
464 OpCode::Clearitems => {
465 let _ = pop_or_unknown(&mut stack);
466 }
467 OpCode::Popitem => {
468 let _ = pop_or_unknown(&mut stack);
469 let _ = pop_or_unknown(&mut stack);
470 stack.push(StackValue::unknown());
471 }
472 OpCode::Size => {
473 let _ = pop_or_unknown(&mut stack);
474 stack.push(StackValue::with_type(ValueType::Integer));
475 }
476 OpCode::Haskey => {
477 let _ = pop_or_unknown(&mut stack);
478 let _ = pop_or_unknown(&mut stack);
479 stack.push(StackValue::with_type(ValueType::Boolean));
480 }
481 OpCode::Isnull => {
482 let _ = pop_or_unknown(&mut stack);
483 stack.push(StackValue::with_type(ValueType::Boolean));
484 }
485 OpCode::Istype => {
486 let _ = pop_or_unknown(&mut stack);
487 let _ = pop_or_unknown(&mut stack);
488 stack.push(StackValue::with_type(ValueType::Boolean));
489 }
490 OpCode::Convert => {
491 let _ = pop_or_unknown(&mut stack);
496 let target = instr
497 .operand
498 .as_ref()
499 .and_then(convert_target_type)
500 .unwrap_or(ValueType::Any);
501 stack.push(StackValue::with_type(target));
502 }
503
504 OpCode::Add
506 | OpCode::Sub
507 | OpCode::Mul
508 | OpCode::Div
509 | OpCode::Mod
510 | OpCode::Pow
511 | OpCode::Min
512 | OpCode::Max
513 | OpCode::Shl
514 | OpCode::Shr => {
515 let _ = pop_or_unknown(&mut stack);
516 let _ = pop_or_unknown(&mut stack);
517 stack.push(StackValue::with_type(ValueType::Integer));
518 }
519 OpCode::Modmul | OpCode::Modpow => {
520 let _ = pop_or_unknown(&mut stack);
521 let _ = pop_or_unknown(&mut stack);
522 let _ = pop_or_unknown(&mut stack);
523 stack.push(StackValue::with_type(ValueType::Integer));
524 }
525 OpCode::Within => {
526 let _ = pop_or_unknown(&mut stack);
527 let _ = pop_or_unknown(&mut stack);
528 let _ = pop_or_unknown(&mut stack);
529 stack.push(StackValue::with_type(ValueType::Boolean));
530 }
531 OpCode::Sqrt
532 | OpCode::Abs
533 | OpCode::Sign
534 | OpCode::Inc
535 | OpCode::Dec
536 | OpCode::Negate => {
537 let _ = pop_or_unknown(&mut stack);
538 stack.push(StackValue::with_type(ValueType::Integer));
539 }
540 OpCode::And | OpCode::Or | OpCode::Xor => {
541 let _ = pop_or_unknown(&mut stack);
542 let _ = pop_or_unknown(&mut stack);
543 stack.push(StackValue::with_type(ValueType::Integer));
544 }
545 OpCode::Invert => {
546 let _ = pop_or_unknown(&mut stack);
547 stack.push(StackValue::with_type(ValueType::Integer));
548 }
549 OpCode::Not => {
550 let _ = pop_or_unknown(&mut stack);
551 stack.push(StackValue::with_type(ValueType::Boolean));
552 }
553 OpCode::Booland | OpCode::Boolor => {
554 let _ = pop_or_unknown(&mut stack);
555 let _ = pop_or_unknown(&mut stack);
556 stack.push(StackValue::with_type(ValueType::Boolean));
557 }
558 OpCode::Equal
559 | OpCode::Numequal
560 | OpCode::Notequal
561 | OpCode::Numnotequal
562 | OpCode::Gt
563 | OpCode::Ge
564 | OpCode::Lt
565 | OpCode::Le
566 | OpCode::Nz => {
567 let _ = pop_or_unknown(&mut stack);
569 if !matches!(instr.opcode, OpCode::Nz) {
570 let _ = pop_or_unknown(&mut stack);
571 }
572 stack.push(StackValue::with_type(ValueType::Boolean));
573 }
574
575 _ => {}
577 }
578 }
579}
580
581fn reverse_top(stack: &mut [StackValue], count: usize) {
582 if count == 0 || stack.len() < count {
583 return;
584 }
585 let start = stack.len() - count;
586 stack[start..].reverse();
587}
588
589fn pop_or_unknown(stack: &mut Vec<StackValue>) -> StackValue {
590 stack.pop().unwrap_or_else(StackValue::unknown)
591}
592
593fn push_slot(stack: &mut Vec<StackValue>, ty: Option<ValueType>) {
594 stack.push(StackValue::with_type(ty.unwrap_or(ValueType::Unknown)));
595}
596
597fn push_fixed_slot(stack: &mut Vec<StackValue>, slots: &mut Vec<ValueType>, index: usize) {
598 if index >= slots.len() {
599 slots.resize(index + 1, ValueType::Unknown);
600 }
601 push_slot(stack, slots.get(index).copied());
602}
603
604fn push_indexed_slot(
605 stack: &mut Vec<StackValue>,
606 slots: &mut Vec<ValueType>,
607 operand: Option<&Operand>,
608) {
609 let Some(Operand::U8(index)) = operand else {
610 stack.push(StackValue::unknown());
611 return;
612 };
613 let idx = *index as usize;
614 if idx >= slots.len() {
615 slots.resize(idx + 1, ValueType::Unknown);
616 }
617 push_slot(stack, slots.get(idx).copied());
618}
619
620fn store_slot(stack: &mut Vec<StackValue>, slots: &mut Vec<ValueType>, index: usize) {
621 let value = pop_or_unknown(stack);
622 if index >= slots.len() {
623 slots.resize(index + 1, ValueType::Unknown);
624 }
625 let current = slots[index];
626 slots[index] = current.join(value.ty);
627}
628
629fn store_indexed_slot(
630 stack: &mut Vec<StackValue>,
631 slots: &mut Vec<ValueType>,
632 operand: Option<&Operand>,
633) {
634 let Some(Operand::U8(index)) = operand else {
635 let _ = pop_or_unknown(stack);
636 return;
637 };
638 store_slot(stack, slots, *index as usize);
639}
640
641fn scan_slot_counts(instructions: &[&Instruction]) -> Option<(usize, usize)> {
642 for instr in instructions {
643 if instr.opcode != OpCode::Initslot {
644 continue;
645 }
646 if let Some(Operand::Bytes(bytes)) = &instr.operand {
647 if bytes.len() == 2 {
648 return Some((bytes[0] as usize, bytes[1] as usize));
649 }
650 }
651 }
652 None
653}
654
655fn scan_static_slot_count(instructions: &[Instruction]) -> Option<usize> {
656 for instr in instructions {
657 if instr.opcode != OpCode::Initsslot {
658 continue;
659 }
660 if let Some(Operand::U8(count)) = &instr.operand {
661 return Some(*count as usize);
662 }
663 }
664 None
665}
666
667fn int_literal_from_operand(operand: Option<&Operand>) -> Option<i64> {
668 match operand {
669 Some(Operand::I8(v)) => Some(*v as i64),
670 Some(Operand::I16(v)) => Some(*v as i64),
671 Some(Operand::I32(v)) => Some(*v as i64),
672 Some(Operand::I64(v)) => Some(*v),
673 Some(Operand::U8(v)) => Some(*v as i64),
674 Some(Operand::U16(v)) => Some(*v as i64),
675 Some(Operand::U32(v)) => Some(*v as i64),
676 _ => None,
677 }
678}
679
680fn type_from_manifest(kind: &str) -> ValueType {
681 match kind.to_ascii_lowercase().as_str() {
682 "any" => ValueType::Any,
683 "boolean" => ValueType::Boolean,
684 "integer" => ValueType::Integer,
685 "string" => ValueType::ByteString,
686 "bytearray" => ValueType::ByteString,
687 "signature" => ValueType::ByteString,
688 "hash160" => ValueType::ByteString,
689 "hash256" => ValueType::ByteString,
690 "array" => ValueType::Array,
691 "map" => ValueType::Map,
692 "interopinterface" => ValueType::InteropInterface,
693 _ => ValueType::Unknown,
694 }
695}
696
697fn convert_target_type(operand: &Operand) -> Option<ValueType> {
698 let byte = match operand {
699 Operand::U8(v) => *v,
700 Operand::I8(v) => *v as u8,
701 _ => return None,
702 };
703 match byte {
704 0x00 => Some(ValueType::Any),
705 0x10 => Some(ValueType::Pointer),
706 0x20 => Some(ValueType::Boolean),
707 0x21 => Some(ValueType::Integer),
708 0x28 => Some(ValueType::ByteString),
709 0x30 => Some(ValueType::Buffer),
710 0x40 => Some(ValueType::Array),
711 0x41 => Some(ValueType::Struct),
712 0x48 => Some(ValueType::Map),
713 0x60 => Some(ValueType::InteropInterface),
714 _ => None,
715 }
716}