use crate::{
compositional_analysis::{CompositionalAnalysis, SummaryCache},
dataflow_analysis::{DataflowAnalysis, TransferFunctions},
dataflow_domains::{AbstractDomain, JoinResult, SetDomain},
function_target::{FunctionData, FunctionTarget},
function_target_pipeline::{FunctionTargetProcessor, FunctionTargetsHolder, FunctionVariant},
stackless_bytecode::{Bytecode, Operation},
};
use move_binary_format::file_format::CodeOffset;
use move_core_types::language_storage::{StructTag, TypeTag};
use move_model::{
model::{FunctionEnv, GlobalEnv},
ty::Type,
};
use std::collections::BTreeSet;
pub fn get_packed_types(
env: &GlobalEnv,
targets: &FunctionTargetsHolder,
coin_types: Vec<Type>,
) -> BTreeSet<StructTag> {
let mut packed_types = BTreeSet::new();
for module_env in env.get_modules() {
let module_name = module_env.get_identifier().to_string();
let is_script = module_env.is_script_module();
if is_script || module_name == "Genesis" {
for func_env in module_env.get_functions() {
let fun_target = targets.get_target(&func_env, &FunctionVariant::Baseline);
let annotation = fun_target
.get_annotations()
.get::<PackedTypesState>()
.expect(
"Invariant violation: usage analysis should be run before calling this",
);
packed_types.extend(annotation.closed_types.clone());
if is_script {
let num_type_parameters = func_env.get_type_parameters().len();
assert!(num_type_parameters <= 1, "Assuming that transaction scripts have <= 1 type parameters for simplicity. If there can be >1 type parameter, the code here must account for all permutations of type params");
if num_type_parameters == 1 {
for open_ty in annotation.open_types.iter() {
for coin_ty in &coin_types {
match open_ty.instantiate(vec![coin_ty.clone()].as_slice()).into_type_tag(env) {
Some(TypeTag::Struct(s)) => {
packed_types.insert(s);
}
_ => panic!("Invariant violation: failed to specialize tx script open type {:?} into struct", open_ty),
}
}
}
}
}
}
}
}
packed_types
}
#[derive(Debug, Clone, Default, Eq, PartialOrd, PartialEq)]
struct PackedTypesState {
closed_types: SetDomain<StructTag>,
open_types: SetDomain<Type>,
}
impl AbstractDomain for PackedTypesState {
fn join(&mut self, other: &Self) -> JoinResult {
match (
self.closed_types.join(&other.closed_types),
self.open_types.join(&other.open_types),
) {
(JoinResult::Unchanged, JoinResult::Unchanged) => JoinResult::Unchanged,
_ => JoinResult::Changed,
}
}
}
struct PackedTypesAnalysis<'a> {
cache: SummaryCache<'a>,
}
impl<'a> TransferFunctions for PackedTypesAnalysis<'a> {
type State = PackedTypesState;
const BACKWARD: bool = false;
fn execute(&self, state: &mut Self::State, instr: &Bytecode, _offset: CodeOffset) {
use Bytecode::*;
use Operation::*;
if let Call(_, _, oper, ..) = instr {
match oper {
Pack(mid, sid, types) => {
let env = self.cache.global_env();
match env.get_struct_tag(*mid, *sid, types) {
Some(tag) => {
state.closed_types.insert(tag);
}
None => {
state
.open_types
.insert(Type::Struct(*mid, *sid, types.clone()));
}
}
}
Function(mid, fid, types) => {
if let Some(summary) = self
.cache
.get::<PackedTypesState>(mid.qualified(*fid), &FunctionVariant::Baseline)
{
for ty in summary.closed_types.iter() {
state.closed_types.insert(ty.clone());
}
for open_ty in summary.open_types.iter() {
let specialized_ty = open_ty.instantiate(types);
if specialized_ty.is_open() {
state.open_types.insert(specialized_ty);
} else if let Some(TypeTag::Struct(s)) =
specialized_ty.into_type_tag(self.cache.global_env())
{
state.closed_types.insert(s);
} else {
panic!("Invariant violation: struct type {:?} became non-struct type after substitution", open_ty)
}
}
}
}
OpaqueCallBegin(_, _, _) | OpaqueCallEnd(_, _, _) => {
}
_ => (),
}
}
}
}
impl<'a> DataflowAnalysis for PackedTypesAnalysis<'a> {}
impl<'a> CompositionalAnalysis<PackedTypesState> for PackedTypesAnalysis<'a> {
fn to_summary(
&self,
state: PackedTypesState,
_fun_target: &FunctionTarget,
) -> PackedTypesState {
state
}
}
pub struct PackedTypesProcessor();
impl PackedTypesProcessor {
pub fn new() -> Box<Self> {
Box::new(PackedTypesProcessor())
}
}
impl FunctionTargetProcessor for PackedTypesProcessor {
fn process(
&self,
targets: &mut FunctionTargetsHolder,
func_env: &FunctionEnv<'_>,
mut data: FunctionData,
) -> FunctionData {
let initial_state = PackedTypesState::default();
let fun_target = FunctionTarget::new(func_env, &data);
let cache = SummaryCache::new(targets, func_env.module_env.env);
let analysis = PackedTypesAnalysis { cache };
let summary = analysis.summarize(&fun_target, initial_state);
data.annotations.set(summary);
data
}
fn name(&self) -> String {
"packed_types_analysis".to_string()
}
}