#![allow(
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_sign_loss
)]
mod call_effects;
mod return_depth;
mod return_effects;
mod simulation;
mod slot_ops;
mod stack_ops;
mod support;
use serde::Serialize;
use crate::instruction::Instruction;
use crate::manifest::ContractManifest;
use crate::nef::MethodToken;
use super::{MethodRef, MethodTable};
use call_effects::build_direct_call_effects_by_offset;
use simulation::infer_types_in_slice;
use support::{scan_slot_counts, scan_static_slot_count, type_from_manifest};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
#[non_exhaustive]
pub enum ValueType {
#[serde(rename = "unknown")]
Unknown,
#[serde(rename = "any")]
Any,
#[serde(rename = "null")]
Null,
#[serde(rename = "bool")]
Boolean,
#[serde(rename = "integer")]
Integer,
#[serde(rename = "bytestring")]
ByteString,
#[serde(rename = "buffer")]
Buffer,
#[serde(rename = "array")]
Array,
#[serde(rename = "struct")]
Struct,
#[serde(rename = "map")]
Map,
#[serde(rename = "interopinterface")]
InteropInterface,
#[serde(rename = "pointer")]
Pointer,
}
impl ValueType {
fn join(self, other: Self) -> Self {
use ValueType::*;
if self == other {
return self;
}
match (self, other) {
(Unknown, x) | (x, Unknown) => x,
(Null, _) | (_, Null) => Any,
_ => Any,
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct MethodTypes {
pub method: MethodRef,
pub arguments: Vec<ValueType>,
pub locals: Vec<ValueType>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct TypeInfo {
pub methods: Vec<MethodTypes>,
pub statics: Vec<ValueType>,
}
#[must_use]
pub fn infer_types(instructions: &[Instruction], manifest: Option<&ContractManifest>) -> TypeInfo {
infer_types_with_method_tokens(instructions, manifest, &[])
}
#[must_use]
pub fn infer_types_with_method_tokens(
instructions: &[Instruction],
manifest: Option<&ContractManifest>,
method_tokens: &[MethodToken],
) -> TypeInfo {
let table = MethodTable::new(instructions, manifest);
let static_count = scan_static_slot_count(instructions).unwrap_or(0);
let mut statics = vec![ValueType::Unknown; static_count];
let direct_call_effects =
build_direct_call_effects_by_offset(&table, instructions, manifest, method_tokens);
let mut methods = Vec::new();
let mut cursor = 0usize;
for span in table.spans() {
while cursor < instructions.len() && instructions[cursor].offset < span.start {
cursor += 1;
}
let begin = cursor;
while cursor < instructions.len() && instructions[cursor].offset < span.end {
cursor += 1;
}
let slice: Vec<&Instruction> = instructions[begin..cursor].iter().collect();
let (locals_count, args_count) = scan_slot_counts(&slice).unwrap_or((0, 0));
let mut locals = vec![ValueType::Unknown; locals_count];
let mut arguments = vec![ValueType::Unknown; args_count];
if let Some(manifest) = manifest {
if let Some(index) = table.manifest_index_for_start(span.start) {
if let Some(method) = manifest.abi.methods.get(index) {
if arguments.len() < method.parameters.len() {
arguments.resize(method.parameters.len(), ValueType::Unknown);
}
for (idx, param) in method.parameters.iter().enumerate() {
arguments[idx] = arguments[idx].join(type_from_manifest(¶m.kind));
}
}
}
}
infer_types_in_slice(
&slice,
&mut locals,
&mut arguments,
&mut statics,
method_tokens,
&direct_call_effects,
);
methods.push(MethodTypes {
method: span.method.clone(),
arguments,
locals,
});
}
TypeInfo { methods, statics }
}