neo_decompiler/decompiler/analysis/
types.rs1#![allow(
14 clippy::cast_possible_truncation,
15 clippy::cast_possible_wrap,
16 clippy::cast_sign_loss
17)]
18
19mod call_effects;
20mod return_depth;
21mod return_effects;
22mod simulation;
23mod slot_ops;
24mod stack_ops;
25mod support;
26
27use serde::Serialize;
28
29use crate::instruction::Instruction;
30use crate::manifest::ContractManifest;
31use crate::nef::MethodToken;
32
33use super::{MethodRef, MethodTable};
34use call_effects::build_direct_call_effects_by_offset;
35use simulation::infer_types_in_slice;
36use support::{scan_slot_counts, scan_static_slot_count, type_from_manifest};
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
40#[non_exhaustive]
41pub enum ValueType {
42 #[serde(rename = "unknown")]
44 Unknown,
45 #[serde(rename = "any")]
47 Any,
48 #[serde(rename = "null")]
50 Null,
51 #[serde(rename = "bool")]
53 Boolean,
54 #[serde(rename = "integer")]
56 Integer,
57 #[serde(rename = "bytestring")]
59 ByteString,
60 #[serde(rename = "buffer")]
62 Buffer,
63 #[serde(rename = "array")]
65 Array,
66 #[serde(rename = "struct")]
68 Struct,
69 #[serde(rename = "map")]
71 Map,
72 #[serde(rename = "interopinterface")]
74 InteropInterface,
75 #[serde(rename = "pointer")]
77 Pointer,
78}
79
80impl ValueType {
81 fn join(self, other: Self) -> Self {
82 use ValueType::*;
83 if self == other {
84 return self;
85 }
86 match (self, other) {
87 (Unknown, x) | (x, Unknown) => x,
88 (Null, _) | (_, Null) => Any,
89 _ => Any,
90 }
91 }
92}
93
94#[derive(Debug, Clone, Serialize)]
96pub struct MethodTypes {
97 pub method: MethodRef,
99 pub arguments: Vec<ValueType>,
101 pub locals: Vec<ValueType>,
103}
104
105#[derive(Debug, Clone, Default, Serialize)]
107pub struct TypeInfo {
108 pub methods: Vec<MethodTypes>,
110 pub statics: Vec<ValueType>,
112}
113
114#[must_use]
116pub fn infer_types(instructions: &[Instruction], manifest: Option<&ContractManifest>) -> TypeInfo {
117 infer_types_with_method_tokens(instructions, manifest, &[])
118}
119
120#[must_use]
122pub fn infer_types_with_method_tokens(
123 instructions: &[Instruction],
124 manifest: Option<&ContractManifest>,
125 method_tokens: &[MethodToken],
126) -> TypeInfo {
127 let table = MethodTable::new(instructions, manifest);
128 let static_count = scan_static_slot_count(instructions).unwrap_or(0);
129 let mut statics = vec![ValueType::Unknown; static_count];
130 let direct_call_effects =
131 build_direct_call_effects_by_offset(&table, instructions, manifest, method_tokens);
132
133 let mut methods = Vec::new();
134 let mut cursor = 0usize;
139 for span in table.spans() {
140 while cursor < instructions.len() && instructions[cursor].offset < span.start {
141 cursor += 1;
142 }
143 let begin = cursor;
144 while cursor < instructions.len() && instructions[cursor].offset < span.end {
145 cursor += 1;
146 }
147 let slice: Vec<&Instruction> = instructions[begin..cursor].iter().collect();
148
149 let (locals_count, args_count) = scan_slot_counts(&slice).unwrap_or((0, 0));
150 let mut locals = vec![ValueType::Unknown; locals_count];
151 let mut arguments = vec![ValueType::Unknown; args_count];
152
153 if let Some(manifest) = manifest {
154 if let Some(index) = table.manifest_index_for_start(span.start) {
155 if let Some(method) = manifest.abi.methods.get(index) {
156 if arguments.len() < method.parameters.len() {
157 arguments.resize(method.parameters.len(), ValueType::Unknown);
158 }
159 for (idx, param) in method.parameters.iter().enumerate() {
160 arguments[idx] = arguments[idx].join(type_from_manifest(¶m.kind));
161 }
162 }
163 }
164 }
165
166 infer_types_in_slice(
167 &slice,
168 &mut locals,
169 &mut arguments,
170 &mut statics,
171 method_tokens,
172 &direct_call_effects,
173 );
174
175 methods.push(MethodTypes {
176 method: span.method.clone(),
177 arguments,
178 locals,
179 });
180 }
181
182 TypeInfo { methods, statics }
183}