use crate::{
access::ModuleAccess,
errors::{PartialVMError, PartialVMResult},
file_format_common,
internals::ModuleIndex,
IndexKind, SignatureTokenKind,
};
use move_core_types::{
account_address::AccountAddress,
identifier::{IdentStr, Identifier},
language_storage::ModuleId,
metadata::Metadata,
vm_status::StatusCode,
};
#[cfg(any(test, feature = "fuzzing"))]
use proptest::{collection::vec, prelude::*, strategy::BoxedStrategy};
#[cfg(any(test, feature = "fuzzing"))]
use proptest_derive::Arbitrary;
use ref_cast::RefCast;
use serde::{Deserialize, Serialize};
use std::ops::BitOr;
use variant_count::VariantCount;
pub type TableIndex = u16;
macro_rules! define_index {
{
name: $name: ident,
kind: $kind: ident,
doc: $comment: literal,
} => {
#[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))]
#[doc=$comment]
pub struct $name(pub TableIndex);
impl $name {
pub fn new(idx: TableIndex) -> Self {
Self(idx)
}
}
impl ::std::fmt::Display for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl ::std::fmt::Debug for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{}({})", stringify!($name), self.0)
}
}
impl ModuleIndex for $name {
const KIND: IndexKind = IndexKind::$kind;
#[inline]
fn into_index(self) -> usize {
self.0 as usize
}
}
};
}
define_index! {
name: ModuleHandleIndex,
kind: ModuleHandle,
doc: "Index into the `ModuleHandle` table.",
}
define_index! {
name: StructHandleIndex,
kind: StructHandle,
doc: "Index into the `StructHandle` table.",
}
define_index! {
name: FunctionHandleIndex,
kind: FunctionHandle,
doc: "Index into the `FunctionHandle` table.",
}
define_index! {
name: FieldHandleIndex,
kind: FieldHandle,
doc: "Index into the `FieldHandle` table.",
}
define_index! {
name: StructDefInstantiationIndex,
kind: StructDefInstantiation,
doc: "Index into the `StructInstantiation` table.",
}
define_index! {
name: FunctionInstantiationIndex,
kind: FunctionInstantiation,
doc: "Index into the `FunctionInstantiation` table.",
}
define_index! {
name: FieldInstantiationIndex,
kind: FieldInstantiation,
doc: "Index into the `FieldInstantiation` table.",
}
define_index! {
name: IdentifierIndex,
kind: Identifier,
doc: "Index into the `Identifier` table.",
}
define_index! {
name: AddressIdentifierIndex,
kind: AddressIdentifier,
doc: "Index into the `AddressIdentifier` table.",
}
define_index! {
name: ConstantPoolIndex,
kind: ConstantPool,
doc: "Index into the `ConstantPool` table.",
}
define_index! {
name: SignatureIndex,
kind: Signature,
doc: "Index into the `Signature` table.",
}
define_index! {
name: StructDefinitionIndex,
kind: StructDefinition,
doc: "Index into the `StructDefinition` table.",
}
define_index! {
name: FunctionDefinitionIndex,
kind: FunctionDefinition,
doc: "Index into the `FunctionDefinition` table.",
}
pub type LocalIndex = u8;
pub type MemberCount = u16;
pub type CodeOffset = u16;
pub type IdentifierPool = Vec<Identifier>;
pub type AddressIdentifierPool = Vec<AccountAddress>;
pub type ConstantPool = Vec<Constant>;
pub type TypeSignaturePool = Vec<TypeSignature>;
pub type SignaturePool = Vec<Signature>;
pub fn self_module_name() -> &'static IdentStr {
IdentStr::ref_cast("<SELF>")
}
pub const NO_TYPE_ARGUMENTS: SignatureIndex = SignatureIndex(0);
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))]
pub struct ModuleHandle {
pub address: AddressIdentifierIndex,
pub name: IdentifierIndex,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))]
pub struct StructHandle {
pub module: ModuleHandleIndex,
pub name: IdentifierIndex,
pub abilities: AbilitySet,
pub type_parameters: Vec<StructTypeParameter>,
}
impl StructHandle {
pub fn type_param_constraints(&self) -> impl ExactSizeIterator<Item = AbilitySet> + '_ {
self.type_parameters.iter().map(|param| param.constraints)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))]
pub struct StructTypeParameter {
pub constraints: AbilitySet,
pub is_phantom: bool,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(params = "usize"))]
pub struct FunctionHandle {
pub module: ModuleHandleIndex,
pub name: IdentifierIndex,
pub parameters: SignatureIndex,
pub return_: SignatureIndex,
pub type_parameters: Vec<AbilitySet>,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))]
pub struct FieldHandle {
pub owner: StructDefinitionIndex,
pub field: MemberCount,
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))]
pub enum StructFieldInformation {
Native,
Declared(Vec<FieldDefinition>),
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))]
pub struct StructDefInstantiation {
pub def: StructDefinitionIndex,
pub type_parameters: SignatureIndex,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))]
pub struct FunctionInstantiation {
pub handle: FunctionHandleIndex,
pub type_parameters: SignatureIndex,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))]
pub struct FieldInstantiation {
pub handle: FieldHandleIndex,
pub type_parameters: SignatureIndex,
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))]
pub struct StructDefinition {
pub struct_handle: StructHandleIndex,
pub field_information: StructFieldInformation,
}
impl StructDefinition {
pub fn declared_field_count(&self) -> PartialVMResult<MemberCount> {
match &self.field_information {
StructFieldInformation::Native => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
.with_message("Looking for field in native structure".to_string())),
StructFieldInformation::Declared(fields) => Ok(fields.len() as u16),
}
}
pub fn field(&self, offset: usize) -> Option<&FieldDefinition> {
match &self.field_information {
StructFieldInformation::Native => None,
StructFieldInformation::Declared(fields) => fields.get(offset),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))]
pub struct FieldDefinition {
pub name: IdentifierIndex,
pub signature: TypeSignature,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))]
#[repr(u8)]
pub enum Visibility {
Private = 0x0,
Public = 0x1,
Friend = 0x3,
}
impl Visibility {
pub const DEPRECATED_SCRIPT: u8 = 0x2;
}
impl Default for Visibility {
fn default() -> Self {
Visibility::Private
}
}
impl std::convert::TryFrom<u8> for Visibility {
type Error = ();
fn try_from(v: u8) -> Result<Self, Self::Error> {
match v {
x if x == Visibility::Private as u8 => Ok(Visibility::Private),
x if x == Visibility::Public as u8 => Ok(Visibility::Public),
x if x == Visibility::Friend as u8 => Ok(Visibility::Friend),
_ => Err(()),
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(params = "usize"))]
pub struct FunctionDefinition {
pub function: FunctionHandleIndex,
pub visibility: Visibility,
pub is_entry: bool,
pub acquires_global_resources: Vec<StructDefinitionIndex>,
#[cfg_attr(
any(test, feature = "fuzzing"),
proptest(strategy = "any_with::<CodeUnit>(params).prop_map(Some)")
)]
pub code: Option<CodeUnit>,
}
impl FunctionDefinition {
pub fn is_native(&self) -> bool {
self.code.is_none()
}
pub const DEPRECATED_PUBLIC_BIT: u8 = 0b01;
pub const NATIVE: u8 = 0b10;
pub const ENTRY: u8 = 0b100;
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))]
pub struct TypeSignature(pub SignatureToken);
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(params = "usize"))]
pub struct FunctionSignature {
#[cfg_attr(
any(test, feature = "fuzzing"),
proptest(strategy = "vec(any::<SignatureToken>(), 0..=params)")
)]
pub return_: Vec<SignatureToken>,
#[cfg_attr(
any(test, feature = "fuzzing"),
proptest(strategy = "vec(any::<SignatureToken>(), 0..=params)")
)]
pub parameters: Vec<SignatureToken>,
pub type_parameters: Vec<AbilitySet>,
}
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(params = "usize"))]
pub struct Signature(
#[cfg_attr(
any(test, feature = "fuzzing"),
proptest(strategy = "vec(any::<SignatureToken>(), 0..=params)")
)]
pub Vec<SignatureToken>,
);
impl Signature {
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
pub type TypeParameterIndex = u16;
#[repr(u8)]
#[derive(Debug, Clone, Eq, Copy, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
pub enum Ability {
Copy = 0x1,
Drop = 0x2,
Store = 0x4,
Key = 0x8,
}
impl Ability {
fn from_u8(u: u8) -> Option<Self> {
match u {
0x1 => Some(Ability::Copy),
0x2 => Some(Ability::Drop),
0x4 => Some(Ability::Store),
0x8 => Some(Ability::Key),
_ => None,
}
}
pub fn requires(self) -> Self {
match self {
Self::Copy => Ability::Copy,
Self::Drop => Ability::Drop,
Self::Store => Ability::Store,
Self::Key => Ability::Store,
}
}
pub fn required_by(self) -> AbilitySet {
match self {
Self::Copy => AbilitySet::EMPTY | Ability::Copy,
Self::Drop => AbilitySet::EMPTY | Ability::Drop,
Self::Store => AbilitySet::EMPTY | Ability::Store | Ability::Key,
Self::Key => AbilitySet::EMPTY,
}
}
}
#[derive(Clone, Eq, Copy, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct AbilitySet(u8);
impl AbilitySet {
pub const EMPTY: Self = Self(0);
pub const PRIMITIVES: AbilitySet =
Self((Ability::Copy as u8) | (Ability::Drop as u8) | (Ability::Store as u8));
pub const REFERENCES: AbilitySet = Self((Ability::Copy as u8) | (Ability::Drop as u8));
pub const SIGNER: AbilitySet = Self(Ability::Drop as u8);
pub const VECTOR: AbilitySet =
Self((Ability::Copy as u8) | (Ability::Drop as u8) | (Ability::Store as u8));
pub const ALL: Self = Self(
(Ability::Copy as u8)
| (Ability::Drop as u8)
| (Ability::Store as u8)
| (Ability::Key as u8),
);
pub fn has_ability(self, ability: Ability) -> bool {
let a = ability as u8;
(a & self.0) == a
}
pub fn has_copy(self) -> bool {
self.has_ability(Ability::Copy)
}
pub fn has_drop(self) -> bool {
self.has_ability(Ability::Drop)
}
pub fn has_store(self) -> bool {
self.has_ability(Ability::Store)
}
pub fn has_key(self) -> bool {
self.has_ability(Ability::Key)
}
pub fn remove(self, ability: Ability) -> Self {
Self(self.0 & (!(ability as u8)))
}
pub fn intersect(self, other: Self) -> Self {
Self(self.0 & other.0)
}
pub fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
#[inline]
fn is_subset_bits(sub: u8, sup: u8) -> bool {
(sub & sup) == sub
}
pub fn is_subset(self, other: Self) -> bool {
Self::is_subset_bits(self.0, other.0)
}
pub fn polymorphic_abilities<I1, I2>(
declared_abilities: Self,
declared_phantom_parameters: I1,
type_arguments: I2,
) -> PartialVMResult<Self>
where
I1: IntoIterator<Item = bool>,
I2: IntoIterator<Item = Self>,
I1::IntoIter: ExactSizeIterator,
I2::IntoIter: ExactSizeIterator,
{
let declared_phantom_parameters = declared_phantom_parameters.into_iter();
let type_arguments = type_arguments.into_iter();
if declared_phantom_parameters.len() != type_arguments.len() {
return Err(
PartialVMError::new(StatusCode::VERIFIER_INVARIANT_VIOLATION).with_message(
"the length of `declared_phantom_parameters` doesn't match the length of `type_arguments`".to_string(),
),
);
}
let abs = type_arguments
.zip(declared_phantom_parameters)
.filter(|(_, is_phantom)| !is_phantom)
.map(|(ty_arg_abilities, _)| {
ty_arg_abilities
.into_iter()
.map(|a| a.required_by())
.fold(AbilitySet::EMPTY, AbilitySet::union)
})
.fold(declared_abilities, |acc, ty_arg_abilities| {
acc.intersect(ty_arg_abilities)
});
Ok(abs)
}
pub fn from_u8(byte: u8) -> Option<Self> {
if Self::is_subset_bits(byte, Self::ALL.0) {
Some(Self(byte))
} else {
None
}
}
pub fn into_u8(self) -> u8 {
self.0
}
}
impl BitOr<Ability> for AbilitySet {
type Output = Self;
fn bitor(self, rhs: Ability) -> Self {
AbilitySet(self.0 | (rhs as u8))
}
}
impl BitOr<AbilitySet> for AbilitySet {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
AbilitySet(self.0 | rhs.0)
}
}
pub struct AbilitySetIterator {
set: AbilitySet,
idx: u8,
}
impl Iterator for AbilitySetIterator {
type Item = Ability;
fn next(&mut self) -> Option<Self::Item> {
while self.idx <= 0x8 {
let next = Ability::from_u8(self.set.0 & self.idx);
self.idx <<= 1;
if next.is_some() {
return next;
}
}
None
}
}
impl IntoIterator for AbilitySet {
type Item = Ability;
type IntoIter = AbilitySetIterator;
fn into_iter(self) -> Self::IntoIter {
AbilitySetIterator {
idx: 0x1,
set: self,
}
}
}
impl std::fmt::Debug for AbilitySet {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "[")?;
for ability in *self {
write!(f, "{:?}, ", ability)?;
}
write!(f, "]")
}
}
#[cfg(any(test, feature = "fuzzing"))]
impl Arbitrary for AbilitySet {
type Strategy = BoxedStrategy<Self>;
type Parameters = ();
fn arbitrary_with(_params: Self::Parameters) -> Self::Strategy {
proptest::bits::u8::masked(AbilitySet::ALL.0)
.prop_map(|u| AbilitySet::from_u8(u).expect("proptest mask failed for AbilitySet"))
.boxed()
}
}
#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum SignatureToken {
Bool,
U8,
U64,
U128,
Address,
Signer,
Vector(Box<SignatureToken>),
Struct(StructHandleIndex),
StructInstantiation(StructHandleIndex, Vec<SignatureToken>),
Reference(Box<SignatureToken>),
MutableReference(Box<SignatureToken>),
TypeParameter(TypeParameterIndex),
}
pub struct SignatureTokenPreorderTraversalIter<'a> {
stack: Vec<&'a SignatureToken>,
}
impl<'a> Iterator for SignatureTokenPreorderTraversalIter<'a> {
type Item = &'a SignatureToken;
fn next(&mut self) -> Option<Self::Item> {
use SignatureToken::*;
match self.stack.pop() {
Some(tok) => {
match tok {
Reference(inner_tok) | MutableReference(inner_tok) | Vector(inner_tok) => {
self.stack.push(inner_tok)
}
StructInstantiation(_, inner_toks) => {
self.stack.extend(inner_toks.iter().rev())
}
Signer | Bool | Address | U8 | U64 | U128 | Struct(_) | TypeParameter(_) => (),
}
Some(tok)
}
None => None,
}
}
}
pub struct SignatureTokenPreorderTraversalIterWithDepth<'a> {
stack: Vec<(&'a SignatureToken, usize)>,
}
impl<'a> Iterator for SignatureTokenPreorderTraversalIterWithDepth<'a> {
type Item = (&'a SignatureToken, usize);
fn next(&mut self) -> Option<Self::Item> {
use SignatureToken::*;
match self.stack.pop() {
Some((tok, depth)) => {
match tok {
Reference(inner_tok) | MutableReference(inner_tok) | Vector(inner_tok) => {
self.stack.push((inner_tok, depth + 1))
}
StructInstantiation(_, inner_toks) => self
.stack
.extend(inner_toks.iter().map(|tok| (tok, depth + 1)).rev()),
Signer | Bool | Address | U8 | U64 | U128 | Struct(_) | TypeParameter(_) => (),
}
Some((tok, depth))
}
None => None,
}
}
}
#[cfg(any(test, feature = "fuzzing"))]
impl Arbitrary for SignatureToken {
type Strategy = BoxedStrategy<Self>;
type Parameters = ();
fn arbitrary_with(_params: Self::Parameters) -> Self::Strategy {
use SignatureToken::*;
let leaf = prop_oneof![
Just(Bool),
Just(U8),
Just(U64),
Just(U128),
Just(Address),
any::<StructHandleIndex>().prop_map(Struct),
any::<TypeParameterIndex>().prop_map(TypeParameter),
];
leaf.prop_recursive(
8, 16, 1, |inner| {
prop_oneof![
inner.clone().prop_map(|token| Vector(Box::new(token))),
inner.clone().prop_map(|token| Reference(Box::new(token))),
inner.prop_map(|token| MutableReference(Box::new(token))),
]
},
)
.boxed()
}
}
impl std::fmt::Debug for SignatureToken {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match self {
SignatureToken::Bool => write!(f, "Bool"),
SignatureToken::U8 => write!(f, "U8"),
SignatureToken::U64 => write!(f, "U64"),
SignatureToken::U128 => write!(f, "U128"),
SignatureToken::Address => write!(f, "Address"),
SignatureToken::Signer => write!(f, "Signer"),
SignatureToken::Vector(boxed) => write!(f, "Vector({:?})", boxed),
SignatureToken::Struct(idx) => write!(f, "Struct({:?})", idx),
SignatureToken::StructInstantiation(idx, types) => {
write!(f, "StructInstantiation({:?}, {:?})", idx, types)
}
SignatureToken::Reference(boxed) => write!(f, "Reference({:?})", boxed),
SignatureToken::MutableReference(boxed) => write!(f, "MutableReference({:?})", boxed),
SignatureToken::TypeParameter(idx) => write!(f, "TypeParameter({:?})", idx),
}
}
}
impl SignatureToken {
#[inline]
pub fn signature_token_kind(&self) -> SignatureTokenKind {
use SignatureToken::*;
match self {
Reference(_) => SignatureTokenKind::Reference,
MutableReference(_) => SignatureTokenKind::MutableReference,
Bool
| U8
| U64
| U128
| Address
| Signer
| Struct(_)
| StructInstantiation(_, _)
| Vector(_) => SignatureTokenKind::Value,
TypeParameter(_) => SignatureTokenKind::Value,
}
}
pub fn is_integer(&self) -> bool {
use SignatureToken::*;
match self {
U8 | U64 | U128 => true,
Bool
| Address
| Signer
| Vector(_)
| Struct(_)
| StructInstantiation(_, _)
| Reference(_)
| MutableReference(_)
| TypeParameter(_) => false,
}
}
pub fn is_reference(&self) -> bool {
use SignatureToken::*;
matches!(self, Reference(_) | MutableReference(_))
}
pub fn is_mutable_reference(&self) -> bool {
use SignatureToken::*;
matches!(self, MutableReference(_))
}
pub fn is_signer(&self) -> bool {
use SignatureToken::*;
matches!(self, Signer)
}
pub fn is_valid_for_constant(&self) -> bool {
use SignatureToken::*;
match self {
Bool | U8 | U64 | U128 | Address => true,
Vector(inner) => inner.is_valid_for_constant(),
Signer
| Struct(_)
| StructInstantiation(_, _)
| Reference(_)
| MutableReference(_)
| TypeParameter(_) => false,
}
}
pub fn debug_set_sh_idx(&mut self, sh_idx: StructHandleIndex) {
match self {
SignatureToken::Struct(ref mut wrapped) => *wrapped = sh_idx,
SignatureToken::StructInstantiation(ref mut wrapped, _) => *wrapped = sh_idx,
SignatureToken::Reference(ref mut token)
| SignatureToken::MutableReference(ref mut token) => token.debug_set_sh_idx(sh_idx),
other => panic!(
"debug_set_sh_idx (to {}) called for non-struct token {:?}",
sh_idx, other
),
}
}
pub fn preorder_traversal(&self) -> SignatureTokenPreorderTraversalIter<'_> {
SignatureTokenPreorderTraversalIter { stack: vec![self] }
}
pub fn preorder_traversal_with_depth(
&self,
) -> SignatureTokenPreorderTraversalIterWithDepth<'_> {
SignatureTokenPreorderTraversalIterWithDepth {
stack: vec![(self, 1)],
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Constant {
pub type_: SignatureToken,
pub data: Vec<u8>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(params = "usize"))]
pub struct CodeUnit {
pub locals: SignatureIndex,
#[cfg_attr(
any(test, feature = "fuzzing"),
proptest(strategy = "vec(any::<Bytecode>(), 0..=params)")
)]
pub code: Vec<Bytecode>,
}
#[derive(Clone, Hash, Eq, VariantCount, PartialEq)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
#[cfg_attr(any(test, feature = "fuzzing"), proptest(no_params))]
pub enum Bytecode {
Pop,
Ret,
BrTrue(CodeOffset),
BrFalse(CodeOffset),
Branch(CodeOffset),
LdU8(u8),
LdU64(u64),
LdU128(u128),
CastU8,
CastU64,
CastU128,
LdConst(ConstantPoolIndex),
LdTrue,
LdFalse,
CopyLoc(LocalIndex),
MoveLoc(LocalIndex),
StLoc(LocalIndex),
Call(FunctionHandleIndex),
CallGeneric(FunctionInstantiationIndex),
Pack(StructDefinitionIndex),
PackGeneric(StructDefInstantiationIndex),
Unpack(StructDefinitionIndex),
UnpackGeneric(StructDefInstantiationIndex),
ReadRef,
WriteRef,
FreezeRef,
MutBorrowLoc(LocalIndex),
ImmBorrowLoc(LocalIndex),
MutBorrowField(FieldHandleIndex),
MutBorrowFieldGeneric(FieldInstantiationIndex),
ImmBorrowField(FieldHandleIndex),
ImmBorrowFieldGeneric(FieldInstantiationIndex),
MutBorrowGlobal(StructDefinitionIndex),
MutBorrowGlobalGeneric(StructDefInstantiationIndex),
ImmBorrowGlobal(StructDefinitionIndex),
ImmBorrowGlobalGeneric(StructDefInstantiationIndex),
Add,
Sub,
Mul,
Mod,
Div,
BitOr,
BitAnd,
Xor,
Or,
And,
Not,
Eq,
Neq,
Lt,
Gt,
Le,
Ge,
Abort,
Nop,
Exists(StructDefinitionIndex),
ExistsGeneric(StructDefInstantiationIndex),
MoveFrom(StructDefinitionIndex),
MoveFromGeneric(StructDefInstantiationIndex),
MoveTo(StructDefinitionIndex),
MoveToGeneric(StructDefInstantiationIndex),
Shl,
Shr,
VecPack(SignatureIndex, u64),
VecLen(SignatureIndex),
VecImmBorrow(SignatureIndex),
VecMutBorrow(SignatureIndex),
VecPushBack(SignatureIndex),
VecPopBack(SignatureIndex),
VecUnpack(SignatureIndex, u64),
VecSwap(SignatureIndex),
}
impl ::std::fmt::Debug for Bytecode {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match self {
Bytecode::Pop => write!(f, "Pop"),
Bytecode::Ret => write!(f, "Ret"),
Bytecode::BrTrue(a) => write!(f, "BrTrue({})", a),
Bytecode::BrFalse(a) => write!(f, "BrFalse({})", a),
Bytecode::Branch(a) => write!(f, "Branch({})", a),
Bytecode::LdU8(a) => write!(f, "LdU8({})", a),
Bytecode::LdU64(a) => write!(f, "LdU64({})", a),
Bytecode::LdU128(a) => write!(f, "LdU128({})", a),
Bytecode::CastU8 => write!(f, "CastU8"),
Bytecode::CastU64 => write!(f, "CastU64"),
Bytecode::CastU128 => write!(f, "CastU128"),
Bytecode::LdConst(a) => write!(f, "LdConst({})", a),
Bytecode::LdTrue => write!(f, "LdTrue"),
Bytecode::LdFalse => write!(f, "LdFalse"),
Bytecode::CopyLoc(a) => write!(f, "CopyLoc({})", a),
Bytecode::MoveLoc(a) => write!(f, "MoveLoc({})", a),
Bytecode::StLoc(a) => write!(f, "StLoc({})", a),
Bytecode::Call(a) => write!(f, "Call({})", a),
Bytecode::CallGeneric(a) => write!(f, "CallGeneric({})", a),
Bytecode::Pack(a) => write!(f, "Pack({})", a),
Bytecode::PackGeneric(a) => write!(f, "PackGeneric({})", a),
Bytecode::Unpack(a) => write!(f, "Unpack({})", a),
Bytecode::UnpackGeneric(a) => write!(f, "UnpackGeneric({})", a),
Bytecode::ReadRef => write!(f, "ReadRef"),
Bytecode::WriteRef => write!(f, "WriteRef"),
Bytecode::FreezeRef => write!(f, "FreezeRef"),
Bytecode::MutBorrowLoc(a) => write!(f, "MutBorrowLoc({})", a),
Bytecode::ImmBorrowLoc(a) => write!(f, "ImmBorrowLoc({})", a),
Bytecode::MutBorrowField(a) => write!(f, "MutBorrowField({:?})", a),
Bytecode::MutBorrowFieldGeneric(a) => write!(f, "MutBorrowFieldGeneric({:?})", a),
Bytecode::ImmBorrowField(a) => write!(f, "ImmBorrowField({:?})", a),
Bytecode::ImmBorrowFieldGeneric(a) => write!(f, "ImmBorrowFieldGeneric({:?})", a),
Bytecode::MutBorrowGlobal(a) => write!(f, "MutBorrowGlobal({:?})", a),
Bytecode::MutBorrowGlobalGeneric(a) => write!(f, "MutBorrowGlobalGeneric({:?})", a),
Bytecode::ImmBorrowGlobal(a) => write!(f, "ImmBorrowGlobal({:?})", a),
Bytecode::ImmBorrowGlobalGeneric(a) => write!(f, "ImmBorrowGlobalGeneric({:?})", a),
Bytecode::Add => write!(f, "Add"),
Bytecode::Sub => write!(f, "Sub"),
Bytecode::Mul => write!(f, "Mul"),
Bytecode::Mod => write!(f, "Mod"),
Bytecode::Div => write!(f, "Div"),
Bytecode::BitOr => write!(f, "BitOr"),
Bytecode::BitAnd => write!(f, "BitAnd"),
Bytecode::Xor => write!(f, "Xor"),
Bytecode::Shl => write!(f, "Shl"),
Bytecode::Shr => write!(f, "Shr"),
Bytecode::Or => write!(f, "Or"),
Bytecode::And => write!(f, "And"),
Bytecode::Not => write!(f, "Not"),
Bytecode::Eq => write!(f, "Eq"),
Bytecode::Neq => write!(f, "Neq"),
Bytecode::Lt => write!(f, "Lt"),
Bytecode::Gt => write!(f, "Gt"),
Bytecode::Le => write!(f, "Le"),
Bytecode::Ge => write!(f, "Ge"),
Bytecode::Abort => write!(f, "Abort"),
Bytecode::Nop => write!(f, "Nop"),
Bytecode::Exists(a) => write!(f, "Exists({:?})", a),
Bytecode::ExistsGeneric(a) => write!(f, "ExistsGeneric({:?})", a),
Bytecode::MoveFrom(a) => write!(f, "MoveFrom({:?})", a),
Bytecode::MoveFromGeneric(a) => write!(f, "MoveFromGeneric({:?})", a),
Bytecode::MoveTo(a) => write!(f, "MoveTo({:?})", a),
Bytecode::MoveToGeneric(a) => write!(f, "MoveToGeneric({:?})", a),
Bytecode::VecPack(a, n) => write!(f, "VecPack({}, {})", a, n),
Bytecode::VecLen(a) => write!(f, "VecLen({})", a),
Bytecode::VecImmBorrow(a) => write!(f, "VecImmBorrow({})", a),
Bytecode::VecMutBorrow(a) => write!(f, "VecMutBorrow({})", a),
Bytecode::VecPushBack(a) => write!(f, "VecPushBack({})", a),
Bytecode::VecPopBack(a) => write!(f, "VecPopBack({})", a),
Bytecode::VecUnpack(a, n) => write!(f, "VecUnpack({}, {})", a, n),
Bytecode::VecSwap(a) => write!(f, "VecSwap({})", a),
}
}
}
impl Bytecode {
pub fn is_unconditional_branch(&self) -> bool {
matches!(self, Bytecode::Ret | Bytecode::Abort | Bytecode::Branch(_))
}
pub fn is_conditional_branch(&self) -> bool {
matches!(self, Bytecode::BrFalse(_) | Bytecode::BrTrue(_))
}
pub fn is_branch(&self) -> bool {
self.is_conditional_branch() || self.is_unconditional_branch()
}
pub fn offset(&self) -> Option<&CodeOffset> {
match self {
Bytecode::BrFalse(offset) | Bytecode::BrTrue(offset) | Bytecode::Branch(offset) => {
Some(offset)
}
_ => None,
}
}
pub fn get_successors(pc: CodeOffset, code: &[Bytecode]) -> Vec<CodeOffset> {
assert!(
pc <= u16::max_value() - 2 && (pc as usize) < code.len(),
"Program counter out of bounds"
);
let bytecode = &code[pc as usize];
let mut v = vec![];
if let Some(offset) = bytecode.offset() {
v.push(*offset);
}
let next_pc = pc + 1;
if next_pc >= code.len() as CodeOffset {
return v;
}
if !bytecode.is_unconditional_branch() && !v.contains(&next_pc) {
v.push(pc + 1);
}
if v.len() > 1 && v[0] > v[1] {
v.swap(0, 1);
}
v
}
}
#[derive(Clone, Default, Eq, PartialEq, Debug)]
pub struct CompiledScript {
pub version: u32,
pub module_handles: Vec<ModuleHandle>,
pub struct_handles: Vec<StructHandle>,
pub function_handles: Vec<FunctionHandle>,
pub function_instantiations: Vec<FunctionInstantiation>,
pub signatures: SignaturePool,
pub identifiers: IdentifierPool,
pub address_identifiers: AddressIdentifierPool,
pub constant_pool: ConstantPool,
pub metadata: Vec<Metadata>,
pub code: CodeUnit,
pub type_parameters: Vec<AbilitySet>,
pub parameters: SignatureIndex,
}
impl CompiledScript {
pub const MAIN_INDEX: FunctionDefinitionIndex = FunctionDefinitionIndex(0);
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct CompiledModule {
pub version: u32,
pub self_module_handle_idx: ModuleHandleIndex,
pub module_handles: Vec<ModuleHandle>,
pub struct_handles: Vec<StructHandle>,
pub function_handles: Vec<FunctionHandle>,
pub field_handles: Vec<FieldHandle>,
pub friend_decls: Vec<ModuleHandle>,
pub struct_def_instantiations: Vec<StructDefInstantiation>,
pub function_instantiations: Vec<FunctionInstantiation>,
pub field_instantiations: Vec<FieldInstantiation>,
pub signatures: SignaturePool,
pub identifiers: IdentifierPool,
pub address_identifiers: AddressIdentifierPool,
pub constant_pool: ConstantPool,
pub metadata: Vec<Metadata>,
pub struct_defs: Vec<StructDefinition>,
pub function_defs: Vec<FunctionDefinition>,
}
#[cfg(any(test, feature = "fuzzing"))]
impl Arbitrary for CompiledScript {
type Strategy = BoxedStrategy<Self>;
type Parameters = usize;
fn arbitrary_with(size: Self::Parameters) -> Self::Strategy {
(
(
vec(any::<ModuleHandle>(), 0..=size),
vec(any::<StructHandle>(), 0..=size),
vec(any::<FunctionHandle>(), 0..=size),
),
vec(any_with::<Signature>(size), 0..=size),
(
vec(any::<Identifier>(), 0..=size),
vec(any::<AccountAddress>(), 0..=size),
),
vec(any::<AbilitySet>(), 0..=size),
any::<SignatureIndex>(),
any::<CodeUnit>(),
)
.prop_map(
|(
(module_handles, struct_handles, function_handles),
signatures,
(identifiers, address_identifiers),
type_parameters,
parameters,
code,
)| {
CompiledScript {
version: file_format_common::VERSION_MAX,
module_handles,
struct_handles,
function_handles,
function_instantiations: vec![],
signatures,
identifiers,
address_identifiers,
constant_pool: vec![],
metadata: vec![],
type_parameters,
parameters,
code,
}
},
)
.boxed()
}
}
#[cfg(any(test, feature = "fuzzing"))]
impl Arbitrary for CompiledModule {
type Strategy = BoxedStrategy<Self>;
type Parameters = usize;
fn arbitrary_with(size: Self::Parameters) -> Self::Strategy {
(
(
vec(any::<ModuleHandle>(), 0..=size),
vec(any::<StructHandle>(), 0..=size),
vec(any::<FunctionHandle>(), 0..=size),
),
any::<ModuleHandleIndex>(),
vec(any::<ModuleHandle>(), 0..=size),
vec(any_with::<Signature>(size), 0..=size),
(
vec(any::<Identifier>(), 0..=size),
vec(any::<AccountAddress>(), 0..=size),
),
(
vec(any::<StructDefinition>(), 0..=size),
vec(any_with::<FunctionDefinition>(size), 0..=size),
),
)
.prop_map(
|(
(module_handles, struct_handles, function_handles),
self_module_handle_idx,
friend_decls,
signatures,
(identifiers, address_identifiers),
(struct_defs, function_defs),
)| {
CompiledModule {
version: file_format_common::VERSION_MAX,
module_handles,
struct_handles,
function_handles,
self_module_handle_idx,
field_handles: vec![],
friend_decls,
struct_def_instantiations: vec![],
function_instantiations: vec![],
field_instantiations: vec![],
signatures,
identifiers,
address_identifiers,
constant_pool: vec![],
metadata: vec![],
struct_defs,
function_defs,
}
},
)
.boxed()
}
}
impl CompiledModule {
pub fn kind_count(&self, kind: IndexKind) -> usize {
debug_assert!(!matches!(
kind,
IndexKind::LocalPool
| IndexKind::CodeDefinition
| IndexKind::FieldDefinition
| IndexKind::TypeParameter
| IndexKind::MemberCount
));
match kind {
IndexKind::ModuleHandle => self.module_handles.len(),
IndexKind::StructHandle => self.struct_handles.len(),
IndexKind::FunctionHandle => self.function_handles.len(),
IndexKind::FieldHandle => self.field_handles.len(),
IndexKind::FriendDeclaration => self.friend_decls.len(),
IndexKind::StructDefInstantiation => self.struct_def_instantiations.len(),
IndexKind::FunctionInstantiation => self.function_instantiations.len(),
IndexKind::FieldInstantiation => self.field_instantiations.len(),
IndexKind::StructDefinition => self.struct_defs.len(),
IndexKind::FunctionDefinition => self.function_defs.len(),
IndexKind::Signature => self.signatures.len(),
IndexKind::Identifier => self.identifiers.len(),
IndexKind::AddressIdentifier => self.address_identifiers.len(),
IndexKind::ConstantPool => self.constant_pool.len(),
other @ IndexKind::LocalPool
| other @ IndexKind::CodeDefinition
| other @ IndexKind::FieldDefinition
| other @ IndexKind::TypeParameter
| other @ IndexKind::MemberCount => unreachable!("invalid kind for count: {:?}", other),
}
}
pub fn module_id_for_handle(&self, module_handle: &ModuleHandle) -> ModuleId {
ModuleId::new(
*self.address_identifier_at(module_handle.address),
self.identifier_at(module_handle.name).to_owned(),
)
}
pub fn self_id(&self) -> ModuleId {
self.module_id_for_handle(self.self_handle())
}
}
pub fn empty_module() -> CompiledModule {
CompiledModule {
version: file_format_common::VERSION_MAX,
module_handles: vec![ModuleHandle {
address: AddressIdentifierIndex(0),
name: IdentifierIndex(0),
}],
self_module_handle_idx: ModuleHandleIndex(0),
identifiers: vec![self_module_name().to_owned()],
address_identifiers: vec![AccountAddress::ZERO],
constant_pool: vec![],
metadata: vec![],
function_defs: vec![],
struct_defs: vec![],
struct_handles: vec![],
function_handles: vec![],
field_handles: vec![],
friend_decls: vec![],
struct_def_instantiations: vec![],
function_instantiations: vec![],
field_instantiations: vec![],
signatures: vec![Signature(vec![])],
}
}
pub fn basic_test_module() -> CompiledModule {
let mut m = empty_module();
m.function_handles.push(FunctionHandle {
module: ModuleHandleIndex(0),
name: IdentifierIndex(m.identifiers.len() as u16),
parameters: SignatureIndex(0),
return_: SignatureIndex(0),
type_parameters: vec![],
});
m.identifiers
.push(Identifier::new("foo".to_string()).unwrap());
m.function_defs.push(FunctionDefinition {
function: FunctionHandleIndex(0),
visibility: Visibility::Private,
is_entry: false,
acquires_global_resources: vec![],
code: Some(CodeUnit {
locals: SignatureIndex(0),
code: vec![Bytecode::Ret],
}),
});
m.struct_handles.push(StructHandle {
module: ModuleHandleIndex(0),
name: IdentifierIndex(m.identifiers.len() as u16),
abilities: AbilitySet::EMPTY,
type_parameters: vec![],
});
m.identifiers
.push(Identifier::new("Bar".to_string()).unwrap());
m.struct_defs.push(StructDefinition {
struct_handle: StructHandleIndex(0),
field_information: StructFieldInformation::Declared(vec![FieldDefinition {
name: IdentifierIndex(m.identifiers.len() as u16),
signature: TypeSignature(SignatureToken::U64),
}]),
});
m.identifiers
.push(Identifier::new("x".to_string()).unwrap());
m
}
pub fn empty_script() -> CompiledScript {
CompiledScript {
version: file_format_common::VERSION_MAX,
module_handles: vec![],
struct_handles: vec![],
function_handles: vec![],
function_instantiations: vec![],
signatures: vec![Signature(vec![])],
identifiers: vec![],
address_identifiers: vec![],
constant_pool: vec![],
metadata: vec![],
type_parameters: vec![],
parameters: SignatureIndex(0),
code: CodeUnit {
locals: SignatureIndex(0),
code: vec![Bytecode::Ret],
},
}
}
pub fn basic_test_script() -> CompiledScript {
empty_script()
}