Skip to content

Commit 9edab42

Browse files
committed
[move tools] add some utility functions to move model
- Add utility functions for finding objects shared, transferred, or frozen by a function/module. These functions let you ask both about direct and transitive shares/transfers/freezes. - Add a similar utility function for showing events emitted by a function/module - Provide a function for asking about the object types returned by a function/module These functions are useful for tools that are trying to do high-level reasoning about Move packages, or codegen of various frontend queries.
1 parent e57533f commit 9edab42

File tree

1 file changed

+238
-4
lines changed

1 file changed

+238
-4
lines changed

move-model/src/model.rs

Lines changed: 238 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,11 @@ use move_binary_format::{
4141
binary_views::BinaryIndexedView,
4242
file_format::{
4343
AddressIdentifierIndex, Bytecode, CodeOffset, Constant as VMConstant, ConstantPoolIndex,
44-
FunctionDefinitionIndex, FunctionHandleIndex, SignatureIndex, SignatureToken,
45-
StructDefinitionIndex, StructFieldInformation, StructHandleIndex, Visibility,
44+
FunctionDefinitionIndex, FunctionHandleIndex, FunctionInstantiation, SignatureIndex,
45+
SignatureToken, StructDefinitionIndex, StructFieldInformation, StructHandleIndex,
46+
Visibility,
4647
},
47-
normalized::Type as MType,
48+
normalized::{FunctionRef, Type as MType},
4849
views::{
4950
FieldDefinitionView, FunctionDefinitionView, FunctionHandleView, SignatureTokenView,
5051
StructDefinitionView, StructHandleView,
@@ -87,6 +88,14 @@ pub const SCRIPT_BYTECODE_FUN_NAME: &str = "<SELF>";
8788
/// A prefix used for structs which are backing specification ("ghost") memory.
8889
pub const GHOST_MEMORY_PREFIX: &str = "Ghost$";
8990

91+
const SUI_FRAMEWORK_ADDRESS: AccountAddress = address_from_single_byte(2);
92+
93+
const fn address_from_single_byte(b: u8) -> AccountAddress {
94+
let mut addr = [0u8; AccountAddress::LENGTH];
95+
addr[AccountAddress::LENGTH - 1] = b;
96+
AccountAddress::new(addr)
97+
}
98+
9099
// =================================================================================================
91100
/// # Locations
92101
@@ -160,6 +169,13 @@ impl Default for Loc {
160169
}
161170
}
162171

172+
/// Return true if `f` is a Sui framework function declared in `module` with a name in `names`
173+
fn is_framework_function(f: &FunctionRef, module: &str, names: Vec<&str>) -> bool {
174+
*f.module_id.address() == SUI_FRAMEWORK_ADDRESS
175+
&& f.module_id.name().to_string() == module
176+
&& names.contains(&f.function_ident.as_str())
177+
}
178+
163179
/// Alias for the Loc variant of MoveIR. This uses a `&static str` instead of `FileId` for the
164180
/// file name.
165181
pub type MoveIrLoc = move_ir_types::location::Loc;
@@ -2193,11 +2209,93 @@ impl<'env> ModuleEnv<'env> {
21932209
self.data.struct_data.len()
21942210
}
21952211

2196-
/// Returns iterator over structs in this module.
2212+
/// Returns an iterator over structs in this module.
21972213
pub fn get_structs(&'env self) -> impl Iterator<Item = StructEnv<'env>> {
21982214
self.clone().into_structs()
21992215
}
22002216

2217+
/// Returns an iterator over all object types declared by this module
2218+
pub fn get_objects(&'env self) -> impl Iterator<Item = StructEnv<'env>> {
2219+
self.clone()
2220+
.into_structs()
2221+
.filter(|s| s.get_abilities().has_key())
2222+
}
2223+
2224+
/// Returns the object types that are shared by code in this module
2225+
/// If `transitive` is false, only return objects directly shared by functions declared in this module
2226+
/// If `transitive` is true, return objects shared by both functions declared in this module and by transitive callees
2227+
/// Note that this can include both types declared inside this module (common case) and types declared outside
2228+
/// Note that objects with `store` can be shared by modules that depend on this one (e.g., by returning the object and subsequently calling `public_share_object`)
2229+
pub fn get_shared_objects(&'env self, transitive: bool) -> BTreeSet<Type> {
2230+
let mut shared = BTreeSet::new();
2231+
for f in self.get_functions() {
2232+
shared.extend(f.get_shared_objects(transitive));
2233+
}
2234+
shared
2235+
}
2236+
2237+
/// Returns the object types that are frozen by this module
2238+
/// If `transitive` is false, only return objects directly transferred by functions declared in this module
2239+
/// If `transitive` is true, return objects transferred by both functions declared in this module and by transitive callees
2240+
/// Note that this function can return both types declared inside this module (common case) and types declared outside
2241+
/// Note that objects with `store` can be transferred by modules that depend on this one (e.g., by returning the object and subsequently calling `public_transfer`),
2242+
/// or transferred by a command in a programmable transaction block
2243+
pub fn get_transferred_objects(&'env self, transitive: bool) -> BTreeSet<Type> {
2244+
let mut transferred = BTreeSet::new();
2245+
for f in self.get_functions() {
2246+
transferred.extend(f.get_transferred_objects(transitive))
2247+
}
2248+
transferred
2249+
}
2250+
2251+
/// Returns the object types that are frozen by this module
2252+
/// If `transitive` is false, only return objects directly frozen by functions declared in this module
2253+
/// If `transitive` is true, return objects frozen by both functions declared in this module and by transitive callees
2254+
/// Note that this function can return both types declared inside this module (common case) and types declared outside
2255+
/// Note that objects with `store` can be frozen by modules that depend on this one (e.g., by returning the object and subsequently calling `public_freeze`)
2256+
pub fn get_frozen_objects(&'env self, transitive: bool) -> BTreeSet<Type> {
2257+
let mut frozen = BTreeSet::new();
2258+
for f in self.get_functions() {
2259+
frozen.extend(f.get_frozen_objects(transitive))
2260+
}
2261+
frozen
2262+
}
2263+
2264+
/// Returns the event types that are emitted by this module
2265+
/// If `transitive` is false, only return events directly emitted by functions declared in this module
2266+
/// If `transitive` is true, return events emitted by both functions declared in this module and by transitive callees
2267+
/// Note that this function can return both event types declared inside this module (common case) and event types declared outside
2268+
pub fn get_events(&'env self, transitive: bool) -> BTreeSet<Type> {
2269+
let mut frozen = BTreeSet::new();
2270+
for f in self.get_functions() {
2271+
frozen.extend(f.get_frozen_objects(transitive))
2272+
}
2273+
frozen
2274+
}
2275+
2276+
/// Returns the objects types that are returned by externally callable (`public`, `entry`, and `friend`) functions in this module
2277+
/// Returned objects with `store` can be transferred, shared, frozen, or wrapped by a different module
2278+
/// Note that this function returns object types both with and without `store`
2279+
pub fn get_externally_returned_objects(&'env self) -> BTreeSet<Type> {
2280+
let mut returned = BTreeSet::new();
2281+
for f in self.get_functions() {
2282+
if !f.is_exposed() {
2283+
continue;
2284+
}
2285+
// Objects returned by a public function can be transferred, shared, frozen, or wrapped
2286+
// by a different module or (in the case of transfer) by a command in a programmable transaction block.
2287+
for f in f.get_return_types() {
2288+
if let Type::Struct(mid, sid, _) = f {
2289+
let struct_env = self.env.get_module(mid).into_struct(sid);
2290+
if struct_env.get_abilities().has_key() {
2291+
returned.insert(f);
2292+
}
2293+
}
2294+
}
2295+
}
2296+
returned
2297+
}
2298+
22012299
/// Returns iterator over structs in this module.
22022300
pub fn into_structs(self) -> impl Iterator<Item = StructEnv<'env>> {
22032301
self.data.struct_data.values().map(move |data| StructEnv {
@@ -3735,6 +3833,142 @@ impl<'env> FunctionEnv<'env> {
37353833
type_param_names: Some(type_param_names),
37363834
}
37373835
}
3836+
3837+
/// Returns the object types that may be shared by this function
3838+
/// If `transitive` is false, only return objects directly shared by this function
3839+
/// If `transitive` is true, return objects shared by both this function and its transitive callees
3840+
pub fn get_shared_objects(&'env self, transitive: bool) -> BTreeSet<Type> {
3841+
let mut shared = BTreeSet::new();
3842+
if transitive {
3843+
let callees = self.get_transitive_closure_of_called_functions();
3844+
for callee in callees {
3845+
let fenv = self.module_env.env.get_function(callee);
3846+
shared.extend(fenv.get_shared_objects(false));
3847+
}
3848+
} else {
3849+
let module = &self.module_env.data.module;
3850+
for b in self.get_bytecode() {
3851+
if let Bytecode::CallGeneric(fi_idx) = b {
3852+
let FunctionInstantiation {
3853+
handle,
3854+
type_parameters,
3855+
} = module.function_instantiation_at(*fi_idx);
3856+
let f_ref = FunctionRef::from_idx(module, handle);
3857+
if is_framework_function(
3858+
&f_ref,
3859+
"transfer",
3860+
vec!["share_object", "public_share_object"],
3861+
) {
3862+
let type_params = module.signature_at(*type_parameters);
3863+
shared.insert(self.module_env.globalize_signature(&type_params.0[0]));
3864+
}
3865+
}
3866+
}
3867+
}
3868+
3869+
shared
3870+
}
3871+
3872+
/// Returns the object types that may be transferred by this function
3873+
/// If `transitive` is false, only objects directly transferred by this function
3874+
/// If `transitive` is true, return objects transferred by both this function and its transitive callees
3875+
pub fn get_transferred_objects(&'env self, transitive: bool) -> BTreeSet<Type> {
3876+
let mut transferred = BTreeSet::new();
3877+
if transitive {
3878+
let callees = self.get_transitive_closure_of_called_functions();
3879+
for callee in callees {
3880+
let fenv = self.module_env.env.get_function(callee);
3881+
transferred.extend(fenv.get_shared_objects(false));
3882+
}
3883+
} else {
3884+
let module = &self.module_env.data.module;
3885+
for b in self.get_bytecode() {
3886+
if let Bytecode::CallGeneric(fi_idx) = b {
3887+
let FunctionInstantiation {
3888+
handle,
3889+
type_parameters,
3890+
} = module.function_instantiation_at(*fi_idx);
3891+
let f_ref = FunctionRef::from_idx(module, handle);
3892+
if is_framework_function(
3893+
&f_ref,
3894+
"transfer",
3895+
vec!["transfer", "public_transfer"],
3896+
) {
3897+
let type_params = module.signature_at(*type_parameters);
3898+
transferred.insert(self.module_env.globalize_signature(&type_params.0[0]));
3899+
}
3900+
}
3901+
}
3902+
}
3903+
3904+
transferred
3905+
}
3906+
3907+
/// Returns the object types that may be frozen by this function
3908+
/// If `transitive` is false, only return objects directly frozen by this function
3909+
/// If `transitive` is true, return objects frozen by both this function and its transitive callees
3910+
pub fn get_frozen_objects(&'env self, transitive: bool) -> BTreeSet<Type> {
3911+
let mut frozen = BTreeSet::new();
3912+
if transitive {
3913+
let callees = self.get_transitive_closure_of_called_functions();
3914+
for callee in callees {
3915+
let fenv = self.module_env.env.get_function(callee);
3916+
frozen.extend(fenv.get_shared_objects(false));
3917+
}
3918+
} else {
3919+
let module = &self.module_env.data.module;
3920+
for b in self.get_bytecode() {
3921+
if let Bytecode::CallGeneric(fi_idx) = b {
3922+
let FunctionInstantiation {
3923+
handle,
3924+
type_parameters,
3925+
} = module.function_instantiation_at(*fi_idx);
3926+
let f_ref = FunctionRef::from_idx(module, handle);
3927+
if is_framework_function(
3928+
&f_ref,
3929+
"transfer",
3930+
vec!["freeze_object", "public_freeze_object"],
3931+
) {
3932+
let type_params = module.signature_at(*type_parameters);
3933+
frozen.insert(self.module_env.globalize_signature(&type_params.0[0]));
3934+
}
3935+
}
3936+
}
3937+
}
3938+
3939+
frozen
3940+
}
3941+
3942+
/// Returns the event types that may be emitted by this function
3943+
/// If `transitive` is false, only return events directly emitted by this function
3944+
/// If `transitive` is true, return events emitted by both this function and its transitive callees
3945+
pub fn get_events(&'env self, transitive: bool) -> BTreeSet<Type> {
3946+
let mut events = BTreeSet::new();
3947+
if transitive {
3948+
let callees = self.get_transitive_closure_of_called_functions();
3949+
for callee in callees {
3950+
let fenv = self.module_env.env.get_function(callee);
3951+
events.extend(fenv.get_events(false));
3952+
}
3953+
} else {
3954+
let module = &self.module_env.data.module;
3955+
for b in self.get_bytecode() {
3956+
if let Bytecode::CallGeneric(fi_idx) = b {
3957+
let FunctionInstantiation {
3958+
handle,
3959+
type_parameters,
3960+
} = module.function_instantiation_at(*fi_idx);
3961+
let f_ref = FunctionRef::from_idx(module, handle);
3962+
if is_framework_function(&f_ref, "event", vec!["emit"]) {
3963+
let type_params = module.signature_at(*type_parameters);
3964+
events.insert(self.module_env.globalize_signature(&type_params.0[0]));
3965+
}
3966+
}
3967+
}
3968+
}
3969+
3970+
events
3971+
}
37383972
}
37393973

37403974
// =================================================================================================

0 commit comments

Comments
 (0)