From ca555da6d7ebf24667b32005b22d42a361c601c6 Mon Sep 17 00:00:00 2001 From: Runtian Zhou Date: Thu, 9 Mar 2023 00:58:22 -0800 Subject: [PATCH 01/11] Initial implementation of function pointer type --- .../tests/serializer_tests.rs | 22 ++++++++- .../move-binary-format/src/binary_views.rs | 1 + .../move-binary-format/src/check_bounds.rs | 5 ++- language/move-binary-format/src/constant.rs | 3 +- .../move-binary-format/src/deserializer.rs | 44 ++++++++++++++++++ .../move-binary-format/src/file_format.rs | 45 +++++++++++++++++-- .../src/file_format_common.rs | 7 ++- language/move-binary-format/src/normalized.rs | 29 +++++++++++- .../src/proptest_types/functions.rs | 2 + .../src/proptest_types/signature.rs | 16 ++++++- .../src/proptest_types/types.rs | 1 + language/move-binary-format/src/serializer.rs | 5 +++ 12 files changed, 170 insertions(+), 10 deletions(-) diff --git a/language/move-binary-format/serializer-tests/tests/serializer_tests.rs b/language/move-binary-format/serializer-tests/tests/serializer_tests.rs index bacd57b72a..e3e96544cd 100644 --- a/language/move-binary-format/serializer-tests/tests/serializer_tests.rs +++ b/language/move-binary-format/serializer-tests/tests/serializer_tests.rs @@ -2,7 +2,7 @@ // Copyright (c) The Move Contributors // SPDX-License-Identifier: Apache-2.0 -use move_binary_format::file_format::CompiledModule; +use move_binary_format::file_format::*; use proptest::prelude::*; proptest! { @@ -34,3 +34,23 @@ proptest! { prop_assert_eq!(module, deserialized_module); } } + +#[test] +fn single_fp_test() { + let mut module = empty_module(); + + module.signatures.push(Signature(vec![ + SignatureToken::Function(Box::new(FunctionType { + parameters: vec![SignatureToken::U8], + return_: vec![SignatureToken::U128], + })) + ])); + + let mut serialized = Vec::with_capacity(65536); + module.serialize(&mut serialized).expect("serialization should work"); + + let deserialized_module = CompiledModule::deserialize_no_check_bounds(&serialized) + .expect("deserialization should work"); + + assert_eq!(module, deserialized_module); +} diff --git a/language/move-binary-format/src/binary_views.rs b/language/move-binary-format/src/binary_views.rs index 43a002dc6f..acf05fecd1 100644 --- a/language/move-binary-format/src/binary_views.rs +++ b/language/move-binary-format/src/binary_views.rs @@ -261,6 +261,7 @@ impl<'a> BinaryIndexedView<'a> { Reference(_) | MutableReference(_) => Ok(AbilitySet::REFERENCES), Signer => Ok(AbilitySet::SIGNER), + Function(_) => Ok(AbilitySet::FUNCTION), TypeParameter(idx) => Ok(constraints[*idx as usize]), Vector(ty) => AbilitySet::polymorphic_abilities( AbilitySet::VECTOR, diff --git a/language/move-binary-format/src/check_bounds.rs b/language/move-binary-format/src/check_bounds.rs index c60fa080c5..dbd7182e27 100644 --- a/language/move-binary-format/src/check_bounds.rs +++ b/language/move-binary-format/src/check_bounds.rs @@ -544,7 +544,7 @@ impl<'a> BoundsChecker<'a> { for ty in ty.preorder_traversal() { match ty { Bool | U8 | U16 | U32 | U64 | U128 | U256 | Address | Signer | TypeParameter(_) - | Reference(_) | MutableReference(_) | Vector(_) => (), + | Reference(_) | MutableReference(_) | Vector(_) | Function(_) => (), Struct(idx) => { check_bounds_impl(self.view.struct_handles(), *idx)?; if let Some(sh) = self.view.struct_handles().get(idx.into_index()) { @@ -612,7 +612,8 @@ impl<'a> BoundsChecker<'a> { | Reference(_) | MutableReference(_) | Vector(_) - | StructInstantiation(_, _) => (), + | StructInstantiation(_, _) + | Function(_) => (), } } Ok(()) diff --git a/language/move-binary-format/src/constant.rs b/language/move-binary-format/src/constant.rs index a6df17d72a..a86af249d0 100644 --- a/language/move-binary-format/src/constant.rs +++ b/language/move-binary-format/src/constant.rs @@ -22,7 +22,8 @@ fn sig_to_ty(sig: &SignatureToken) -> Option { | SignatureToken::MutableReference(_) | SignatureToken::Struct(_) | SignatureToken::TypeParameter(_) - | SignatureToken::StructInstantiation(_, _) => None, + | SignatureToken::StructInstantiation(_, _) + | SignatureToken::Function(_) => None, } } diff --git a/language/move-binary-format/src/deserializer.rs b/language/move-binary-format/src/deserializer.rs index 33ca060009..be2b49edde 100644 --- a/language/move-binary-format/src/deserializer.rs +++ b/language/move-binary-format/src/deserializer.rs @@ -986,6 +986,12 @@ fn load_signature_token(cursor: &mut VersionedCursor) -> BinaryLoaderResult, }, + Function { + params_len: usize, + parameters: Vec, + return_len: usize, + return_: Vec, + } } impl TypeBuilder { @@ -1012,6 +1018,29 @@ fn load_signature_token(cursor: &mut VersionedCursor) -> BinaryLoaderResult { + if parameters.len() < params_len { + parameters.push(tok); + T::Function { params_len, parameters, return_len, return_ } + } else if return_.len() < return_len { + return_.push(tok); + if return_.len() == return_len { + T::Saturated(SignatureToken::Function(Box::new(FunctionType { + parameters, + return_, + }))) + } else { + T::Function { params_len, parameters, return_len, return_ } + } + } else { + unreachable!("invalid type constructor application") + } + } _ => unreachable!("invalid type constructor application"), } } @@ -1041,6 +1070,14 @@ fn load_signature_token(cursor: &mut VersionedCursor) -> BinaryLoaderResult { + return Err( + PartialVMError::new(StatusCode::MALFORMED).with_message(format!( + "u16, u32, u256 integers not supported in bytecode version {}", + cursor.version() + )), + ); + }, _ => (), }; @@ -1078,6 +1115,12 @@ fn load_signature_token(cursor: &mut VersionedCursor) -> BinaryLoaderResult { + let params_len = load_type_parameter_count(cursor)?; + let return_len = load_type_parameter_count(cursor)?; + + T::Function { params_len, parameters: vec![], return_len, return_: vec![] } + } }) } else { Err(PartialVMError::new(StatusCode::MALFORMED) @@ -1641,6 +1684,7 @@ impl SerializedType { 0xD => Ok(SerializedType::U16), 0xE => Ok(SerializedType::U32), 0xF => Ok(SerializedType::U256), + 0xFF => Ok(SerializedType::FUNCTION), _ => Err(PartialVMError::new(StatusCode::UNKNOWN_SERIALIZED_TYPE)), } } diff --git a/language/move-binary-format/src/file_format.rs b/language/move-binary-format/src/file_format.rs index e1bb74957d..2df7de3bb7 100644 --- a/language/move-binary-format/src/file_format.rs +++ b/language/move-binary-format/src/file_format.rs @@ -297,6 +297,20 @@ pub struct FunctionHandle { pub type_parameters: Vec, } +/// A `FunctionType` is the type of a function pointer. +/// +/// It's similar to function handle but don't have module names and type parameters. All type parameters will be fully instantiated at type creation time. +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(any(test, feature = "fuzzing"), derive(proptest_derive::Arbitrary))] +#[cfg_attr(any(test, feature = "fuzzing"), proptest(params = "usize"))] +#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] +pub struct FunctionType { + /// The list of arguments to the function. + pub parameters: Vec, + /// The list of return types. + pub return_: Vec, +} + /// A field access info (owner type and offset) #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(any(test, feature = "fuzzing"), derive(proptest_derive::Arbitrary))] @@ -634,6 +648,9 @@ impl AbilitySet { /// Abilities for `Bool`, `U8`, `U64`, `U128`, and `Address` pub const PRIMITIVES: AbilitySet = Self((Ability::Copy as u8) | (Ability::Drop as u8) | (Ability::Store as u8)); + /// Abilities for Function Pointer + pub const FUNCTION: AbilitySet = + Self((Ability::Copy as u8) | (Ability::Drop as u8)); /// Abilities for `Reference` and `MutableReference` pub const REFERENCES: AbilitySet = Self((Ability::Copy as u8) | (Ability::Drop as u8)); /// Abilities for `Signer` @@ -868,6 +885,8 @@ pub enum SignatureToken { U32, /// Unsigned integers, 256 bits length. U256, + /// Types for a function pointer at runtime. + Function(Box), } /// An iterator to help traverse the `SignatureToken` in a non-recursive fashion to avoid @@ -895,6 +914,11 @@ impl<'a> Iterator for SignatureTokenPreorderTraversalIter<'a> { self.stack.extend(inner_toks.iter().rev()) } + Function(func_ty) => { + self.stack.extend(func_ty.return_.iter().rev()); + self.stack.extend(func_ty.parameters.iter().rev()); + } + Signer | Bool | Address | U8 | U16 | U32 | U64 | U128 | U256 | Struct(_) | TypeParameter(_) => (), } @@ -928,6 +952,13 @@ impl<'a> Iterator for SignatureTokenPreorderTraversalIterWithDepth<'a> { .stack .extend(inner_toks.iter().map(|tok| (tok, depth + 1)).rev()), + Function(func_ty) => { + self.stack + .extend(func_ty.return_.iter().map(|tok| (tok, depth + 1)).rev()); + self.stack + .extend(func_ty.parameters.iter().map(|tok| (tok, depth + 1)).rev()); + } + Signer | Bool | Address | U8 | U16 | U32 | U64 | U128 | U256 | Struct(_) | TypeParameter(_) => (), } @@ -995,6 +1026,11 @@ impl std::fmt::Debug for SignatureToken { SignatureToken::Reference(boxed) => write!(f, "Reference({:?})", boxed), SignatureToken::MutableReference(boxed) => write!(f, "MutableReference({:?})", boxed), SignatureToken::TypeParameter(idx) => write!(f, "TypeParameter({:?})", idx), + SignatureToken::Function(func_ty) => write!( + f, + "Function({:?} => {:?})", + func_ty.parameters, func_ty.return_ + ), } } } @@ -1021,7 +1057,8 @@ impl SignatureToken { | Signer | Struct(_) | StructInstantiation(_, _) - | Vector(_) => SignatureTokenKind::Value, + | Vector(_) + | Function(_) => SignatureTokenKind::Value, // TODO: This is a temporary hack to please the verifier. SignatureTokenKind will soon // be completely removed. `SignatureTokenView::kind()` should be used instead. TypeParameter(_) => SignatureTokenKind::Value, @@ -1041,7 +1078,8 @@ impl SignatureToken { | StructInstantiation(_, _) | Reference(_) | MutableReference(_) - | TypeParameter(_) => false, + | TypeParameter(_) + | Function(_) => false, } } @@ -1079,7 +1117,8 @@ impl SignatureToken { | StructInstantiation(_, _) | Reference(_) | MutableReference(_) - | TypeParameter(_) => false, + | TypeParameter(_) + | Function(_) => false, } } diff --git a/language/move-binary-format/src/file_format_common.rs b/language/move-binary-format/src/file_format_common.rs index 392090b574..30cbfead73 100644 --- a/language/move-binary-format/src/file_format_common.rs +++ b/language/move-binary-format/src/file_format_common.rs @@ -124,6 +124,7 @@ pub enum SerializedType { U16 = 0xD, U32 = 0xE, U256 = 0xF, + FUNCTION = 0xFF, } #[rustfmt::skip] @@ -405,8 +406,12 @@ pub const VERSION_5: u32 = 5; /// + u16, u32, u256 integers and corresponding Ld, Cast bytecodes pub const VERSION_6: u32 = 6; +/// Version 7: changes compared with version 6 +/// + function pointers? +pub const VERSION_7: u32 = 7; + // Mark which version is the latest version -pub const VERSION_MAX: u32 = VERSION_6; +pub const VERSION_MAX: u32 = VERSION_7; // Mark which oldest version is supported. // TODO(#145): finish v4 compatibility; as of now, only metadata is implemented diff --git a/language/move-binary-format/src/normalized.rs b/language/move-binary-format/src/normalized.rs index 183ae569c0..02f2f956fc 100644 --- a/language/move-binary-format/src/normalized.rs +++ b/language/move-binary-format/src/normalized.rs @@ -60,6 +60,12 @@ pub enum Type { U32, #[serde(rename = "u256")] U256, + // TBD: Which bytecode version should we use here? + #[serde(rename = "function")] + Function { + arguments: Vec, + return_: Vec, + }, } /// Normalized version of a `FieldDefinition`. The `name` is included even though it is @@ -179,6 +185,18 @@ impl Type { TypeParameter(i) => Type::TypeParameter(*i), Reference(t) => Type::Reference(Box::new(Type::new(m, t))), MutableReference(t) => Type::MutableReference(Box::new(Type::new(m, t))), + Function(func_ty) => Type::Function { + arguments: func_ty + .parameters + .iter() + .map(|tok| Type::new(m, tok)) + .collect(), + return_: func_ty + .return_ + .iter() + .map(|tok| Type::new(m, tok)) + .collect(), + }, } } @@ -198,6 +216,9 @@ impl Type { Signer => true, Struct { type_arguments, .. } => type_arguments.iter().all(|t| t.is_closed()), Vector(t) | Reference(t) | MutableReference(t) => t.is_closed(), + Function { arguments, return_ } => { + arguments.iter().all(|t| t.is_closed()) && return_.iter().all(|t| t.is_closed()) + } } } @@ -205,7 +226,7 @@ impl Type { use Type::*; Some(if self.is_closed() { match self { - Reference(_) | MutableReference(_) => return None, + Reference(_) | MutableReference(_) | Function { .. } => return None, Bool => TypeTag::Bool, U8 => TypeTag::U8, U16 => TypeTag::U16, @@ -276,6 +297,9 @@ impl Type { .get(*i as usize) .expect("Type parameter index out of bound") .clone(), + Function { arguments, return_} => { + Function { arguments: arguments.iter().map(|ty| ty.subst(type_args)).collect(), return_: return_.iter().map(|ty| ty.subst(type_args)).collect() } + } } } } @@ -417,6 +441,9 @@ impl std::fmt::Display for Type { Type::Reference(r) => write!(f, "&{}", r), Type::MutableReference(r) => write!(f, "&mut {}", r), Type::TypeParameter(i) => write!(f, "T{:?}", i), + Type::Function { arguments, return_ } => { + write!(f, "Function({:?} => {:?})", arguments, return_) + } } } } diff --git a/language/move-binary-format/src/proptest_types/functions.rs b/language/move-binary-format/src/proptest_types/functions.rs index 30c9a28af8..b766e30e45 100644 --- a/language/move-binary-format/src/proptest_types/functions.rs +++ b/language/move-binary-format/src/proptest_types/functions.rs @@ -896,6 +896,8 @@ impl BytecodeGen { match token { U8 | U16 | U32 | U64 | U128 | U256 | Bool | Address | Signer | Struct(_) | TypeParameter(_) => true, + Function(ty) => + ty.return_.iter().all(BytecodeGen::check_signature_token) && ty.parameters.iter().all(BytecodeGen::check_signature_token), Vector(element_token) => BytecodeGen::check_signature_token(element_token), StructInstantiation(_, type_arguments) => type_arguments .iter() diff --git a/language/move-binary-format/src/proptest_types/signature.rs b/language/move-binary-format/src/proptest_types/signature.rs index 5b94c12925..61b4014073 100644 --- a/language/move-binary-format/src/proptest_types/signature.rs +++ b/language/move-binary-format/src/proptest_types/signature.rs @@ -4,7 +4,7 @@ use crate::file_format::{ Ability, AbilitySet, Signature, SignatureToken, StructHandle, StructHandleIndex, TableIndex, - TypeParameterIndex, + TypeParameterIndex, FunctionType, }; use proptest::{ collection::{vec, SizeRange}, @@ -98,6 +98,7 @@ pub enum SignatureTokenGen { Vector(Box), Reference(Box), MutableReference(Box), + Function(Vec, Vec), } impl SignatureTokenGen { @@ -107,6 +108,7 @@ impl SignatureTokenGen { (1, Self::reference_strategy().boxed()), (1, Self::mutable_reference_strategy().boxed()), (1, Self::vector_strategy().boxed()), + (1, Self::function_strategy().boxed()), ]) } @@ -157,6 +159,12 @@ impl SignatureTokenGen { Self::owned_strategy().prop_map(|atom| SignatureTokenGen::MutableReference(Box::new(atom))) } + pub fn function_strategy() -> impl Strategy { + (vec(Self::owned_strategy(), 5), vec(Self::owned_strategy(), 5)).prop_map(|(params, return_)| { + SignatureTokenGen::Function(params, return_) + }) + } + pub fn materialize(self, struct_handles: &[StructHandle]) -> SignatureToken { use SignatureTokenGen::*; match self { @@ -169,6 +177,12 @@ impl SignatureTokenGen { U256 => SignatureToken::U256, Address => SignatureToken::Address, Signer => SignatureToken::Signer, + Function(params_gen, return_gen) => { + SignatureToken::Function(Box::new(FunctionType { + parameters: params_gen.into_iter().map(|gen| gen.materialize(struct_handles)).collect(), + return_: return_gen.into_iter().map(|gen| gen.materialize(struct_handles)).collect(), + })) + }, Struct(idx) => { let struct_handles_len = struct_handles.len(); if struct_handles_len == 0 { diff --git a/language/move-binary-format/src/proptest_types/types.rs b/language/move-binary-format/src/proptest_types/types.rs index 8879daa9e6..e7f1b7fc79 100644 --- a/language/move-binary-format/src/proptest_types/types.rs +++ b/language/move-binary-format/src/proptest_types/types.rs @@ -62,6 +62,7 @@ impl StDefnMaterializeState { match ty { Bool | U8 | U16 | U32 | U64 | U128 | U256 | Address => AbilitySet::PRIMITIVES, + Function(_) => AbilitySet::FUNCTION, Reference(_) | MutableReference(_) => AbilitySet::REFERENCES, Signer => AbilitySet::SIGNER, diff --git a/language/move-binary-format/src/serializer.rs b/language/move-binary-format/src/serializer.rs index 3a63bf5717..603900eb95 100644 --- a/language/move-binary-format/src/serializer.rs +++ b/language/move-binary-format/src/serializer.rs @@ -670,6 +670,11 @@ fn serialize_signature_token_single_node_impl( binary.push(SerializedType::TYPE_PARAMETER as u8)?; serialize_type_parameter_index(binary, *idx)?; } + SignatureToken::Function(func_ty) => { + binary.push(SerializedType::FUNCTION as u8)?; + serialize_signature_size(binary, func_ty.parameters.len())?; + serialize_signature_size(binary, func_ty.return_.len())?; + } } Ok(()) } From 3f35bcb5235a0394aed1ce6ef51dcc368d1ee948 Mon Sep 17 00:00:00 2001 From: Runtian Zhou Date: Thu, 9 Mar 2023 05:09:26 -0800 Subject: [PATCH 02/11] Add bytecode instructions --- .../move-binary-format/src/check_bounds.rs | 6 +++--- .../move-binary-format/src/deserializer.rs | 20 +++++++++++++++++++ .../move-binary-format/src/file_format.rs | 12 +++++++++++ .../src/file_format_common.rs | 7 +++++++ language/move-binary-format/src/serializer.rs | 19 ++++++++++++++++++ 5 files changed, 61 insertions(+), 3 deletions(-) diff --git a/language/move-binary-format/src/check_bounds.rs b/language/move-binary-format/src/check_bounds.rs index dbd7182e27..50c104f0f0 100644 --- a/language/move-binary-format/src/check_bounds.rs +++ b/language/move-binary-format/src/check_bounds.rs @@ -415,12 +415,12 @@ impl<'a> BoundsChecker<'a> { } } } - Call(idx) => self.check_code_unit_bounds_impl( + Call(idx) | GetFunctionPointer(idx) => self.check_code_unit_bounds_impl( self.view.function_handles(), *idx, bytecode_offset, )?, - CallGeneric(idx) => { + CallGeneric(idx) | GetFunctionPointerGeneric(idx) => { self.check_code_unit_bounds_impl( self.view.function_instantiations(), *idx, @@ -532,7 +532,7 @@ impl<'a> BoundsChecker<'a> { | LdU128(_) | CastU8 | CastU16 | CastU32 | CastU64 | CastU128 | CastU256 | LdTrue | LdFalse | ReadRef | WriteRef | Add | Sub | Mul | Mod | Div | BitOr | BitAnd | Xor | Shl | Shr | Or | And | Not | Eq | Neq | Lt | Gt | Le | Ge - | Abort | Nop => (), + | Abort | Nop | CallFunctionPointer => (), } } Ok(()) diff --git a/language/move-binary-format/src/deserializer.rs b/language/move-binary-format/src/deserializer.rs index be2b49edde..680612a57e 100644 --- a/language/move-binary-format/src/deserializer.rs +++ b/language/move-binary-format/src/deserializer.rs @@ -1521,6 +1521,22 @@ fn load_code(cursor: &mut VersionedCursor, code: &mut Vec) -> BinaryLo _ => (), }; + match opcode { + Opcodes::CALL_FUNC_PTR + | Opcodes::GET_FUNC_PTR + | Opcodes::GET_FUNC_PTR_GENERIC + if (cursor.version() < VERSION_7) => + { + return Err( + PartialVMError::new(StatusCode::MALFORMED).with_message(format!( + "Function Pointer not supported in bytecode version {}", + cursor.version() + )), + ); + } + _ => (), + }; + // conversion let bytecode = match opcode { Opcodes::POP => Bytecode::Pop, @@ -1637,6 +1653,10 @@ fn load_code(cursor: &mut VersionedCursor, code: &mut Vec) -> BinaryLo Opcodes::CAST_U16 => Bytecode::CastU16, Opcodes::CAST_U32 => Bytecode::CastU32, Opcodes::CAST_U256 => Bytecode::CastU256, + + Opcodes::GET_FUNC_PTR => Bytecode::GetFunctionPointer(load_function_handle_index(cursor)?), + Opcodes::GET_FUNC_PTR_GENERIC => Bytecode::GetFunctionPointerGeneric(load_function_inst_index(cursor)?), + Opcodes::CALL_FUNC_PTR => Bytecode::CallFunctionPointer, }; code.push(bytecode); } diff --git a/language/move-binary-format/src/file_format.rs b/language/move-binary-format/src/file_format.rs index 2df7de3bb7..820898da0f 100644 --- a/language/move-binary-format/src/file_format.rs +++ b/language/move-binary-format/src/file_format.rs @@ -1674,6 +1674,15 @@ pub enum Bytecode { /// /// ```..., integer_value -> ..., u256_value``` CastU256, + /// Function Pointers + /// + /// + + GetFunctionPointer(FunctionHandleIndex), + + GetFunctionPointerGeneric(FunctionInstantiationIndex), + + CallFunctionPointer, } impl ::std::fmt::Debug for Bytecode { @@ -1756,6 +1765,9 @@ impl ::std::fmt::Debug for Bytecode { Bytecode::VecPopBack(a) => write!(f, "VecPopBack({})", a), Bytecode::VecUnpack(a, n) => write!(f, "VecUnpack({}, {})", a, n), Bytecode::VecSwap(a) => write!(f, "VecSwap({})", a), + Bytecode::CallFunctionPointer => write!(f, "CallFuncPtr"), + Bytecode::GetFunctionPointer(idx) => write!(f, "GetFuncPtr({:?})", idx), + Bytecode::GetFunctionPointerGeneric(idx) => write!(f, "GetFuncPtrGeneric({:?})", idx), } } } diff --git a/language/move-binary-format/src/file_format_common.rs b/language/move-binary-format/src/file_format_common.rs index 30cbfead73..2007bbbf9d 100644 --- a/language/move-binary-format/src/file_format_common.rs +++ b/language/move-binary-format/src/file_format_common.rs @@ -219,6 +219,10 @@ pub enum Opcodes { CAST_U16 = 0x4B, CAST_U32 = 0x4C, CAST_U256 = 0x4D, + + GET_FUNC_PTR = 0xF0, + GET_FUNC_PTR_GENERIC = 0xF1, + CALL_FUNC_PTR = 0xF2, } /// Upper limit on the binary size @@ -630,6 +634,9 @@ pub fn instruction_key(instruction: &Bytecode) -> u8 { CastU16 => Opcodes::CAST_U16, CastU32 => Opcodes::CAST_U32, CastU256 => Opcodes::CAST_U256, + GetFunctionPointer(_) => Opcodes::GET_FUNC_PTR, + GetFunctionPointerGeneric(_) => Opcodes::GET_FUNC_PTR_GENERIC, + CallFunctionPointer => Opcodes::CALL_FUNC_PTR, }; opcode as u8 } diff --git a/language/move-binary-format/src/serializer.rs b/language/move-binary-format/src/serializer.rs index 603900eb95..bdc644dde8 100644 --- a/language/move-binary-format/src/serializer.rs +++ b/language/move-binary-format/src/serializer.rs @@ -753,6 +753,16 @@ fn serialize_instruction_inner( major_version )); } + Bytecode::CallFunctionPointer + | Bytecode::GetFunctionPointer(_) + | Bytecode::GetFunctionPointerGeneric(_) + if (major_version < VERSION_7) => + { + return Err(anyhow!( + "Function pointers not supported in bytecode version {}", + major_version + )); + } _ => (), }; @@ -965,6 +975,15 @@ fn serialize_instruction_inner( Bytecode::CastU16 => binary.push(Opcodes::CAST_U16 as u8), Bytecode::CastU32 => binary.push(Opcodes::CAST_U32 as u8), Bytecode::CastU256 => binary.push(Opcodes::CAST_U256 as u8), + Bytecode::GetFunctionPointer(idx) => { + binary.push(Opcodes::GET_FUNC_PTR as u8)?; + serialize_function_handle_index(binary, idx) + } + Bytecode::GetFunctionPointerGeneric(method_idx) => { + binary.push(Opcodes::GET_FUNC_PTR_GENERIC as u8)?; + serialize_function_inst_index(binary, method_idx) + } + Bytecode::CallFunctionPointer => binary.push(Opcodes::CALL_FUNC_PTR as u8), }; res?; Ok(()) From 74521804bd7080df6b511022de55098a4a3a7946 Mon Sep 17 00:00:00 2001 From: Runtian Zhou Date: Thu, 9 Mar 2023 19:54:02 -0800 Subject: [PATCH 03/11] Fix compilation --- .../move-bytecode-verifier/src/acquires_list_verifier.rs | 1 + language/move-bytecode-verifier/src/dependencies.rs | 7 ++++++- language/move-bytecode-verifier/src/instantiation_loops.rs | 5 +++++ .../move-bytecode-verifier/src/instruction_consistency.rs | 1 + language/move-bytecode-verifier/src/locals_safety/mod.rs | 1 + .../move-bytecode-verifier/src/reference_safety/mod.rs | 1 + language/move-bytecode-verifier/src/signature.rs | 4 ++++ .../move-bytecode-verifier/src/stack_usage_verifier.rs | 1 + language/move-bytecode-verifier/src/struct_defs.rs | 1 + language/move-bytecode-verifier/src/type_safety.rs | 2 ++ language/move-compiler/src/interface_generator.rs | 1 + .../move-ir-compiler/move-ir-to-bytecode/src/context.rs | 1 + language/move-model/src/model.rs | 1 + .../bytecode/src/stackless_bytecode_generator.rs | 1 + language/move-vm/runtime/src/interpreter.rs | 3 +++ language/move-vm/runtime/src/loader.rs | 1 + language/move-vm/types/src/loaded_data/runtime_types.rs | 2 +- language/move-vm/types/src/values/values_impl.rs | 2 +- language/tools/move-bytecode-utils/src/layout.rs | 4 ++-- language/tools/move-disassembler/src/disassembler.rs | 1 + language/tools/move-resource-viewer/src/resolver.rs | 1 + 21 files changed, 37 insertions(+), 5 deletions(-) diff --git a/language/move-bytecode-verifier/src/acquires_list_verifier.rs b/language/move-bytecode-verifier/src/acquires_list_verifier.rs index 8454c56eb8..cefdb5c589 100644 --- a/language/move-bytecode-verifier/src/acquires_list_verifier.rs +++ b/language/move-bytecode-verifier/src/acquires_list_verifier.rs @@ -170,6 +170,7 @@ impl<'a> AcquiresVerifier<'a> { | Bytecode::VecPopBack(_) | Bytecode::VecUnpack(..) | Bytecode::VecSwap(_) => Ok(()), + Bytecode::CallFunctionPointer | Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) => unimplemented!(), } } diff --git a/language/move-bytecode-verifier/src/dependencies.rs b/language/move-bytecode-verifier/src/dependencies.rs index f24b75487f..2a42c8737e 100644 --- a/language/move-bytecode-verifier/src/dependencies.rs +++ b/language/move-bytecode-verifier/src/dependencies.rs @@ -474,6 +474,10 @@ fn compare_types( Ok(()) } } + (SignatureToken::Function(ty1), SignatureToken::Function(ty2)) => { + compare_cross_module_signatures(context, &ty1.parameters, &ty2.parameters, def_module)?; + compare_cross_module_signatures(context,&ty1.return_, &ty2.return_, def_module) + } (SignatureToken::Bool, _) | (SignatureToken::U8, _) | (SignatureToken::U64, _) @@ -488,7 +492,8 @@ fn compare_types( | (SignatureToken::TypeParameter(_), _) | (SignatureToken::U16, _) | (SignatureToken::U32, _) - | (SignatureToken::U256, _) => Err(PartialVMError::new(StatusCode::TYPE_MISMATCH)), + | (SignatureToken::U256, _) + | (SignatureToken::Function(_), _) => Err(PartialVMError::new(StatusCode::TYPE_MISMATCH)), } } diff --git a/language/move-bytecode-verifier/src/instantiation_loops.rs b/language/move-bytecode-verifier/src/instantiation_loops.rs index 9af7dff237..a663ebd5da 100644 --- a/language/move-bytecode-verifier/src/instantiation_loops.rs +++ b/language/move-bytecode-verifier/src/instantiation_loops.rs @@ -154,6 +154,11 @@ impl<'a> InstantiationLoopChecker<'a> { rec(type_params, ty); } } + Function(func_ty) => { + for ty in func_ty.parameters.iter().chain(func_ty.return_.iter()) { + rec(type_params, ty); + } + } } } diff --git a/language/move-bytecode-verifier/src/instruction_consistency.rs b/language/move-bytecode-verifier/src/instruction_consistency.rs index 52cb7cd00c..93fbae21b9 100644 --- a/language/move-bytecode-verifier/src/instruction_consistency.rs +++ b/language/move-bytecode-verifier/src/instruction_consistency.rs @@ -149,6 +149,7 @@ impl<'a> InstructionConsistency<'a> { | Or | And | Not | Eq | Neq | Lt | Gt | Le | Ge | CopyLoc(_) | MoveLoc(_) | StLoc(_) | MutBorrowLoc(_) | ImmBorrowLoc(_) | VecLen(_) | VecImmBorrow(_) | VecMutBorrow(_) | VecPushBack(_) | VecPopBack(_) | VecSwap(_) | Abort | Nop => (), + GetFunctionPointer(_) | GetFunctionPointerGeneric(_) | CallFunctionPointer => unimplemented!(), } } Ok(()) diff --git a/language/move-bytecode-verifier/src/locals_safety/mod.rs b/language/move-bytecode-verifier/src/locals_safety/mod.rs index e552b887b3..f9ca92ed5d 100644 --- a/language/move-bytecode-verifier/src/locals_safety/mod.rs +++ b/language/move-bytecode-verifier/src/locals_safety/mod.rs @@ -152,6 +152,7 @@ fn execute_inner( | Bytecode::VecPopBack(_) | Bytecode::VecUnpack(..) | Bytecode::VecSwap(_) => (), + Bytecode::CallFunctionPointer | Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) => unimplemented!(), }; Ok(()) } diff --git a/language/move-bytecode-verifier/src/reference_safety/mod.rs b/language/move-bytecode-verifier/src/reference_safety/mod.rs index dfd77df384..d9ce93b2de 100644 --- a/language/move-bytecode-verifier/src/reference_safety/mod.rs +++ b/language/move-bytecode-verifier/src/reference_safety/mod.rs @@ -403,6 +403,7 @@ fn execute_inner( let vec_ref = safe_unwrap!(verifier.stack.pop()); state.vector_op(offset, vec_ref, true)?; } + Bytecode::CallFunctionPointer | Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) => unimplemented!(), }; Ok(()) } diff --git a/language/move-bytecode-verifier/src/signature.rs b/language/move-bytecode-verifier/src/signature.rs index 3894b4b448..fde780411c 100644 --- a/language/move-bytecode-verifier/src/signature.rs +++ b/language/move-bytecode-verifier/src/signature.rs @@ -210,6 +210,7 @@ impl<'a> SignatureChecker<'a> { | Ge | CopyLoc(_) | MoveLoc(_) | StLoc(_) | MutBorrowLoc(_) | ImmBorrowLoc(_) | MutBorrowField(_) | ImmBorrowField(_) | MutBorrowGlobal(_) | ImmBorrowGlobal(_) | Exists(_) | MoveTo(_) | MoveFrom(_) | Abort | Nop => Ok(()), + GetFunctionPointer(_) | GetFunctionPointerGeneric(_) | CallFunctionPointer => unimplemented!(), }; result.map_err(|err| { err.append_message_with_separator(' ', format!("at offset {} ", offset)) @@ -260,6 +261,7 @@ impl<'a> SignatureChecker<'a> { | SignatureToken::U256 | SignatureToken::Address | SignatureToken::Signer => {} + SignatureToken::Function(_) => unimplemented!(), } Ok(()) } @@ -302,6 +304,7 @@ impl<'a> SignatureChecker<'a> { } Vector(ty) => self.check_signature_token(ty), StructInstantiation(_, type_arguments) => self.check_signature_tokens(type_arguments), + Function(_) => unimplemented!(), } } @@ -364,6 +367,7 @@ impl<'a> SignatureChecker<'a> { | SignatureToken::U256 | SignatureToken::Address | SignatureToken::Signer => Ok(()), + SignatureToken::Function(_) => unimplemented!(), } } diff --git a/language/move-bytecode-verifier/src/stack_usage_verifier.rs b/language/move-bytecode-verifier/src/stack_usage_verifier.rs index bb4b112e5c..0b8735885e 100644 --- a/language/move-bytecode-verifier/src/stack_usage_verifier.rs +++ b/language/move-bytecode-verifier/src/stack_usage_verifier.rs @@ -262,6 +262,7 @@ impl<'a> StackUsageVerifier<'a> { }; (1, field_count as u64) } + Bytecode::CallFunctionPointer | Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) => unimplemented!(), }) } diff --git a/language/move-bytecode-verifier/src/struct_defs.rs b/language/move-bytecode-verifier/src/struct_defs.rs index 00c9746863..a3d4f9122b 100644 --- a/language/move-bytecode-verifier/src/struct_defs.rs +++ b/language/move-bytecode-verifier/src/struct_defs.rs @@ -141,6 +141,7 @@ impl<'a> StructDefGraphBuilder<'a> { self.add_signature_token(neighbors, cur_idx, t)? } } + T::Function(_) => unimplemented!(), }) } } diff --git a/language/move-bytecode-verifier/src/type_safety.rs b/language/move-bytecode-verifier/src/type_safety.rs index eb1ed2df18..91e1d98f31 100644 --- a/language/move-bytecode-verifier/src/type_safety.rs +++ b/language/move-bytecode-verifier/src/type_safety.rs @@ -834,6 +834,7 @@ fn verify_instr( } verifier.stack.push(ST::U256); } + Bytecode::CallFunctionPointer | Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) => unimplemented!(), }; Ok(()) } @@ -880,6 +881,7 @@ fn instantiate(token: &SignatureToken, subst: &Signature) -> SignatureToken { debug_assert!((*idx as usize) < subst.len()); subst.0[*idx as usize].clone() } + SignatureToken::Function(_) => unimplemented!(), } } diff --git a/language/move-compiler/src/interface_generator.rs b/language/move-compiler/src/interface_generator.rs index bd37bf2967..71692dc9f2 100644 --- a/language/move-compiler/src/interface_generator.rs +++ b/language/move-compiler/src/interface_generator.rs @@ -369,6 +369,7 @@ fn write_signature_token(ctx: &mut Context, t: &SignatureToken) -> String { format!("&mut {}", write_signature_token(ctx, inner)) } SignatureToken::TypeParameter(idx) => write_type_parameter(*idx), + SignatureToken::Function(_) => unimplemented!(), } } diff --git a/language/move-ir-compiler/move-ir-to-bytecode/src/context.rs b/language/move-ir-compiler/move-ir-to-bytecode/src/context.rs index 8afc1aebf0..97d4f97b3f 100644 --- a/language/move-ir-compiler/move-ir-to-bytecode/src/context.rs +++ b/language/move-ir-compiler/move-ir-to-bytecode/src/context.rs @@ -808,6 +808,7 @@ impl<'a> Context<'a> { .collect::>()?; SignatureToken::StructInstantiation(correct_sh_idx, correct_inners) } + SignatureToken::Function(_) => unimplemented!(), }) } diff --git a/language/move-model/src/model.rs b/language/move-model/src/model.rs index ebfb970677..435d58be2f 100644 --- a/language/move-model/src/model.rs +++ b/language/move-model/src/model.rs @@ -2258,6 +2258,7 @@ impl<'env> ModuleEnv<'env> { self.globalize_signatures(args), ) } + SignatureToken::Function(_) => unimplemented!(), } } diff --git a/language/move-prover/bytecode/src/stackless_bytecode_generator.rs b/language/move-prover/bytecode/src/stackless_bytecode_generator.rs index 03cad999ef..a4c2076a8d 100644 --- a/language/move-prover/bytecode/src/stackless_bytecode_generator.rs +++ b/language/move-prover/bytecode/src/stackless_bytecode_generator.rs @@ -1323,6 +1323,7 @@ impl<'a> StacklessBytecodeGenerator<'a> { None, )) } + MoveBytecode::CallFunctionPointer | MoveBytecode::GetFunctionPointer(_) | MoveBytecode::GetFunctionPointerGeneric(_) => unimplemented!(), } } diff --git a/language/move-vm/runtime/src/interpreter.rs b/language/move-vm/runtime/src/interpreter.rs index f51c738636..57c646717b 100644 --- a/language/move-vm/runtime/src/interpreter.rs +++ b/language/move-vm/runtime/src/interpreter.rs @@ -1139,6 +1139,7 @@ impl Frame { | Bytecode::VecPopBack(_) | Bytecode::VecUnpack(_, _) | Bytecode::VecSwap(_) => (), + Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) | Bytecode::CallFunctionPointer => unimplemented!(), }; Ok(()) } @@ -1627,6 +1628,7 @@ impl Frame { .pop_ty()? .check_vec_ref(&ty, true)?; } + Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) | Bytecode::CallFunctionPointer => unimplemented!(), } Ok(()) } @@ -2223,6 +2225,7 @@ impl Frame { gas_meter.charge_vec_swap(make_ty!(ty))?; vec_ref.swap(idx1, idx2, ty)?; } + Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) | Bytecode::CallFunctionPointer => unimplemented!(), } if interpreter.paranoid_type_checks { Self::post_execution_type_stack_transition( diff --git a/language/move-vm/runtime/src/loader.rs b/language/move-vm/runtime/src/loader.rs index 3b43e2d1d7..0d045bd4c3 100644 --- a/language/move-vm/runtime/src/loader.rs +++ b/language/move-vm/runtime/src/loader.rs @@ -407,6 +407,7 @@ impl ModuleCache { let def_idx = resolver(struct_name, &module_id)?; Type::StructInstantiation(def_idx, type_parameters) } + SignatureToken::Function(_) => unimplemented!(), }; Ok(res) } diff --git a/language/move-vm/types/src/loaded_data/runtime_types.rs b/language/move-vm/types/src/loaded_data/runtime_types.rs index a8d1addd43..3b8946cad9 100644 --- a/language/move-vm/types/src/loaded_data/runtime_types.rs +++ b/language/move-vm/types/src/loaded_data/runtime_types.rs @@ -155,7 +155,7 @@ impl Type { ) } // Not allowed/Not meaningful - S::TypeParameter(_) | S::Reference(_) | S::MutableReference(_) | S::Signer => { + S::TypeParameter(_) | S::Reference(_) | S::MutableReference(_) | S::Signer | S::Function(_) => { return Err( PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) .with_message("Unable to load const type signature".to_string()), diff --git a/language/move-vm/types/src/values/values_impl.rs b/language/move-vm/types/src/values/values_impl.rs index 412c1848bd..1522492aaa 100644 --- a/language/move-vm/types/src/values/values_impl.rs +++ b/language/move-vm/types/src/values/values_impl.rs @@ -3210,7 +3210,7 @@ impl Value { // Not yet supported S::Struct(_) | S::StructInstantiation(_, _) => return None, // Not allowed/Not meaningful - S::TypeParameter(_) | S::Reference(_) | S::MutableReference(_) => return None, + S::TypeParameter(_) | S::Reference(_) | S::MutableReference(_) | S::Function(_) => return None, }) } diff --git a/language/tools/move-bytecode-utils/src/layout.rs b/language/tools/move-bytecode-utils/src/layout.rs index c054adff1e..a435f009ba 100644 --- a/language/tools/move-bytecode-utils/src/layout.rs +++ b/language/tools/move-bytecode-utils/src/layout.rs @@ -169,7 +169,7 @@ impl<'a, T: GetModule> SerdeLayoutBuilder<'a, T> { } } TypeParameter(i) => input_type_args[*i as usize].clone(), - Reference(_) | MutableReference(_) => unreachable!(), // structs cannot store references + Reference(_) | MutableReference(_) | Function { .. } => unreachable!(), // structs cannot store references }) } @@ -415,7 +415,7 @@ impl TypeLayoutBuilder { U256 => MoveTypeLayout::U256, Address => MoveTypeLayout::Address, Signer => bail!("Type layouts cannot contain signer"), - Reference(_) | MutableReference(_) => bail!("Type layouts cannot contain references"), + Reference(_) | MutableReference(_) | Function { .. } => bail!("Type layouts cannot contain references"), }) } } diff --git a/language/tools/move-disassembler/src/disassembler.rs b/language/tools/move-disassembler/src/disassembler.rs index d2d176fb5b..519b74de61 100644 --- a/language/tools/move-disassembler/src/disassembler.rs +++ b/language/tools/move-disassembler/src/disassembler.rs @@ -531,6 +531,7 @@ impl<'a> Disassembler<'a> { })? .0 .to_string(), + SignatureToken::Function(_) => unimplemented!(), }) } diff --git a/language/tools/move-resource-viewer/src/resolver.rs b/language/tools/move-resource-viewer/src/resolver.rs index 6550abbcb2..1344b2637e 100644 --- a/language/tools/move-resource-viewer/src/resolver.rs +++ b/language/tools/move-resource-viewer/src/resolver.rs @@ -176,6 +176,7 @@ impl<'a, T: MoveResolver + ?Sized> Resolver<'a, T> { SignatureToken::Signer => FatType::Reference(Box::new(FatType::Signer)), _ => return Err(anyhow!("Unexpected Reference")), }, + SignatureToken::Function(_) => return Err(anyhow!("Unexpected Function Type")), }) } From f941d9684c879420613c9a24b46d0a0f3a664a0f Mon Sep 17 00:00:00 2001 From: Runtian Zhou Date: Tue, 14 Mar 2023 15:26:46 -0700 Subject: [PATCH 04/11] implement verifier logic --- .../move-binary-format/src/binary_views.rs | 12 +++++- .../move-binary-format/src/check_bounds.rs | 5 ++- .../move-binary-format/src/deserializer.rs | 2 +- .../move-binary-format/src/file_format.rs | 4 +- .../src/file_format_common.rs | 2 +- language/move-binary-format/src/serializer.rs | 7 +++- .../src/acquires_list_verifier.rs | 20 ++++++++- .../src/instruction_consistency.rs | 10 ++++- .../src/locals_safety/mod.rs | 6 ++- .../src/reference_safety/mod.rs | 41 ++++++++++++++++++- .../move-bytecode-verifier/src/signature.rs | 18 ++++---- .../src/stack_usage_verifier.rs | 14 +++++-- .../move-bytecode-verifier/src/struct_defs.rs | 5 +-- .../move-bytecode-verifier/src/type_safety.rs | 34 +++++++++++++-- .../src/stackless_bytecode_generator.rs | 2 +- language/move-vm/runtime/src/interpreter.rs | 6 +-- 16 files changed, 149 insertions(+), 39 deletions(-) diff --git a/language/move-binary-format/src/binary_views.rs b/language/move-binary-format/src/binary_views.rs index acf05fecd1..e89536c90e 100644 --- a/language/move-binary-format/src/binary_views.rs +++ b/language/move-binary-format/src/binary_views.rs @@ -13,7 +13,7 @@ use crate::{ FunctionInstantiation, FunctionInstantiationIndex, IdentifierIndex, ModuleHandle, ModuleHandleIndex, Signature, SignatureIndex, SignatureToken, StructDefInstantiation, StructDefInstantiationIndex, StructDefinition, StructDefinitionIndex, StructHandle, - StructHandleIndex, + StructHandleIndex, FunctionType, }, CompiledModule, }; @@ -315,6 +315,16 @@ impl<'a> BinaryIndexedView<'a> { BinaryIndexedView::Script(script) => script.version(), } } + + pub fn function_type_from_handle(&self, fh_idx: FunctionHandleIndex) -> FunctionType { + let fh = self.function_handle_at(fh_idx); + let parameters = self.signature_at(fh.parameters).0.clone(); + let return_ = self.signature_at(fh.return_).0.clone(); + FunctionType { + parameters, + return_, + } + } } const EMPTY_SIGNATURE: &Signature = &Signature(vec![]); diff --git a/language/move-binary-format/src/check_bounds.rs b/language/move-binary-format/src/check_bounds.rs index 50c104f0f0..03f2e3c88c 100644 --- a/language/move-binary-format/src/check_bounds.rs +++ b/language/move-binary-format/src/check_bounds.rs @@ -513,7 +513,8 @@ impl<'a> BoundsChecker<'a> { | VecPushBack(idx) | VecPopBack(idx) | VecUnpack(idx, _) - | VecSwap(idx) => { + | VecSwap(idx) + | CallFunctionPointer(idx) => { self.check_code_unit_bounds_impl( self.view.signatures(), *idx, @@ -532,7 +533,7 @@ impl<'a> BoundsChecker<'a> { | LdU128(_) | CastU8 | CastU16 | CastU32 | CastU64 | CastU128 | CastU256 | LdTrue | LdFalse | ReadRef | WriteRef | Add | Sub | Mul | Mod | Div | BitOr | BitAnd | Xor | Shl | Shr | Or | And | Not | Eq | Neq | Lt | Gt | Le | Ge - | Abort | Nop | CallFunctionPointer => (), + | Abort | Nop => (), } } Ok(()) diff --git a/language/move-binary-format/src/deserializer.rs b/language/move-binary-format/src/deserializer.rs index 680612a57e..c281f655df 100644 --- a/language/move-binary-format/src/deserializer.rs +++ b/language/move-binary-format/src/deserializer.rs @@ -1656,7 +1656,7 @@ fn load_code(cursor: &mut VersionedCursor, code: &mut Vec) -> BinaryLo Opcodes::GET_FUNC_PTR => Bytecode::GetFunctionPointer(load_function_handle_index(cursor)?), Opcodes::GET_FUNC_PTR_GENERIC => Bytecode::GetFunctionPointerGeneric(load_function_inst_index(cursor)?), - Opcodes::CALL_FUNC_PTR => Bytecode::CallFunctionPointer, + Opcodes::CALL_FUNC_PTR => Bytecode::CallFunctionPointer(load_signature_index(cursor)?), }; code.push(bytecode); } diff --git a/language/move-binary-format/src/file_format.rs b/language/move-binary-format/src/file_format.rs index 820898da0f..780d6a9791 100644 --- a/language/move-binary-format/src/file_format.rs +++ b/language/move-binary-format/src/file_format.rs @@ -1682,7 +1682,7 @@ pub enum Bytecode { GetFunctionPointerGeneric(FunctionInstantiationIndex), - CallFunctionPointer, + CallFunctionPointer(SignatureIndex), } impl ::std::fmt::Debug for Bytecode { @@ -1765,7 +1765,7 @@ impl ::std::fmt::Debug for Bytecode { Bytecode::VecPopBack(a) => write!(f, "VecPopBack({})", a), Bytecode::VecUnpack(a, n) => write!(f, "VecUnpack({}, {})", a, n), Bytecode::VecSwap(a) => write!(f, "VecSwap({})", a), - Bytecode::CallFunctionPointer => write!(f, "CallFuncPtr"), + Bytecode::CallFunctionPointer(a) => write!(f, "CallFuncPtr({})", a), Bytecode::GetFunctionPointer(idx) => write!(f, "GetFuncPtr({:?})", idx), Bytecode::GetFunctionPointerGeneric(idx) => write!(f, "GetFuncPtrGeneric({:?})", idx), } diff --git a/language/move-binary-format/src/file_format_common.rs b/language/move-binary-format/src/file_format_common.rs index 2007bbbf9d..b553983c6c 100644 --- a/language/move-binary-format/src/file_format_common.rs +++ b/language/move-binary-format/src/file_format_common.rs @@ -636,7 +636,7 @@ pub fn instruction_key(instruction: &Bytecode) -> u8 { CastU256 => Opcodes::CAST_U256, GetFunctionPointer(_) => Opcodes::GET_FUNC_PTR, GetFunctionPointerGeneric(_) => Opcodes::GET_FUNC_PTR_GENERIC, - CallFunctionPointer => Opcodes::CALL_FUNC_PTR, + CallFunctionPointer(_) => Opcodes::CALL_FUNC_PTR, }; opcode as u8 } diff --git a/language/move-binary-format/src/serializer.rs b/language/move-binary-format/src/serializer.rs index bdc644dde8..6428092c60 100644 --- a/language/move-binary-format/src/serializer.rs +++ b/language/move-binary-format/src/serializer.rs @@ -753,7 +753,7 @@ fn serialize_instruction_inner( major_version )); } - Bytecode::CallFunctionPointer + Bytecode::CallFunctionPointer(_) | Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) if (major_version < VERSION_7) => @@ -983,7 +983,10 @@ fn serialize_instruction_inner( binary.push(Opcodes::GET_FUNC_PTR_GENERIC as u8)?; serialize_function_inst_index(binary, method_idx) } - Bytecode::CallFunctionPointer => binary.push(Opcodes::CALL_FUNC_PTR as u8), + Bytecode::CallFunctionPointer(sig_idx) => { + binary.push(Opcodes::CALL_FUNC_PTR as u8)?; + serialize_signature_index(binary, sig_idx) + } }; res?; Ok(()) diff --git a/language/move-bytecode-verifier/src/acquires_list_verifier.rs b/language/move-bytecode-verifier/src/acquires_list_verifier.rs index cefdb5c589..9d2aba77b0 100644 --- a/language/move-bytecode-verifier/src/acquires_list_verifier.rs +++ b/language/move-bytecode-verifier/src/acquires_list_verifier.rs @@ -169,8 +169,24 @@ impl<'a> AcquiresVerifier<'a> { | Bytecode::VecPushBack(_) | Bytecode::VecPopBack(_) | Bytecode::VecUnpack(..) - | Bytecode::VecSwap(_) => Ok(()), - Bytecode::CallFunctionPointer | Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) => unimplemented!(), + | Bytecode::VecSwap(_) + | Bytecode::CallFunctionPointer(_) => Ok(()), + Bytecode::GetFunctionPointer(fh_idx) => { + self.assert_no_acquire(*fh_idx, offset) + } + Bytecode::GetFunctionPointerGeneric(idx) => { + let fi = self.module.function_instantiation_at(*idx); + self.assert_no_acquire(fi.handle, offset) + } + } + } + + fn assert_no_acquire(&mut self, fh_idx: FunctionHandleIndex, offset: CodeOffset) -> PartialVMResult<()> { + let function_handle = self.module.function_handle_at(fh_idx); + if !self.function_acquired_resources(function_handle, fh_idx).is_empty() { + Err(self.error(StatusCode::MISSING_ACQUIRES_ANNOTATION, offset)) + } else { + Ok(()) } } diff --git a/language/move-bytecode-verifier/src/instruction_consistency.rs b/language/move-bytecode-verifier/src/instruction_consistency.rs index 93fbae21b9..6db8bfe43f 100644 --- a/language/move-bytecode-verifier/src/instruction_consistency.rs +++ b/language/move-bytecode-verifier/src/instruction_consistency.rs @@ -148,8 +148,14 @@ impl<'a> InstructionConsistency<'a> { | WriteRef | Add | Sub | Mul | Mod | Div | BitOr | BitAnd | Xor | Shl | Shr | Or | And | Not | Eq | Neq | Lt | Gt | Le | Ge | CopyLoc(_) | MoveLoc(_) | StLoc(_) | MutBorrowLoc(_) | ImmBorrowLoc(_) | VecLen(_) | VecImmBorrow(_) - | VecMutBorrow(_) | VecPushBack(_) | VecPopBack(_) | VecSwap(_) | Abort | Nop => (), - GetFunctionPointer(_) | GetFunctionPointerGeneric(_) | CallFunctionPointer => unimplemented!(), + | VecMutBorrow(_) | VecPushBack(_) | VecPopBack(_) | VecSwap(_) | Abort | Nop | CallFunctionPointer(_) => (), + GetFunctionPointer(idx) => { + self.check_function_op(offset, *idx, /* generic */ false)?; + } + GetFunctionPointerGeneric(idx) => { + let func_inst = self.resolver.function_instantiation_at(*idx); + self.check_function_op(offset, func_inst.handle, /* generic */ true)?; + } } } Ok(()) diff --git a/language/move-bytecode-verifier/src/locals_safety/mod.rs b/language/move-bytecode-verifier/src/locals_safety/mod.rs index f9ca92ed5d..f4b80292f6 100644 --- a/language/move-bytecode-verifier/src/locals_safety/mod.rs +++ b/language/move-bytecode-verifier/src/locals_safety/mod.rs @@ -151,8 +151,10 @@ fn execute_inner( | Bytecode::VecPushBack(_) | Bytecode::VecPopBack(_) | Bytecode::VecUnpack(..) - | Bytecode::VecSwap(_) => (), - Bytecode::CallFunctionPointer | Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) => unimplemented!(), + | Bytecode::VecSwap(_) + | Bytecode::CallFunctionPointer(_) + | Bytecode::GetFunctionPointer(_) + | Bytecode::GetFunctionPointerGeneric(_) => (), }; Ok(()) } diff --git a/language/move-bytecode-verifier/src/reference_safety/mod.rs b/language/move-bytecode-verifier/src/reference_safety/mod.rs index d9ce93b2de..993d51583a 100644 --- a/language/move-bytecode-verifier/src/reference_safety/mod.rs +++ b/language/move-bytecode-verifier/src/reference_safety/mod.rs @@ -17,7 +17,7 @@ use move_binary_format::{ errors::{PartialVMError, PartialVMResult}, file_format::{ Bytecode, CodeOffset, FunctionDefinitionIndex, FunctionHandle, IdentifierIndex, - SignatureIndex, SignatureToken, StructDefinition, StructFieldInformation, + SignatureIndex, SignatureToken, StructDefinition, StructFieldInformation, FunctionType, Signature, }, safe_assert, safe_unwrap, }; @@ -91,6 +91,32 @@ fn call( Ok(()) } +fn call_func_pointer( + verifier: &mut ReferenceSafetyAnalysis, + state: &mut AbstractState, + offset: CodeOffset, + function_ty: &FunctionType, +) -> PartialVMResult<()> { + // The first argument is a function pointer type. + verifier.stack.pop().unwrap(); + + let arguments = function_ty + .parameters + .iter() + .map(|_| verifier.stack.pop().unwrap()) + .rev() + .collect(); + + // Function Pointer cannot acqure resources; + let acquired_resources = BTreeSet::new(); + let return_ = Signature(function_ty.return_.clone()); + let values = state.call(offset, arguments, &acquired_resources, &return_)?; + for value in values { + verifier.stack.push(value) + } + Ok(()) +} + fn num_fields(struct_def: &StructDefinition) -> usize { match &struct_def.field_information { StructFieldInformation::Native => 0, @@ -403,7 +429,18 @@ fn execute_inner( let vec_ref = safe_unwrap!(verifier.stack.pop()); state.vector_op(offset, vec_ref, true)?; } - Bytecode::CallFunctionPointer | Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) => unimplemented!(), + Bytecode::CallFunctionPointer(sig_idx) => { + match &verifier.resolver.signature_at(*sig_idx).0[0] { + SignatureToken::Function(function_ty) => { + call_func_pointer(verifier, state, offset, &function_ty)?; + } + _ => return Err(PartialVMError::new(StatusCode::TYPE_MISMATCH).at_code_offset(verifier.function_view.index().unwrap_or_default(), offset)), + } + }, + Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) => { + // Push a naive function pointer type because it is not a reference. + verifier.stack.push(state.value_for(&SignatureToken::Function(Box::new(FunctionType { parameters: vec![], return_: vec![]})))); + } }; Ok(()) } diff --git a/language/move-bytecode-verifier/src/signature.rs b/language/move-bytecode-verifier/src/signature.rs index fde780411c..2b7b463f5a 100644 --- a/language/move-bytecode-verifier/src/signature.rs +++ b/language/move-bytecode-verifier/src/signature.rs @@ -137,7 +137,7 @@ impl<'a> SignatureChecker<'a> { use Bytecode::*; for (offset, instr) in code.code.iter().enumerate() { let result = match instr { - CallGeneric(idx) => { + CallGeneric(idx) | GetFunctionPointerGeneric(idx) => { let func_inst = self.resolver.function_instantiation_at(*idx); let func_handle = self.resolver.function_handle_at(func_inst.handle); let type_arguments = &self.resolver.signature_at(func_inst.type_parameters).0; @@ -209,8 +209,7 @@ impl<'a> SignatureChecker<'a> { | BitOr | BitAnd | Xor | Shl | Shr | Or | And | Not | Eq | Neq | Lt | Gt | Le | Ge | CopyLoc(_) | MoveLoc(_) | StLoc(_) | MutBorrowLoc(_) | ImmBorrowLoc(_) | MutBorrowField(_) | ImmBorrowField(_) | MutBorrowGlobal(_) - | ImmBorrowGlobal(_) | Exists(_) | MoveTo(_) | MoveFrom(_) | Abort | Nop => Ok(()), - GetFunctionPointer(_) | GetFunctionPointerGeneric(_) | CallFunctionPointer => unimplemented!(), + | ImmBorrowGlobal(_) | Exists(_) | MoveTo(_) | MoveFrom(_) | Abort | Nop | GetFunctionPointer(_) | CallFunctionPointer(_) => Ok(()), }; result.map_err(|err| { err.append_message_with_separator(' ', format!("at offset {} ", offset)) @@ -260,8 +259,8 @@ impl<'a> SignatureChecker<'a> { | SignatureToken::U128 | SignatureToken::U256 | SignatureToken::Address - | SignatureToken::Signer => {} - SignatureToken::Function(_) => unimplemented!(), + | SignatureToken::Signer + | SignatureToken::Function(_) => {} } Ok(()) } @@ -304,7 +303,10 @@ impl<'a> SignatureChecker<'a> { } Vector(ty) => self.check_signature_token(ty), StructInstantiation(_, type_arguments) => self.check_signature_tokens(type_arguments), - Function(_) => unimplemented!(), + Function(_) => { + Err(PartialVMError::new(StatusCode::INVALID_SIGNATURE_TOKEN) + .with_message("function not allowed".to_string())) + } } } @@ -366,8 +368,8 @@ impl<'a> SignatureChecker<'a> { | SignatureToken::U128 | SignatureToken::U256 | SignatureToken::Address - | SignatureToken::Signer => Ok(()), - SignatureToken::Function(_) => unimplemented!(), + | SignatureToken::Signer + | SignatureToken::Function(_) => Ok(()), } } diff --git a/language/move-bytecode-verifier/src/stack_usage_verifier.rs b/language/move-bytecode-verifier/src/stack_usage_verifier.rs index 0b8735885e..8fcaf21ae1 100644 --- a/language/move-bytecode-verifier/src/stack_usage_verifier.rs +++ b/language/move-bytecode-verifier/src/stack_usage_verifier.rs @@ -14,7 +14,7 @@ use move_binary_format::{ binary_views::{BinaryIndexedView, FunctionView}, control_flow_graph::{BlockId, ControlFlowGraph}, errors::{PartialVMError, PartialVMResult}, - file_format::{Bytecode, CodeUnit, FunctionDefinitionIndex, Signature, StructFieldInformation}, + file_format::{Bytecode, CodeUnit, FunctionDefinitionIndex, Signature, StructFieldInformation, SignatureToken, CodeOffset}, }; use move_core_types::vm_status::StatusCode; @@ -55,7 +55,7 @@ impl<'a> StackUsageVerifier<'a> { let block_start = cfg.block_start(block_id); let mut overall_push = 0; for i in block_start..=cfg.block_end(block_id) { - let (num_pops, num_pushes) = self.instruction_effect(&code[i as usize])?; + let (num_pops, num_pushes) = self.instruction_effect(&code[i as usize], i)?; if let Some(new_pushes) = u64::checked_add(overall_push, num_pushes) { overall_push = new_pushes }; @@ -112,7 +112,7 @@ impl<'a> StackUsageVerifier<'a> { /// The effect of an instruction is a tuple where the first element /// is the number of pops it does, and the second element is the number /// of pushes it does - fn instruction_effect(&self, instruction: &Bytecode) -> PartialVMResult<(u64, u64)> { + fn instruction_effect(&self, instruction: &Bytecode, offset: CodeOffset) -> PartialVMResult<(u64, u64)> { Ok(match instruction { // Instructions that pop, but don't push Bytecode::Pop @@ -262,7 +262,13 @@ impl<'a> StackUsageVerifier<'a> { }; (1, field_count as u64) } - Bytecode::CallFunctionPointer | Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) => unimplemented!(), + Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) => (0, 1), + Bytecode::CallFunctionPointer(idx) => { + match &self.resolver.signature_at(*idx).0[0] { + SignatureToken::Function(func_ty) => (func_ty.parameters.len() as u64, func_ty.return_.len() as u64), + _ => return Err(PartialVMError::new(StatusCode::TYPE_MISMATCH).at_code_offset(self.current_function(), offset)), + } + } }) } diff --git a/language/move-bytecode-verifier/src/struct_defs.rs b/language/move-bytecode-verifier/src/struct_defs.rs index a3d4f9122b..44c9801343 100644 --- a/language/move-bytecode-verifier/src/struct_defs.rs +++ b/language/move-bytecode-verifier/src/struct_defs.rs @@ -115,10 +115,10 @@ impl<'a> StructDefGraphBuilder<'a> { | T::Address | T::Signer | T::TypeParameter(_) => (), - T::Reference(_) | T::MutableReference(_) => { + T::Reference(_) | T::MutableReference(_) | T::Function(_) => { return Err( PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) - .with_message("Reference field when checking recursive structs".to_owned()), + .with_message("Reference field/Function Pointer when checking recursive structs".to_owned()), ) } T::Vector(inner) => self.add_signature_token(neighbors, cur_idx, inner)?, @@ -141,7 +141,6 @@ impl<'a> StructDefGraphBuilder<'a> { self.add_signature_token(neighbors, cur_idx, t)? } } - T::Function(_) => unimplemented!(), }) } } diff --git a/language/move-bytecode-verifier/src/type_safety.rs b/language/move-bytecode-verifier/src/type_safety.rs index 91e1d98f31..5bc866cd75 100644 --- a/language/move-bytecode-verifier/src/type_safety.rs +++ b/language/move-bytecode-verifier/src/type_safety.rs @@ -12,7 +12,7 @@ use move_binary_format::{ file_format::{ AbilitySet, Bytecode, CodeOffset, FieldHandleIndex, FunctionDefinitionIndex, FunctionHandle, LocalIndex, Signature, SignatureToken, SignatureToken as ST, - StructDefinition, StructDefinitionIndex, StructFieldInformation, StructHandleIndex, + StructDefinition, StructDefinitionIndex, StructFieldInformation, StructHandleIndex, FunctionType, }, safe_unwrap, }; @@ -834,7 +834,32 @@ fn verify_instr( } verifier.stack.push(ST::U256); } - Bytecode::CallFunctionPointer | Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) => unimplemented!(), + Bytecode::CallFunctionPointer(sig_idx) => { + match &verifier.resolver.signature_at(*sig_idx).0[0] { + SignatureToken::Function(func_ty) => { + verifier.stack.push(SignatureToken::Function(func_ty.clone())); + } + _ => return Err(PartialVMError::new(StatusCode::TYPE_MISMATCH).at_code_offset(verifier.function_view.index().unwrap_or_default(), offset)), + } + } + Bytecode::GetFunctionPointer(fh_idx) => { + let function_handle = verifier.resolver.function_handle_at(*fh_idx); + verifier.stack.push(SignatureToken::Function(Box::new(FunctionType { + parameters: verifier.resolver.signature_at(function_handle.parameters).0.clone(), + return_: verifier.resolver.signature_at(function_handle.return_).0.clone(), + }))); + } + Bytecode::GetFunctionPointerGeneric(fi_idx) => { + let function_inst = verifier.resolver.function_instantiation_at(*fi_idx); + + let function_handle = verifier.resolver.function_handle_at(function_inst.handle); + let type_actuals = verifier.resolver.signature_at(function_inst.type_parameters); + + verifier.stack.push(SignatureToken::Function(Box::new(FunctionType { + parameters: verifier.resolver.signature_at(function_handle.parameters).0.iter().map(|tok| instantiate(tok, type_actuals)).collect(), + return_: verifier.resolver.signature_at(function_handle.return_).0.iter().map(|tok| instantiate(tok, type_actuals)).collect(), + }))); + } }; Ok(()) } @@ -881,7 +906,10 @@ fn instantiate(token: &SignatureToken, subst: &Signature) -> SignatureToken { debug_assert!((*idx as usize) < subst.len()); subst.0[*idx as usize].clone() } - SignatureToken::Function(_) => unimplemented!(), + SignatureToken::Function(func_ty) => Function(Box::new(FunctionType { + parameters: func_ty.parameters.iter().map(|ty| instantiate(ty, subst)).collect(), + return_: func_ty.return_.iter().map(|ty| instantiate(ty, subst)).collect(), + })) } } diff --git a/language/move-prover/bytecode/src/stackless_bytecode_generator.rs b/language/move-prover/bytecode/src/stackless_bytecode_generator.rs index a4c2076a8d..a63a2a921d 100644 --- a/language/move-prover/bytecode/src/stackless_bytecode_generator.rs +++ b/language/move-prover/bytecode/src/stackless_bytecode_generator.rs @@ -1323,7 +1323,7 @@ impl<'a> StacklessBytecodeGenerator<'a> { None, )) } - MoveBytecode::CallFunctionPointer | MoveBytecode::GetFunctionPointer(_) | MoveBytecode::GetFunctionPointerGeneric(_) => unimplemented!(), + MoveBytecode::CallFunctionPointer(_) | MoveBytecode::GetFunctionPointer(_) | MoveBytecode::GetFunctionPointerGeneric(_) => unimplemented!(), } } diff --git a/language/move-vm/runtime/src/interpreter.rs b/language/move-vm/runtime/src/interpreter.rs index 57c646717b..78b3eb3e3f 100644 --- a/language/move-vm/runtime/src/interpreter.rs +++ b/language/move-vm/runtime/src/interpreter.rs @@ -1139,7 +1139,7 @@ impl Frame { | Bytecode::VecPopBack(_) | Bytecode::VecUnpack(_, _) | Bytecode::VecSwap(_) => (), - Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) | Bytecode::CallFunctionPointer => unimplemented!(), + Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) | Bytecode::CallFunctionPointer(_) => unimplemented!(), }; Ok(()) } @@ -1628,7 +1628,7 @@ impl Frame { .pop_ty()? .check_vec_ref(&ty, true)?; } - Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) | Bytecode::CallFunctionPointer => unimplemented!(), + Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) | Bytecode::CallFunctionPointer(_) => unimplemented!(), } Ok(()) } @@ -2225,7 +2225,7 @@ impl Frame { gas_meter.charge_vec_swap(make_ty!(ty))?; vec_ref.swap(idx1, idx2, ty)?; } - Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) | Bytecode::CallFunctionPointer => unimplemented!(), + Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) | Bytecode::CallFunctionPointer(_) => unimplemented!(), } if interpreter.paranoid_type_checks { Self::post_execution_type_stack_transition( From 39143172f965c25e89462c40cde8b65935306c31 Mon Sep 17 00:00:00 2001 From: Runtian Zhou Date: Tue, 14 Mar 2023 15:27:02 -0700 Subject: [PATCH 05/11] fmt --- .../tests/serializer_tests.rs | 18 +++-- .../move-binary-format/src/binary_views.rs | 8 +- .../move-binary-format/src/deserializer.rs | 41 +++++++--- .../move-binary-format/src/file_format.rs | 6 +- language/move-binary-format/src/normalized.rs | 7 +- .../src/proptest_types/functions.rs | 6 +- .../src/proptest_types/signature.rs | 28 ++++--- .../src/acquires_list_verifier.rs | 17 +++-- .../src/dependencies.rs | 2 +- .../src/instruction_consistency.rs | 63 ++++++++++++++-- .../src/reference_safety/mod.rs | 27 +++++-- .../move-bytecode-verifier/src/signature.rs | 75 ++++++++++++++++--- .../src/stack_usage_verifier.rs | 25 +++++-- .../move-bytecode-verifier/src/struct_defs.rs | 5 +- .../move-bytecode-verifier/src/type_safety.rs | 75 +++++++++++++++---- .../src/stackless_bytecode_generator.rs | 4 +- language/move-vm/runtime/src/interpreter.rs | 12 ++- .../types/src/loaded_data/runtime_types.rs | 6 +- .../move-vm/types/src/values/values_impl.rs | 4 +- .../tools/move-bytecode-utils/src/layout.rs | 4 +- 20 files changed, 327 insertions(+), 106 deletions(-) diff --git a/language/move-binary-format/serializer-tests/tests/serializer_tests.rs b/language/move-binary-format/serializer-tests/tests/serializer_tests.rs index e3e96544cd..1374249d39 100644 --- a/language/move-binary-format/serializer-tests/tests/serializer_tests.rs +++ b/language/move-binary-format/serializer-tests/tests/serializer_tests.rs @@ -39,15 +39,19 @@ proptest! { fn single_fp_test() { let mut module = empty_module(); - module.signatures.push(Signature(vec![ - SignatureToken::Function(Box::new(FunctionType { - parameters: vec![SignatureToken::U8], - return_: vec![SignatureToken::U128], - })) - ])); + module + .signatures + .push(Signature(vec![SignatureToken::Function(Box::new( + FunctionType { + parameters: vec![SignatureToken::U8], + return_: vec![SignatureToken::U128], + }, + ))])); let mut serialized = Vec::with_capacity(65536); - module.serialize(&mut serialized).expect("serialization should work"); + module + .serialize(&mut serialized) + .expect("serialization should work"); let deserialized_module = CompiledModule::deserialize_no_check_bounds(&serialized) .expect("deserialization should work"); diff --git a/language/move-binary-format/src/binary_views.rs b/language/move-binary-format/src/binary_views.rs index e89536c90e..039c9f67e5 100644 --- a/language/move-binary-format/src/binary_views.rs +++ b/language/move-binary-format/src/binary_views.rs @@ -10,10 +10,10 @@ use crate::{ AbilitySet, AddressIdentifierIndex, CodeUnit, CompiledScript, Constant, ConstantPoolIndex, FieldHandle, FieldHandleIndex, FieldInstantiation, FieldInstantiationIndex, FunctionDefinition, FunctionDefinitionIndex, FunctionHandle, FunctionHandleIndex, - FunctionInstantiation, FunctionInstantiationIndex, IdentifierIndex, ModuleHandle, - ModuleHandleIndex, Signature, SignatureIndex, SignatureToken, StructDefInstantiation, - StructDefInstantiationIndex, StructDefinition, StructDefinitionIndex, StructHandle, - StructHandleIndex, FunctionType, + FunctionInstantiation, FunctionInstantiationIndex, FunctionType, IdentifierIndex, + ModuleHandle, ModuleHandleIndex, Signature, SignatureIndex, SignatureToken, + StructDefInstantiation, StructDefInstantiationIndex, StructDefinition, + StructDefinitionIndex, StructHandle, StructHandleIndex, }, CompiledModule, }; diff --git a/language/move-binary-format/src/deserializer.rs b/language/move-binary-format/src/deserializer.rs index c281f655df..d246cb3d0a 100644 --- a/language/move-binary-format/src/deserializer.rs +++ b/language/move-binary-format/src/deserializer.rs @@ -990,8 +990,8 @@ fn load_signature_token(cursor: &mut VersionedCursor) -> BinaryLoaderResult, return_len: usize, - return_: Vec, - } + return_: Vec, + }, } impl TypeBuilder { @@ -1026,7 +1026,12 @@ fn load_signature_token(cursor: &mut VersionedCursor) -> BinaryLoaderResult { if parameters.len() < params_len { parameters.push(tok); - T::Function { params_len, parameters, return_len, return_ } + T::Function { + params_len, + parameters, + return_len, + return_, + } } else if return_.len() < return_len { return_.push(tok); if return_.len() == return_len { @@ -1034,8 +1039,13 @@ fn load_signature_token(cursor: &mut VersionedCursor) -> BinaryLoaderResult BinaryLoaderResult (), }; @@ -1119,7 +1129,12 @@ fn load_signature_token(cursor: &mut VersionedCursor) -> BinaryLoaderResult) -> BinaryLo }; match opcode { - Opcodes::CALL_FUNC_PTR - | Opcodes::GET_FUNC_PTR - | Opcodes::GET_FUNC_PTR_GENERIC + Opcodes::CALL_FUNC_PTR | Opcodes::GET_FUNC_PTR | Opcodes::GET_FUNC_PTR_GENERIC if (cursor.version() < VERSION_7) => { return Err( @@ -1654,8 +1667,12 @@ fn load_code(cursor: &mut VersionedCursor, code: &mut Vec) -> BinaryLo Opcodes::CAST_U32 => Bytecode::CastU32, Opcodes::CAST_U256 => Bytecode::CastU256, - Opcodes::GET_FUNC_PTR => Bytecode::GetFunctionPointer(load_function_handle_index(cursor)?), - Opcodes::GET_FUNC_PTR_GENERIC => Bytecode::GetFunctionPointerGeneric(load_function_inst_index(cursor)?), + Opcodes::GET_FUNC_PTR => { + Bytecode::GetFunctionPointer(load_function_handle_index(cursor)?) + } + Opcodes::GET_FUNC_PTR_GENERIC => { + Bytecode::GetFunctionPointerGeneric(load_function_inst_index(cursor)?) + } Opcodes::CALL_FUNC_PTR => Bytecode::CallFunctionPointer(load_signature_index(cursor)?), }; code.push(bytecode); diff --git a/language/move-binary-format/src/file_format.rs b/language/move-binary-format/src/file_format.rs index 780d6a9791..02b4e28f5e 100644 --- a/language/move-binary-format/src/file_format.rs +++ b/language/move-binary-format/src/file_format.rs @@ -649,8 +649,7 @@ impl AbilitySet { pub const PRIMITIVES: AbilitySet = Self((Ability::Copy as u8) | (Ability::Drop as u8) | (Ability::Store as u8)); /// Abilities for Function Pointer - pub const FUNCTION: AbilitySet = - Self((Ability::Copy as u8) | (Ability::Drop as u8)); + pub const FUNCTION: AbilitySet = Self((Ability::Copy as u8) | (Ability::Drop as u8)); /// Abilities for `Reference` and `MutableReference` pub const REFERENCES: AbilitySet = Self((Ability::Copy as u8) | (Ability::Drop as u8)); /// Abilities for `Signer` @@ -1676,8 +1675,7 @@ pub enum Bytecode { CastU256, /// Function Pointers /// - /// - + /// GetFunctionPointer(FunctionHandleIndex), GetFunctionPointerGeneric(FunctionInstantiationIndex), diff --git a/language/move-binary-format/src/normalized.rs b/language/move-binary-format/src/normalized.rs index 02f2f956fc..9eec39365d 100644 --- a/language/move-binary-format/src/normalized.rs +++ b/language/move-binary-format/src/normalized.rs @@ -297,9 +297,10 @@ impl Type { .get(*i as usize) .expect("Type parameter index out of bound") .clone(), - Function { arguments, return_} => { - Function { arguments: arguments.iter().map(|ty| ty.subst(type_args)).collect(), return_: return_.iter().map(|ty| ty.subst(type_args)).collect() } - } + Function { arguments, return_ } => Function { + arguments: arguments.iter().map(|ty| ty.subst(type_args)).collect(), + return_: return_.iter().map(|ty| ty.subst(type_args)).collect(), + }, } } } diff --git a/language/move-binary-format/src/proptest_types/functions.rs b/language/move-binary-format/src/proptest_types/functions.rs index b766e30e45..38121e383a 100644 --- a/language/move-binary-format/src/proptest_types/functions.rs +++ b/language/move-binary-format/src/proptest_types/functions.rs @@ -896,8 +896,10 @@ impl BytecodeGen { match token { U8 | U16 | U32 | U64 | U128 | U256 | Bool | Address | Signer | Struct(_) | TypeParameter(_) => true, - Function(ty) => - ty.return_.iter().all(BytecodeGen::check_signature_token) && ty.parameters.iter().all(BytecodeGen::check_signature_token), + Function(ty) => { + ty.return_.iter().all(BytecodeGen::check_signature_token) + && ty.parameters.iter().all(BytecodeGen::check_signature_token) + } Vector(element_token) => BytecodeGen::check_signature_token(element_token), StructInstantiation(_, type_arguments) => type_arguments .iter() diff --git a/language/move-binary-format/src/proptest_types/signature.rs b/language/move-binary-format/src/proptest_types/signature.rs index 61b4014073..2afbe086bf 100644 --- a/language/move-binary-format/src/proptest_types/signature.rs +++ b/language/move-binary-format/src/proptest_types/signature.rs @@ -3,8 +3,8 @@ // SPDX-License-Identifier: Apache-2.0 use crate::file_format::{ - Ability, AbilitySet, Signature, SignatureToken, StructHandle, StructHandleIndex, TableIndex, - TypeParameterIndex, FunctionType, + Ability, AbilitySet, FunctionType, Signature, SignatureToken, StructHandle, StructHandleIndex, + TableIndex, TypeParameterIndex, }; use proptest::{ collection::{vec, SizeRange}, @@ -160,9 +160,11 @@ impl SignatureTokenGen { } pub fn function_strategy() -> impl Strategy { - (vec(Self::owned_strategy(), 5), vec(Self::owned_strategy(), 5)).prop_map(|(params, return_)| { - SignatureTokenGen::Function(params, return_) - }) + ( + vec(Self::owned_strategy(), 5), + vec(Self::owned_strategy(), 5), + ) + .prop_map(|(params, return_)| SignatureTokenGen::Function(params, return_)) } pub fn materialize(self, struct_handles: &[StructHandle]) -> SignatureToken { @@ -177,12 +179,16 @@ impl SignatureTokenGen { U256 => SignatureToken::U256, Address => SignatureToken::Address, Signer => SignatureToken::Signer, - Function(params_gen, return_gen) => { - SignatureToken::Function(Box::new(FunctionType { - parameters: params_gen.into_iter().map(|gen| gen.materialize(struct_handles)).collect(), - return_: return_gen.into_iter().map(|gen| gen.materialize(struct_handles)).collect(), - })) - }, + Function(params_gen, return_gen) => SignatureToken::Function(Box::new(FunctionType { + parameters: params_gen + .into_iter() + .map(|gen| gen.materialize(struct_handles)) + .collect(), + return_: return_gen + .into_iter() + .map(|gen| gen.materialize(struct_handles)) + .collect(), + })), Struct(idx) => { let struct_handles_len = struct_handles.len(); if struct_handles_len == 0 { diff --git a/language/move-bytecode-verifier/src/acquires_list_verifier.rs b/language/move-bytecode-verifier/src/acquires_list_verifier.rs index 9d2aba77b0..768d09e257 100644 --- a/language/move-bytecode-verifier/src/acquires_list_verifier.rs +++ b/language/move-bytecode-verifier/src/acquires_list_verifier.rs @@ -171,19 +171,24 @@ impl<'a> AcquiresVerifier<'a> { | Bytecode::VecUnpack(..) | Bytecode::VecSwap(_) | Bytecode::CallFunctionPointer(_) => Ok(()), - Bytecode::GetFunctionPointer(fh_idx) => { - self.assert_no_acquire(*fh_idx, offset) - } + Bytecode::GetFunctionPointer(fh_idx) => self.assert_no_acquire(*fh_idx, offset), Bytecode::GetFunctionPointerGeneric(idx) => { let fi = self.module.function_instantiation_at(*idx); self.assert_no_acquire(fi.handle, offset) } } } - - fn assert_no_acquire(&mut self, fh_idx: FunctionHandleIndex, offset: CodeOffset) -> PartialVMResult<()> { + + fn assert_no_acquire( + &mut self, + fh_idx: FunctionHandleIndex, + offset: CodeOffset, + ) -> PartialVMResult<()> { let function_handle = self.module.function_handle_at(fh_idx); - if !self.function_acquired_resources(function_handle, fh_idx).is_empty() { + if !self + .function_acquired_resources(function_handle, fh_idx) + .is_empty() + { Err(self.error(StatusCode::MISSING_ACQUIRES_ANNOTATION, offset)) } else { Ok(()) diff --git a/language/move-bytecode-verifier/src/dependencies.rs b/language/move-bytecode-verifier/src/dependencies.rs index 2a42c8737e..444e1b8f37 100644 --- a/language/move-bytecode-verifier/src/dependencies.rs +++ b/language/move-bytecode-verifier/src/dependencies.rs @@ -476,7 +476,7 @@ fn compare_types( } (SignatureToken::Function(ty1), SignatureToken::Function(ty2)) => { compare_cross_module_signatures(context, &ty1.parameters, &ty2.parameters, def_module)?; - compare_cross_module_signatures(context,&ty1.return_, &ty2.return_, def_module) + compare_cross_module_signatures(context, &ty1.return_, &ty2.return_, def_module) } (SignatureToken::Bool, _) | (SignatureToken::U8, _) diff --git a/language/move-bytecode-verifier/src/instruction_consistency.rs b/language/move-bytecode-verifier/src/instruction_consistency.rs index 6db8bfe43f..fc8fcdf356 100644 --- a/language/move-bytecode-verifier/src/instruction_consistency.rs +++ b/language/move-bytecode-verifier/src/instruction_consistency.rs @@ -142,13 +142,62 @@ impl<'a> InstructionConsistency<'a> { // List out the other options explicitly so there's a compile error if a new // bytecode gets added. - FreezeRef | Pop | Ret | Branch(_) | BrTrue(_) | BrFalse(_) | LdU8(_) | LdU16(_) - | LdU32(_) | LdU64(_) | LdU128(_) | LdU256(_) | LdConst(_) | CastU8 | CastU16 - | CastU32 | CastU64 | CastU128 | CastU256 | LdTrue | LdFalse | ReadRef - | WriteRef | Add | Sub | Mul | Mod | Div | BitOr | BitAnd | Xor | Shl | Shr - | Or | And | Not | Eq | Neq | Lt | Gt | Le | Ge | CopyLoc(_) | MoveLoc(_) - | StLoc(_) | MutBorrowLoc(_) | ImmBorrowLoc(_) | VecLen(_) | VecImmBorrow(_) - | VecMutBorrow(_) | VecPushBack(_) | VecPopBack(_) | VecSwap(_) | Abort | Nop | CallFunctionPointer(_) => (), + FreezeRef + | Pop + | Ret + | Branch(_) + | BrTrue(_) + | BrFalse(_) + | LdU8(_) + | LdU16(_) + | LdU32(_) + | LdU64(_) + | LdU128(_) + | LdU256(_) + | LdConst(_) + | CastU8 + | CastU16 + | CastU32 + | CastU64 + | CastU128 + | CastU256 + | LdTrue + | LdFalse + | ReadRef + | WriteRef + | Add + | Sub + | Mul + | Mod + | Div + | BitOr + | BitAnd + | Xor + | Shl + | Shr + | Or + | And + | Not + | Eq + | Neq + | Lt + | Gt + | Le + | Ge + | CopyLoc(_) + | MoveLoc(_) + | StLoc(_) + | MutBorrowLoc(_) + | ImmBorrowLoc(_) + | VecLen(_) + | VecImmBorrow(_) + | VecMutBorrow(_) + | VecPushBack(_) + | VecPopBack(_) + | VecSwap(_) + | Abort + | Nop + | CallFunctionPointer(_) => (), GetFunctionPointer(idx) => { self.check_function_op(offset, *idx, /* generic */ false)?; } diff --git a/language/move-bytecode-verifier/src/reference_safety/mod.rs b/language/move-bytecode-verifier/src/reference_safety/mod.rs index 993d51583a..33d0734080 100644 --- a/language/move-bytecode-verifier/src/reference_safety/mod.rs +++ b/language/move-bytecode-verifier/src/reference_safety/mod.rs @@ -16,8 +16,9 @@ use move_binary_format::{ binary_views::{BinaryIndexedView, FunctionView}, errors::{PartialVMError, PartialVMResult}, file_format::{ - Bytecode, CodeOffset, FunctionDefinitionIndex, FunctionHandle, IdentifierIndex, - SignatureIndex, SignatureToken, StructDefinition, StructFieldInformation, FunctionType, Signature, + Bytecode, CodeOffset, FunctionDefinitionIndex, FunctionHandle, FunctionType, + IdentifierIndex, Signature, SignatureIndex, SignatureToken, StructDefinition, + StructFieldInformation, }, safe_assert, safe_unwrap, }; @@ -108,7 +109,7 @@ fn call_func_pointer( .collect(); // Function Pointer cannot acqure resources; - let acquired_resources = BTreeSet::new(); + let acquired_resources = BTreeSet::new(); let return_ = Signature(function_ty.return_.clone()); let values = state.call(offset, arguments, &acquired_resources, &return_)?; for value in values { @@ -434,12 +435,26 @@ fn execute_inner( SignatureToken::Function(function_ty) => { call_func_pointer(verifier, state, offset, &function_ty)?; } - _ => return Err(PartialVMError::new(StatusCode::TYPE_MISMATCH).at_code_offset(verifier.function_view.index().unwrap_or_default(), offset)), + _ => { + return Err( + PartialVMError::new(StatusCode::TYPE_MISMATCH).at_code_offset( + verifier.function_view.index().unwrap_or_default(), + offset, + ), + ) + } } - }, + } Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) => { // Push a naive function pointer type because it is not a reference. - verifier.stack.push(state.value_for(&SignatureToken::Function(Box::new(FunctionType { parameters: vec![], return_: vec![]})))); + verifier + .stack + .push( + state.value_for(&SignatureToken::Function(Box::new(FunctionType { + parameters: vec![], + return_: vec![], + }))), + ); } }; Ok(()) diff --git a/language/move-bytecode-verifier/src/signature.rs b/language/move-bytecode-verifier/src/signature.rs index 2b7b463f5a..23f1ef125c 100644 --- a/language/move-bytecode-verifier/src/signature.rs +++ b/language/move-bytecode-verifier/src/signature.rs @@ -202,14 +202,67 @@ impl<'a> SignatureChecker<'a> { // List out the other options explicitly so there's a compile error if a new // bytecode gets added. - Pop | Ret | Branch(_) | BrTrue(_) | BrFalse(_) | LdU8(_) | LdU16(_) | LdU32(_) - | LdU64(_) | LdU128(_) | LdU256(_) | LdConst(_) | CastU8 | CastU16 | CastU32 - | CastU64 | CastU128 | CastU256 | LdTrue | LdFalse | Call(_) | Pack(_) - | Unpack(_) | ReadRef | WriteRef | FreezeRef | Add | Sub | Mul | Mod | Div - | BitOr | BitAnd | Xor | Shl | Shr | Or | And | Not | Eq | Neq | Lt | Gt | Le - | Ge | CopyLoc(_) | MoveLoc(_) | StLoc(_) | MutBorrowLoc(_) | ImmBorrowLoc(_) - | MutBorrowField(_) | ImmBorrowField(_) | MutBorrowGlobal(_) - | ImmBorrowGlobal(_) | Exists(_) | MoveTo(_) | MoveFrom(_) | Abort | Nop | GetFunctionPointer(_) | CallFunctionPointer(_) => Ok(()), + Pop + | Ret + | Branch(_) + | BrTrue(_) + | BrFalse(_) + | LdU8(_) + | LdU16(_) + | LdU32(_) + | LdU64(_) + | LdU128(_) + | LdU256(_) + | LdConst(_) + | CastU8 + | CastU16 + | CastU32 + | CastU64 + | CastU128 + | CastU256 + | LdTrue + | LdFalse + | Call(_) + | Pack(_) + | Unpack(_) + | ReadRef + | WriteRef + | FreezeRef + | Add + | Sub + | Mul + | Mod + | Div + | BitOr + | BitAnd + | Xor + | Shl + | Shr + | Or + | And + | Not + | Eq + | Neq + | Lt + | Gt + | Le + | Ge + | CopyLoc(_) + | MoveLoc(_) + | StLoc(_) + | MutBorrowLoc(_) + | ImmBorrowLoc(_) + | MutBorrowField(_) + | ImmBorrowField(_) + | MutBorrowGlobal(_) + | ImmBorrowGlobal(_) + | Exists(_) + | MoveTo(_) + | MoveFrom(_) + | Abort + | Nop + | GetFunctionPointer(_) + | CallFunctionPointer(_) => Ok(()), }; result.map_err(|err| { err.append_message_with_separator(' ', format!("at offset {} ", offset)) @@ -303,10 +356,8 @@ impl<'a> SignatureChecker<'a> { } Vector(ty) => self.check_signature_token(ty), StructInstantiation(_, type_arguments) => self.check_signature_tokens(type_arguments), - Function(_) => { - Err(PartialVMError::new(StatusCode::INVALID_SIGNATURE_TOKEN) - .with_message("function not allowed".to_string())) - } + Function(_) => Err(PartialVMError::new(StatusCode::INVALID_SIGNATURE_TOKEN) + .with_message("function not allowed".to_string())), } } diff --git a/language/move-bytecode-verifier/src/stack_usage_verifier.rs b/language/move-bytecode-verifier/src/stack_usage_verifier.rs index 8fcaf21ae1..e96deded3a 100644 --- a/language/move-bytecode-verifier/src/stack_usage_verifier.rs +++ b/language/move-bytecode-verifier/src/stack_usage_verifier.rs @@ -14,7 +14,10 @@ use move_binary_format::{ binary_views::{BinaryIndexedView, FunctionView}, control_flow_graph::{BlockId, ControlFlowGraph}, errors::{PartialVMError, PartialVMResult}, - file_format::{Bytecode, CodeUnit, FunctionDefinitionIndex, Signature, StructFieldInformation, SignatureToken, CodeOffset}, + file_format::{ + Bytecode, CodeOffset, CodeUnit, FunctionDefinitionIndex, Signature, SignatureToken, + StructFieldInformation, + }, }; use move_core_types::vm_status::StatusCode; @@ -112,7 +115,11 @@ impl<'a> StackUsageVerifier<'a> { /// The effect of an instruction is a tuple where the first element /// is the number of pops it does, and the second element is the number /// of pushes it does - fn instruction_effect(&self, instruction: &Bytecode, offset: CodeOffset) -> PartialVMResult<(u64, u64)> { + fn instruction_effect( + &self, + instruction: &Bytecode, + offset: CodeOffset, + ) -> PartialVMResult<(u64, u64)> { Ok(match instruction { // Instructions that pop, but don't push Bytecode::Pop @@ -263,12 +270,16 @@ impl<'a> StackUsageVerifier<'a> { (1, field_count as u64) } Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) => (0, 1), - Bytecode::CallFunctionPointer(idx) => { - match &self.resolver.signature_at(*idx).0[0] { - SignatureToken::Function(func_ty) => (func_ty.parameters.len() as u64, func_ty.return_.len() as u64), - _ => return Err(PartialVMError::new(StatusCode::TYPE_MISMATCH).at_code_offset(self.current_function(), offset)), + Bytecode::CallFunctionPointer(idx) => match &self.resolver.signature_at(*idx).0[0] { + SignatureToken::Function(func_ty) => ( + func_ty.parameters.len() as u64, + func_ty.return_.len() as u64, + ), + _ => { + return Err(PartialVMError::new(StatusCode::TYPE_MISMATCH) + .at_code_offset(self.current_function(), offset)) } - } + }, }) } diff --git a/language/move-bytecode-verifier/src/struct_defs.rs b/language/move-bytecode-verifier/src/struct_defs.rs index 44c9801343..8f484d27ac 100644 --- a/language/move-bytecode-verifier/src/struct_defs.rs +++ b/language/move-bytecode-verifier/src/struct_defs.rs @@ -118,7 +118,10 @@ impl<'a> StructDefGraphBuilder<'a> { T::Reference(_) | T::MutableReference(_) | T::Function(_) => { return Err( PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) - .with_message("Reference field/Function Pointer when checking recursive structs".to_owned()), + .with_message( + "Reference field/Function Pointer when checking recursive structs" + .to_owned(), + ), ) } T::Vector(inner) => self.add_signature_token(neighbors, cur_idx, inner)?, diff --git a/language/move-bytecode-verifier/src/type_safety.rs b/language/move-bytecode-verifier/src/type_safety.rs index 5bc866cd75..bd122e6094 100644 --- a/language/move-bytecode-verifier/src/type_safety.rs +++ b/language/move-bytecode-verifier/src/type_safety.rs @@ -11,8 +11,8 @@ use move_binary_format::{ errors::{PartialVMError, PartialVMResult}, file_format::{ AbilitySet, Bytecode, CodeOffset, FieldHandleIndex, FunctionDefinitionIndex, - FunctionHandle, LocalIndex, Signature, SignatureToken, SignatureToken as ST, - StructDefinition, StructDefinitionIndex, StructFieldInformation, StructHandleIndex, FunctionType, + FunctionHandle, FunctionType, LocalIndex, Signature, SignatureToken, SignatureToken as ST, + StructDefinition, StructDefinitionIndex, StructFieldInformation, StructHandleIndex, }, safe_unwrap, }; @@ -837,28 +837,63 @@ fn verify_instr( Bytecode::CallFunctionPointer(sig_idx) => { match &verifier.resolver.signature_at(*sig_idx).0[0] { SignatureToken::Function(func_ty) => { - verifier.stack.push(SignatureToken::Function(func_ty.clone())); + verifier + .stack + .push(SignatureToken::Function(func_ty.clone())); + } + _ => { + return Err( + PartialVMError::new(StatusCode::TYPE_MISMATCH).at_code_offset( + verifier.function_view.index().unwrap_or_default(), + offset, + ), + ) } - _ => return Err(PartialVMError::new(StatusCode::TYPE_MISMATCH).at_code_offset(verifier.function_view.index().unwrap_or_default(), offset)), } } Bytecode::GetFunctionPointer(fh_idx) => { let function_handle = verifier.resolver.function_handle_at(*fh_idx); - verifier.stack.push(SignatureToken::Function(Box::new(FunctionType { - parameters: verifier.resolver.signature_at(function_handle.parameters).0.clone(), - return_: verifier.resolver.signature_at(function_handle.return_).0.clone(), - }))); + verifier + .stack + .push(SignatureToken::Function(Box::new(FunctionType { + parameters: verifier + .resolver + .signature_at(function_handle.parameters) + .0 + .clone(), + return_: verifier + .resolver + .signature_at(function_handle.return_) + .0 + .clone(), + }))); } Bytecode::GetFunctionPointerGeneric(fi_idx) => { let function_inst = verifier.resolver.function_instantiation_at(*fi_idx); let function_handle = verifier.resolver.function_handle_at(function_inst.handle); - let type_actuals = verifier.resolver.signature_at(function_inst.type_parameters); + let type_actuals = verifier + .resolver + .signature_at(function_inst.type_parameters); - verifier.stack.push(SignatureToken::Function(Box::new(FunctionType { - parameters: verifier.resolver.signature_at(function_handle.parameters).0.iter().map(|tok| instantiate(tok, type_actuals)).collect(), - return_: verifier.resolver.signature_at(function_handle.return_).0.iter().map(|tok| instantiate(tok, type_actuals)).collect(), - }))); + verifier + .stack + .push(SignatureToken::Function(Box::new(FunctionType { + parameters: verifier + .resolver + .signature_at(function_handle.parameters) + .0 + .iter() + .map(|tok| instantiate(tok, type_actuals)) + .collect(), + return_: verifier + .resolver + .signature_at(function_handle.return_) + .0 + .iter() + .map(|tok| instantiate(tok, type_actuals)) + .collect(), + }))); } }; Ok(()) @@ -907,9 +942,17 @@ fn instantiate(token: &SignatureToken, subst: &Signature) -> SignatureToken { subst.0[*idx as usize].clone() } SignatureToken::Function(func_ty) => Function(Box::new(FunctionType { - parameters: func_ty.parameters.iter().map(|ty| instantiate(ty, subst)).collect(), - return_: func_ty.return_.iter().map(|ty| instantiate(ty, subst)).collect(), - })) + parameters: func_ty + .parameters + .iter() + .map(|ty| instantiate(ty, subst)) + .collect(), + return_: func_ty + .return_ + .iter() + .map(|ty| instantiate(ty, subst)) + .collect(), + })), } } diff --git a/language/move-prover/bytecode/src/stackless_bytecode_generator.rs b/language/move-prover/bytecode/src/stackless_bytecode_generator.rs index a63a2a921d..1a682f255a 100644 --- a/language/move-prover/bytecode/src/stackless_bytecode_generator.rs +++ b/language/move-prover/bytecode/src/stackless_bytecode_generator.rs @@ -1323,7 +1323,9 @@ impl<'a> StacklessBytecodeGenerator<'a> { None, )) } - MoveBytecode::CallFunctionPointer(_) | MoveBytecode::GetFunctionPointer(_) | MoveBytecode::GetFunctionPointerGeneric(_) => unimplemented!(), + MoveBytecode::CallFunctionPointer(_) + | MoveBytecode::GetFunctionPointer(_) + | MoveBytecode::GetFunctionPointerGeneric(_) => unimplemented!(), } } diff --git a/language/move-vm/runtime/src/interpreter.rs b/language/move-vm/runtime/src/interpreter.rs index 78b3eb3e3f..64ab394cfb 100644 --- a/language/move-vm/runtime/src/interpreter.rs +++ b/language/move-vm/runtime/src/interpreter.rs @@ -1139,7 +1139,9 @@ impl Frame { | Bytecode::VecPopBack(_) | Bytecode::VecUnpack(_, _) | Bytecode::VecSwap(_) => (), - Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) | Bytecode::CallFunctionPointer(_) => unimplemented!(), + Bytecode::GetFunctionPointer(_) + | Bytecode::GetFunctionPointerGeneric(_) + | Bytecode::CallFunctionPointer(_) => unimplemented!(), }; Ok(()) } @@ -1628,7 +1630,9 @@ impl Frame { .pop_ty()? .check_vec_ref(&ty, true)?; } - Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) | Bytecode::CallFunctionPointer(_) => unimplemented!(), + Bytecode::GetFunctionPointer(_) + | Bytecode::GetFunctionPointerGeneric(_) + | Bytecode::CallFunctionPointer(_) => unimplemented!(), } Ok(()) } @@ -2225,7 +2229,9 @@ impl Frame { gas_meter.charge_vec_swap(make_ty!(ty))?; vec_ref.swap(idx1, idx2, ty)?; } - Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) | Bytecode::CallFunctionPointer(_) => unimplemented!(), + Bytecode::GetFunctionPointer(_) + | Bytecode::GetFunctionPointerGeneric(_) + | Bytecode::CallFunctionPointer(_) => unimplemented!(), } if interpreter.paranoid_type_checks { Self::post_execution_type_stack_transition( diff --git a/language/move-vm/types/src/loaded_data/runtime_types.rs b/language/move-vm/types/src/loaded_data/runtime_types.rs index 3b8946cad9..2031bdccd1 100644 --- a/language/move-vm/types/src/loaded_data/runtime_types.rs +++ b/language/move-vm/types/src/loaded_data/runtime_types.rs @@ -155,7 +155,11 @@ impl Type { ) } // Not allowed/Not meaningful - S::TypeParameter(_) | S::Reference(_) | S::MutableReference(_) | S::Signer | S::Function(_) => { + S::TypeParameter(_) + | S::Reference(_) + | S::MutableReference(_) + | S::Signer + | S::Function(_) => { return Err( PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) .with_message("Unable to load const type signature".to_string()), diff --git a/language/move-vm/types/src/values/values_impl.rs b/language/move-vm/types/src/values/values_impl.rs index 1522492aaa..0b8d785c68 100644 --- a/language/move-vm/types/src/values/values_impl.rs +++ b/language/move-vm/types/src/values/values_impl.rs @@ -3210,7 +3210,9 @@ impl Value { // Not yet supported S::Struct(_) | S::StructInstantiation(_, _) => return None, // Not allowed/Not meaningful - S::TypeParameter(_) | S::Reference(_) | S::MutableReference(_) | S::Function(_) => return None, + S::TypeParameter(_) | S::Reference(_) | S::MutableReference(_) | S::Function(_) => { + return None + } }) } diff --git a/language/tools/move-bytecode-utils/src/layout.rs b/language/tools/move-bytecode-utils/src/layout.rs index a435f009ba..c063159079 100644 --- a/language/tools/move-bytecode-utils/src/layout.rs +++ b/language/tools/move-bytecode-utils/src/layout.rs @@ -415,7 +415,9 @@ impl TypeLayoutBuilder { U256 => MoveTypeLayout::U256, Address => MoveTypeLayout::Address, Signer => bail!("Type layouts cannot contain signer"), - Reference(_) | MutableReference(_) | Function { .. } => bail!("Type layouts cannot contain references"), + Reference(_) | MutableReference(_) | Function { .. } => { + bail!("Type layouts cannot contain references") + } }) } } From 5e98f3c5b3a43f725216c527e4a6f19b2ba4d9ca Mon Sep 17 00:00:00 2001 From: Runtian Zhou Date: Thu, 16 Mar 2023 00:09:01 -0700 Subject: [PATCH 06/11] implement runtime --- .../move-binary-format/src/file_format.rs | 6 + .../move-bytecode-verifier/src/type_safety.rs | 31 +++-- language/move-vm/runtime/src/interpreter.rs | 130 ++++++++++++++++-- language/move-vm/runtime/src/loader.rs | 29 +++- .../types/src/loaded_data/runtime_types.rs | 4 + .../move-vm/types/src/values/values_impl.rs | 59 ++++++-- 6 files changed, 219 insertions(+), 40 deletions(-) diff --git a/language/move-binary-format/src/file_format.rs b/language/move-binary-format/src/file_format.rs index 02b4e28f5e..5f9f323368 100644 --- a/language/move-binary-format/src/file_format.rs +++ b/language/move-binary-format/src/file_format.rs @@ -1089,6 +1089,12 @@ impl SignatureToken { matches!(self, Reference(_) | MutableReference(_)) } + /// Returns true if the `SignatureToken` is a function pointer + pub fn is_function(&self) -> bool { + use SignatureToken::*; + matches!(self, Function(_)) + } + /// Returns true if the `SignatureToken` is a mutable reference. pub fn is_mutable_reference(&self) -> bool { use SignatureToken::*; diff --git a/language/move-bytecode-verifier/src/type_safety.rs b/language/move-bytecode-verifier/src/type_safety.rs index bd122e6094..2662b4a871 100644 --- a/language/move-bytecode-verifier/src/type_safety.rs +++ b/language/move-bytecode-verifier/src/type_safety.rs @@ -150,7 +150,7 @@ fn borrow_loc( ) -> PartialVMResult<()> { let loc_signature = verifier.local_at(idx).clone(); - if loc_signature.is_reference() { + if loc_signature.is_reference() || loc_signature.is_function() { return Err(verifier.error(StatusCode::BORROWLOC_REFERENCE_ERROR, offset)); } @@ -835,20 +835,25 @@ fn verify_instr( verifier.stack.push(ST::U256); } Bytecode::CallFunctionPointer(sig_idx) => { - match &verifier.resolver.signature_at(*sig_idx).0[0] { - SignatureToken::Function(func_ty) => { - verifier - .stack - .push(SignatureToken::Function(func_ty.clone())); + let operand_ty = safe_unwrap!(verifier.stack.pop()); + let declared_element_type = &verifier.resolver.signature_at(*sig_idx).0[0]; + + if &operand_ty != declared_element_type { + return Err(verifier.error(StatusCode::TYPE_MISMATCH, offset)); + } + + if let SignatureToken::Function(func_ty) = operand_ty { + for parameter in func_ty.parameters.iter().rev() { + let arg = safe_unwrap!(verifier.stack.pop()); + if &arg != parameter { + return Err(verifier.error(StatusCode::CALL_TYPE_MISMATCH_ERROR, offset)); + } } - _ => { - return Err( - PartialVMError::new(StatusCode::TYPE_MISMATCH).at_code_offset( - verifier.function_view.index().unwrap_or_default(), - offset, - ), - ) + for return_type in func_ty.return_.into_iter() { + verifier.stack.push(return_type); } + } else { + return Err(verifier.error(StatusCode::TYPE_MISMATCH, offset)); } } Bytecode::GetFunctionPointer(fh_idx) => { diff --git a/language/move-vm/runtime/src/interpreter.rs b/language/move-vm/runtime/src/interpreter.rs index 64ab394cfb..4f8407f395 100644 --- a/language/move-vm/runtime/src/interpreter.rs +++ b/language/move-vm/runtime/src/interpreter.rs @@ -218,6 +218,75 @@ impl Interpreter { self.check_friend_or_private_call(¤t_frame.function, &func)?; } + // Charge gas + let module_id = func + .module_id() + .ok_or_else(|| { + PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) + .with_message("Failed to get native function module id".to_string()) + }) + .map_err(|e| set_err_info!(current_frame, e))?; + gas_meter + .charge_call_generic( + module_id, + func.name(), + ty_args.iter().map(|ty| TypeWithLoader { ty, loader }), + self.operand_stack + .last_n(func.arg_count()) + .map_err(|e| set_err_info!(current_frame, e))?, + (func.local_count() as u64).into(), + ) + .map_err(|e| set_err_info!(current_frame, e))?; + + if func.is_native() { + self.call_native( + &resolver, data_store, gas_meter, extensions, func, ty_args, + )?; + current_frame.pc += 1; // advance past the Call instruction in the caller + continue; + } + let frame = self + .make_call_frame(loader, func, ty_args) + .map_err(|e| self.set_location(e)) + .map_err(|err| self.maybe_core_dump(err, ¤t_frame))?; + self.call_stack.push(current_frame).map_err(|frame| { + let err = PartialVMError::new(StatusCode::CALL_STACK_OVERFLOW); + let err = set_err_info!(frame, err); + self.maybe_core_dump(err, &frame) + })?; + current_frame = frame; + } + ExitCode::CallFunctionPointer => { + // TODO(Gas): We should charge gas as we do type substitution... + let (module_id, func_name, ty_args) = self + .operand_stack + .pop() + .map_err(|e| self.set_location(e))? + .as_function() + .ok_or_else(|| { + PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) + .with_message( + "CallFunctionPointer has a non function pointer value on stack" + .to_string(), + ) + }) + .map_err(|e| set_err_info!(current_frame, e))?; + + let func = resolver + .function_from_name(&module_id, &func_name) + .ok_or_else(|| { + PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) + .with_message( + "CallFunctionPointer failed to resolve function handle" + .to_string(), + ) + }) + .map_err(|e| set_err_info!(current_frame, e))?; + + if self.paranoid_type_checks { + self.check_friend_or_private_call(¤t_frame.function, &func)?; + } + // Charge gas let module_id = func .module_id() @@ -997,6 +1066,7 @@ enum ExitCode { Return, Call(FunctionHandleIndex), CallGeneric(FunctionInstantiationIndex), + CallFunctionPointer, } fn check_ability(has_ability: bool) -> PartialVMResult<()> { @@ -1045,7 +1115,7 @@ impl Frame { ) -> PartialVMResult<()> { match instruction { // Call instruction will be checked at execute_main. - Bytecode::Call(_) | Bytecode::CallGeneric(_) => (), + Bytecode::Call(_) | Bytecode::CallGeneric(_) | Bytecode::CallFunctionPointer(_) => (), Bytecode::BrFalse(_) | Bytecode::BrTrue(_) => { interpreter.operand_stack.pop_ty()?; } @@ -1138,10 +1208,9 @@ impl Frame { | Bytecode::VecPushBack(_) | Bytecode::VecPopBack(_) | Bytecode::VecUnpack(_, _) - | Bytecode::VecSwap(_) => (), - Bytecode::GetFunctionPointer(_) - | Bytecode::GetFunctionPointerGeneric(_) - | Bytecode::CallFunctionPointer(_) => unimplemented!(), + | Bytecode::VecSwap(_) + | Bytecode::GetFunctionPointer(_) + | Bytecode::GetFunctionPointerGeneric(_) => (), }; Ok(()) } @@ -1162,6 +1231,7 @@ impl Frame { | Bytecode::Ret | Bytecode::Call(_) | Bytecode::CallGeneric(_) + | Bytecode::CallFunctionPointer(_) | Bytecode::Abort => { // Invariants hold because all of the instructions above will force VM to break from the interpreter loop and thus not hit this code path. unreachable!("control flow instruction encountered during type check") @@ -1630,9 +1700,9 @@ impl Frame { .pop_ty()? .check_vec_ref(&ty, true)?; } - Bytecode::GetFunctionPointer(_) - | Bytecode::GetFunctionPointerGeneric(_) - | Bytecode::CallFunctionPointer(_) => unimplemented!(), + Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) => { + interpreter.operand_stack.push_ty(Type::Function)?; + } } Ok(()) } @@ -2229,9 +2299,47 @@ impl Frame { gas_meter.charge_vec_swap(make_ty!(ty))?; vec_ref.swap(idx1, idx2, ty)?; } - Bytecode::GetFunctionPointer(_) - | Bytecode::GetFunctionPointerGeneric(_) - | Bytecode::CallFunctionPointer(_) => unimplemented!(), + Bytecode::GetFunctionPointer(fh_idx) => { + let func = resolver.function_from_handle(*fh_idx); + let module_id = func + .module_id() + .ok_or_else(|| { + PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) + .with_message( + "Failed to get native function module id".to_string(), + ) + })? + .clone(); + + let func_name = func.identifier().clone(); + + interpreter.operand_stack.push(Value::function( + module_id, + func_name, + vec![], + ))?; + } + Bytecode::GetFunctionPointerGeneric(fi_idx) => { + let func = resolver.function_from_instantiation(*fi_idx); + let ty_args = + resolver.instantiate_generic_function(*fi_idx, self.ty_args())?; + let module_id = func + .module_id() + .ok_or_else(|| { + PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) + .with_message( + "Failed to get native function module id".to_string(), + ) + })? + .clone(); + + let func_name = func.identifier().clone(); + + interpreter + .operand_stack + .push(Value::function(module_id, func_name, ty_args))?; + } + Bytecode::CallFunctionPointer(_) => return Ok(ExitCode::CallFunctionPointer), } if interpreter.paranoid_type_checks { Self::post_execution_type_stack_transition( diff --git a/language/move-vm/runtime/src/loader.rs b/language/move-vm/runtime/src/loader.rs index 0d045bd4c3..117f347061 100644 --- a/language/move-vm/runtime/src/loader.rs +++ b/language/move-vm/runtime/src/loader.rs @@ -407,7 +407,7 @@ impl ModuleCache { let def_idx = resolver(struct_name, &module_id)?; Type::StructInstantiation(def_idx, type_parameters) } - SignatureToken::Function(_) => unimplemented!(), + SignatureToken::Function(_func_ty) => Type::Function, }; Ok(res) } @@ -1356,6 +1356,8 @@ impl Loader { | Type::U256 | Type::Address => Ok(AbilitySet::PRIMITIVES), + Type::Function => Ok(AbilitySet::FUNCTION), + // Technically unreachable but, no point in erroring if we don't have to Type::Reference(_) | Type::MutableReference(_) => Ok(AbilitySet::REFERENCES), Type::Signer => Ok(AbilitySet::SIGNER), @@ -1442,6 +1444,17 @@ impl<'a> Resolver<'a> { self.loader.function_at(idx) } + pub(crate) fn function_from_name( + &self, + module_id: &ModuleId, + name: &Identifier, + ) -> Option> { + Some( + self.loader + .function_at(self.loader.get_module(module_id).function_by_name(name)?), + ) + } + pub(crate) fn function_from_instantiation( &self, idx: FunctionInstantiationIndex, @@ -1991,6 +2004,10 @@ impl Module { self.module.clone() } + pub(crate) fn function_by_name(&self, name: &Identifier) -> Option { + self.function_map.get(name).cloned() + } + fn field_offset(&self, idx: FieldHandleIndex) -> usize { self.field_handles[idx.0 as usize].offset } @@ -2348,6 +2365,10 @@ impl Function { self.name.as_str() } + pub(crate) fn identifier(&self) -> &Identifier { + &self.name + } + pub(crate) fn code(&self) -> &[Bytecode] { &self.code } @@ -2560,7 +2581,7 @@ impl Loader { Type::StructInstantiation(gidx, ty_args) => { TypeTag::Struct(Box::new(self.struct_gidx_to_type_tag(*gidx, ty_args)?)) } - Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => { + Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) | Type::Function => { return Err( PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) .with_message(format!("no type tag for {:?}", ty)), @@ -2703,7 +2724,7 @@ impl Loader { self.struct_gidx_to_type_layout(*gidx, ty_args, count, depth)?, ) } - Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => { + Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) | Type::Function => { return Err( PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) .with_message(format!("no type layout for {:?}", ty)), @@ -2798,7 +2819,7 @@ impl Loader { Type::StructInstantiation(gidx, ty_args) => MoveTypeLayout::Struct( self.struct_gidx_to_fully_annotated_layout(*gidx, ty_args, count, depth)?, ), - Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => { + Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) | Type::Function => { return Err( PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) .with_message(format!("no type layout for {:?}", ty)), diff --git a/language/move-vm/types/src/loaded_data/runtime_types.rs b/language/move-vm/types/src/loaded_data/runtime_types.rs index 2031bdccd1..42a54c053f 100644 --- a/language/move-vm/types/src/loaded_data/runtime_types.rs +++ b/language/move-vm/types/src/loaded_data/runtime_types.rs @@ -50,6 +50,7 @@ pub enum Type { U16, U32, U256, + Function, } impl Type { @@ -88,6 +89,7 @@ impl Type { } Type::StructInstantiation(*def_idx, inst) } + Type::Function => Type::Function, }; Ok(res) } @@ -126,6 +128,8 @@ impl Type { Vector(ty) | Reference(ty) | MutableReference(ty) => { Self::LEGACY_BASE_MEMORY_SIZE + ty.size() } + // TODO: is this sizing appropriate? + Function => Self::LEGACY_BASE_MEMORY_SIZE, Struct(_) => Self::LEGACY_BASE_MEMORY_SIZE, StructInstantiation(_, tys) => tys .iter() diff --git a/language/move-vm/types/src/values/values_impl.rs b/language/move-vm/types/src/values/values_impl.rs index 0b8d785c68..f2fa268436 100644 --- a/language/move-vm/types/src/values/values_impl.rs +++ b/language/move-vm/types/src/values/values_impl.rs @@ -14,6 +14,8 @@ use move_core_types::{ account_address::AccountAddress, effects::Op, gas_algebra::AbstractMemorySize, + identifier::Identifier, + language_storage::ModuleId, u256, value::{MoveStructLayout, MoveTypeLayout}, vm_status::{sub_status::NFE_VECTOR_ERROR_BASE, StatusCode}, @@ -53,6 +55,7 @@ enum ValueImpl { ContainerRef(ContainerRef), IndexedRef(IndexedRef), + Function(ModuleId, Identifier, Vec), } /// A container is a collection of values. It is used to represent data structures like a @@ -358,6 +361,9 @@ impl ValueImpl { // When cloning a container, we need to make sure we make a deep // copy of the data instead of a shallow copy of the Rc. Container(c) => Container(c.copy_value()?), + Function(id, func_name, ty_args) => { + Function(id.clone(), func_name.clone(), ty_args.clone()) + } }) } } @@ -474,6 +480,9 @@ impl ValueImpl { (ContainerRef(l), ContainerRef(r)) => l.equals(r)?, (IndexedRef(l), IndexedRef(r)) => l.equals(r)?, + (Function(lid, lname, lty_args), Function(rid, rname, rty_args)) => { + lid == rid && lname == rname && lty_args == rty_args + } (Invalid, _) | (U8(_), _) | (U16(_), _) @@ -485,7 +494,8 @@ impl ValueImpl { | (Address(_), _) | (Container(_), _) | (ContainerRef(_), _) - | (IndexedRef(_), _) => { + | (IndexedRef(_), _) + | (Function(_, _, _), _) => { return Err(PartialVMError::new(StatusCode::INTERNAL_TYPE_ERROR) .with_message(format!("cannot compare values: {:?}, {:?}", self, other))) } @@ -951,10 +961,13 @@ impl Locals { idx, }))), - ValueImpl::ContainerRef(_) | ValueImpl::Invalid | ValueImpl::IndexedRef(_) => Err( - PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) - .with_message(format!("cannot borrow local {:?}", &v[idx])), - ), + ValueImpl::ContainerRef(_) + | ValueImpl::Invalid + | ValueImpl::IndexedRef(_) + | ValueImpl::Function(_, _, _) => Err(PartialVMError::new( + StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR, + ) + .with_message(format!("cannot borrow local {:?}", &v[idx]))), } } } @@ -1181,6 +1194,17 @@ impl Value { it.into_iter().map(|v| v.0).collect(), ))))) } + + pub fn function(module_id: ModuleId, func_name: Identifier, ty_args: Vec) -> Self { + Self(ValueImpl::Function(module_id, func_name, ty_args)) + } + + pub fn as_function(self) -> Option<(ModuleId, Identifier, Vec)> { + match self.0 { + ValueImpl::Function(id, func_name, tys) => Some((id, func_name, tys)), + _ => None, + } + } } /*************************************************************************************** @@ -1958,7 +1982,8 @@ fn check_elem_layout(ty: &Type, v: &Container) -> PartialVMResult<()> { (Type::Struct(_), Container::Vec(_)) | (Type::Signer, Container::Vec(_)) - | (Type::StructInstantiation(_, _), Container::Vec(_)) => Ok(()), + | (Type::StructInstantiation(_, _), Container::Vec(_)) + | (Type::Function, Container::Vec(_)) => Ok(()), (Type::Reference(_), _) | (Type::MutableReference(_), _) | (Type::TyParam(_), _) => Err( PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) @@ -1976,7 +2001,8 @@ fn check_elem_layout(ty: &Type, v: &Container) -> PartialVMResult<()> { | (Type::Signer, _) | (Type::Vector(_), _) | (Type::Struct(_), _) - | (Type::StructInstantiation(_, _), _) => Err(PartialVMError::new( + | (Type::StructInstantiation(_, _), _) + | (Type::Function, _) => Err(PartialVMError::new( StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR, ) .with_message(format!( @@ -2186,11 +2212,13 @@ impl Vector { .collect::>>()?, ), - Type::Signer | Type::Vector(_) | Type::Struct(_) | Type::StructInstantiation(_, _) => { - Value(ValueImpl::Container(Container::Vec(Rc::new(RefCell::new( - elements.into_iter().map(|v| v.0).collect(), - ))))) - } + Type::Signer + | Type::Vector(_) + | Type::Struct(_) + | Type::StructInstantiation(_, _) + | Type::Function => Value(ValueImpl::Container(Container::Vec(Rc::new(RefCell::new( + elements.into_iter().map(|v| v.0).collect(), + ))))), Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => { return Err( @@ -2606,6 +2634,9 @@ impl Display for ValueImpl { Self::ContainerRef(r) => write!(f, "{}", r), Self::IndexedRef(r) => write!(f, "{}", r), + Self::Function(id, fh_idx, ty_args) => { + write!(f, "func({:?}.{:?}<{:?}>)", id, fh_idx, ty_args) + } } } } @@ -2774,6 +2805,9 @@ pub mod debug { ValueImpl::ContainerRef(r) => print_container_ref(buf, r), ValueImpl::IndexedRef(r) => print_indexed_ref(buf, r), + ValueImpl::Function(id, fh_idx, ty_args) => { + debug_write!(buf, "func({}.{}<{:?}>)", id, fh_idx, ty_args) + } } } @@ -3340,6 +3374,7 @@ impl ValueImpl { ContainerRef(r) => r.visit_impl(visitor, depth), IndexedRef(r) => r.visit_impl(visitor, depth), + Function(_, _, _) => (), } } } From c206c395c28dbeafc9631a0ed7de82cbb2d9e571 Mon Sep 17 00:00:00 2001 From: Runtian Zhou Date: Fri, 17 Mar 2023 14:58:07 -0700 Subject: [PATCH 07/11] fixup! implement runtime --- language/move-bytecode-verifier/src/struct_defs.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/language/move-bytecode-verifier/src/struct_defs.rs b/language/move-bytecode-verifier/src/struct_defs.rs index 8f484d27ac..e9b68d2d73 100644 --- a/language/move-bytecode-verifier/src/struct_defs.rs +++ b/language/move-bytecode-verifier/src/struct_defs.rs @@ -115,12 +115,11 @@ impl<'a> StructDefGraphBuilder<'a> { | T::Address | T::Signer | T::TypeParameter(_) => (), - T::Reference(_) | T::MutableReference(_) | T::Function(_) => { + T::Reference(_) | T::MutableReference(_) => { return Err( PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) .with_message( - "Reference field/Function Pointer when checking recursive structs" - .to_owned(), + "Reference fieldr when checking recursive structs".to_owned(), ), ) } @@ -133,6 +132,14 @@ impl<'a> StructDefGraphBuilder<'a> { .insert(*struct_def_idx); } } + T::Function(func_ty) => { + for t in &func_ty.parameters { + self.add_signature_token(neighbors, cur_idx, t)? + } + for t in &func_ty.return_ { + self.add_signature_token(neighbors, cur_idx, t)? + } + } T::StructInstantiation(sh_idx, inners) => { if let Some(struct_def_idx) = self.handle_to_def.get(sh_idx) { neighbors From f5a78139ba198813a28a07e152c0a98cd32e6e18 Mon Sep 17 00:00:00 2001 From: Runtian Zhou Date: Mon, 20 Mar 2023 17:54:09 -0700 Subject: [PATCH 08/11] Implement IR support --- .../src/file_format_common.rs | 6 +-- .../move-ir-to-bytecode/src/compiler.rs | 48 +++++++++++++++++-- .../move-ir-to-bytecode/src/context.rs | 20 ++++++-- .../move-ir-to-bytecode/syntax/src/lexer.rs | 4 ++ .../move-ir-to-bytecode/syntax/src/syntax.rs | 39 ++++++++++++++- language/move-ir/types/src/ast.rs | 25 ++++++++++ .../move-vm/test-utils/src/gas_schedule.rs | 6 +++ .../function_pointers/simple_get_and_call.exp | 0 .../simple_get_and_call.mvir | 21 ++++++++ 9 files changed, 156 insertions(+), 13 deletions(-) create mode 100644 language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.exp create mode 100644 language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.mvir diff --git a/language/move-binary-format/src/file_format_common.rs b/language/move-binary-format/src/file_format_common.rs index b553983c6c..17f39962bb 100644 --- a/language/move-binary-format/src/file_format_common.rs +++ b/language/move-binary-format/src/file_format_common.rs @@ -220,9 +220,9 @@ pub enum Opcodes { CAST_U32 = 0x4C, CAST_U256 = 0x4D, - GET_FUNC_PTR = 0xF0, - GET_FUNC_PTR_GENERIC = 0xF1, - CALL_FUNC_PTR = 0xF2, + GET_FUNC_PTR = 0x4E, + GET_FUNC_PTR_GENERIC = 0x4F, + CALL_FUNC_PTR = 0x50, } /// Upper limit on the binary size diff --git a/language/move-ir-compiler/move-ir-to-bytecode/src/compiler.rs b/language/move-ir-compiler/move-ir-to-bytecode/src/compiler.rs index ade4aa0ee4..db2b0b71ea 100644 --- a/language/move-ir-compiler/move-ir-to-bytecode/src/compiler.rs +++ b/language/move-ir-compiler/move-ir-to-bytecode/src/compiler.rs @@ -7,10 +7,10 @@ use anyhow::{bail, format_err, Result}; use move_binary_format::{ file_format::{ Ability, AbilitySet, Bytecode, CodeOffset, CodeUnit, CompiledModule, CompiledScript, - Constant, FieldDefinition, FunctionDefinition, FunctionSignature, ModuleHandle, Signature, - SignatureToken, StructDefinition, StructDefinitionIndex, StructFieldInformation, - StructHandleIndex, StructTypeParameter, TableIndex, TypeParameterIndex, TypeSignature, - Visibility, + Constant, FieldDefinition, FunctionDefinition, FunctionSignature, FunctionType, + ModuleHandle, Signature, SignatureToken, StructDefinition, StructDefinitionIndex, + StructFieldInformation, StructHandleIndex, StructTypeParameter, TableIndex, + TypeParameterIndex, TypeSignature, Visibility, }, file_format_common::VERSION_MAX, }; @@ -664,6 +664,10 @@ fn compile_type( type_parameters, inner_type, )?)), + Type::Function(parameters, return_) => SignatureToken::Function(Box::new(FunctionType { + parameters: compile_types(context, type_parameters, parameters)?, + return_: compile_types(context, type_parameters, return_)?, + })), Type::Reference(is_mutable, inner_type) => { let inner_token = Box::new(compile_type(context, type_parameters, inner_type)?); if *is_mutable { @@ -1321,6 +1325,23 @@ fn compile_expression( compile_expression(context, function_frame, code, e)?; } } + Exp_::GetFunctionPointer { module, name, type_actuals } => { + let ty_arg_tokens = + compile_types(context, function_frame.type_parameters(), &type_actuals)?; + let tokens = Signature(ty_arg_tokens); + let type_actuals_id = context.signature_index(tokens)?; + let fh_idx = context.function_handle(module, name)?.1; + let fcall = if type_actuals.is_empty() { + Bytecode::GetFunctionPointer(fh_idx) + } else { + let fi_idx = + context.function_instantiation_index(fh_idx, type_actuals_id)?; + Bytecode::GetFunctionPointerGeneric(fi_idx) + }; + push_instr!(context.decl_location(), fcall); + // Return value of current function is pushed onto the stack. + function_frame.push()?; + } }) } @@ -1545,6 +1566,24 @@ fn compile_call( // Return value of current function is pushed onto the stack. function_frame.push()?; } + FunctionCall_::CallFunctionPointer(func_ty) => { + let (param_len, return_len) = + if let move_ir_types::ast::Type::Function(parameters, return_) = &func_ty { + (parameters.len(), return_.len()) + } else { + bail!("CallFunctionPointer expect a function type in the argument") + }; + let ty = compile_types(context, function_frame.type_parameters(), &vec![func_ty])?; + let tokens = Signature(ty); + let func_ty_id = context.signature_index(tokens)?; + push_instr!(call.loc, Bytecode::CallFunctionPointer(func_ty_id)); + for _ in 0..param_len { + function_frame.pop()?; + } + for _ in 0..return_len { + function_frame.push()?; + } + } }) } @@ -1562,6 +1601,7 @@ fn compile_constant(_context: &mut Context, ty: Type, value: MoveValue) -> Resul Type::Bool => MoveTypeLayout::Bool, Type::Vector(inner_type) => MoveTypeLayout::Vector(Box::new(type_layout(*inner_type)?)), Type::Reference(_, _) => bail!("References are not supported in constant type layouts"), + Type::Function(_, _) => bail!("Functions are not supported in constant type layouts"), Type::TypeParameter(_) => { bail!("Type parameters are not supported in constant type layouts") } diff --git a/language/move-ir-compiler/move-ir-to-bytecode/src/context.rs b/language/move-ir-compiler/move-ir-to-bytecode/src/context.rs index 97d4f97b3f..b09e3f3162 100644 --- a/language/move-ir-compiler/move-ir-to-bytecode/src/context.rs +++ b/language/move-ir-compiler/move-ir-to-bytecode/src/context.rs @@ -9,9 +9,10 @@ use move_binary_format::{ AbilitySet, AddressIdentifierIndex, CodeOffset, Constant, ConstantPoolIndex, FieldHandle, FieldHandleIndex, FieldInstantiation, FieldInstantiationIndex, FunctionDefinitionIndex, FunctionHandle, FunctionHandleIndex, FunctionInstantiation, FunctionInstantiationIndex, - FunctionSignature, IdentifierIndex, ModuleHandle, ModuleHandleIndex, Signature, - SignatureIndex, SignatureToken, StructDefInstantiation, StructDefInstantiationIndex, - StructDefinitionIndex, StructHandle, StructHandleIndex, StructTypeParameter, TableIndex, + FunctionSignature, FunctionType, IdentifierIndex, ModuleHandle, ModuleHandleIndex, + Signature, SignatureIndex, SignatureToken, StructDefInstantiation, + StructDefInstantiationIndex, StructDefinitionIndex, StructHandle, StructHandleIndex, + StructTypeParameter, TableIndex, }, CompiledModule, }; @@ -808,7 +809,18 @@ impl<'a> Context<'a> { .collect::>()?; SignatureToken::StructInstantiation(correct_sh_idx, correct_inners) } - SignatureToken::Function(_) => unimplemented!(), + SignatureToken::Function(func_ty) => SignatureToken::Function(Box::new(FunctionType { + parameters: func_ty + .parameters + .into_iter() + .map(|tok| self.reindex_signature_token(dep, tok)) + .collect::>>()?, + return_: func_ty + .return_ + .into_iter() + .map(|tok| self.reindex_signature_token(dep, tok)) + .collect::>>()?, + })), }) } diff --git a/language/move-ir-compiler/move-ir-to-bytecode/syntax/src/lexer.rs b/language/move-ir-compiler/move-ir-to-bytecode/syntax/src/lexer.rs index 8bca7244ca..33da180233 100644 --- a/language/move-ir-compiler/move-ir-to-bytecode/syntax/src/lexer.rs +++ b/language/move-ir-compiler/move-ir-to-bytecode/syntax/src/lexer.rs @@ -114,6 +114,8 @@ pub enum Tok { LSquare, RSquare, PeriodPeriod, + GetFuncPointer, + CallFunctionPointer, } impl Tok { @@ -279,6 +281,7 @@ impl<'input> Lexer<'input> { "move_from" => (Tok::MoveFrom, len + 1), "move_to" => (Tok::MoveTo, len + 1), "main" => (Tok::Main, len), + "call_function_pointer" => (Tok::CallFunctionPointer, len + 1), _ => { if let Some(stripped) = name.strip_prefix("vec_pack_") { match stripped.parse::() { @@ -299,6 +302,7 @@ impl<'input> Lexer<'input> { "assert" => (Tok::Assert, len + 1), "copy" => (Tok::Copy, len + 1), "move" => (Tok::Move, len + 1), + "get_function_pointer" => (Tok::GetFuncPointer, len + 1), _ => (get_name_token(name), len), }, _ => (get_name_token(name), len), diff --git a/language/move-ir-compiler/move-ir-to-bytecode/syntax/src/syntax.rs b/language/move-ir-compiler/move-ir-to-bytecode/syntax/src/syntax.rs index 1054ca4048..48c5cc91b9 100644 --- a/language/move-ir-compiler/move-ir-to-bytecode/syntax/src/syntax.rs +++ b/language/move-ir-compiler/move-ir-to-bytecode/syntax/src/syntax.rs @@ -476,6 +476,12 @@ fn parse_qualified_function_name( let f = parse_builtin(tokens)?; FunctionCall_::Builtin(f) } + Tok::CallFunctionPointer => { + tokens.advance()?; + let func_type = parse_type(tokens)?; + consume_token(tokens, Tok::Greater)?; + FunctionCall_::CallFunctionPointer(func_type) + } Tok::DotNameValue => { let module_dot_name = parse_dot_name(tokens)?; let type_actuals = parse_type_actuals(tokens)?; @@ -619,7 +625,8 @@ fn parse_call_or_term_(tokens: &mut Lexer) -> Result { + | Tok::ToU256 + | Tok::CallFunctionPointer => { let f = parse_qualified_function_name(tokens)?; let exp = parse_call_or_term(tokens)?; Ok(Exp_::FunctionCall(f, Box::new(exp))) @@ -673,6 +680,21 @@ fn parse_pack_( fn parse_term_(tokens: &mut Lexer) -> Result> { match tokens.peek() { + Tok::GetFuncPointer => { + tokens.advance()?; + let module_dot_name = parse_dot_name(tokens)?; + let type_actuals = parse_type_actuals(tokens)?; + let v: Vec<&str> = module_dot_name.split('.').collect(); + assert!(v.len() == 2); + consume_token(tokens, Tok::RParen)?; + + Ok(Exp_::GetFunctionPointer { + module: ModuleName(Symbol::from(v[0])), + name: FunctionName(Symbol::from(v[1])), + type_actuals, + } + ) + }, Tok::Move => { tokens.advance()?; let v = parse_var(tokens)?; @@ -1078,7 +1100,9 @@ fn parse_statement_(tokens: &mut Lexer) -> Result Ok(Statement_::Exp(Box::new(parse_call(tokens)?))), + | Tok::ToU256 + | Tok::CallFunctionPointer => Ok(Statement_::Exp(Box::new(parse_call(tokens)?))), + Tok::LParen => { tokens.advance()?; let start = tokens.start_loc(); @@ -1286,6 +1310,17 @@ fn parse_type(tokens: &mut Lexer) -> Result let tys = parse_type_actuals(tokens)?; Type::Struct(s, tys) } + Tok::Pipe => { + tokens.advance()?; + let parameters = parse_comma_list(tokens, &[Tok::Pipe], parse_type, true)?; + adjust_token(tokens, &[Tok::Pipe])?; + consume_token(tokens, Tok::Pipe)?; + consume_token(tokens, Tok::LParen)?; + let return_ = parse_comma_list(tokens, &[Tok::RParen], parse_type, true)?; + adjust_token(tokens, &[Tok::RParen])?; + consume_token(tokens, Tok::RParen)?; + Type::Function(parameters, return_) + } Tok::Amp => { tokens.advance()?; Type::Reference(false, Box::new(parse_type(tokens)?)) diff --git a/language/move-ir/types/src/ast.rs b/language/move-ir/types/src/ast.rs index 9368f6cf83..ad5def2345 100644 --- a/language/move-ir/types/src/ast.rs +++ b/language/move-ir/types/src/ast.rs @@ -201,6 +201,7 @@ pub enum Type { Reference(bool, Box), /// A type parameter TypeParameter(TypeVar_), + Function(Vec, Vec), } //************************************************************************************************** @@ -458,6 +459,7 @@ pub enum FunctionCall_ { name: FunctionName, type_actuals: Vec, }, + CallFunctionPointer(Type), } /// The type for a function call and its location pub type FunctionCall = Spanned; @@ -637,6 +639,11 @@ pub enum Exp_ { FunctionCall(FunctionCall, Box), /// (e_1, e_2, e_3, ..., e_j) ExprList(Vec), + GetFunctionPointer { + module: ModuleName, + name: FunctionName, + type_actuals: Vec, + } } /// The type for a `Exp_` and its location @@ -1480,6 +1487,20 @@ impl fmt::Display for Type { write!(f, "&{}{}", if *is_mutable { "mut " } else { "" }, t) } Type::TypeParameter(s) => write!(f, "{}", s), + Type::Function(parameters, return_) => { + let print_tys = |f: &mut fmt::Formatter<'_>, tys: &[Type]| { + if tys.is_empty() { + write!(f, "") + } else { + write!(f, "{}", intersperse(tys, ",")) + } + }; + + write!(f, "|")?; + print_tys(f, parameters)?; + write!(f, "|")?; + print_tys(f, return_) + } } } } @@ -1542,6 +1563,7 @@ impl fmt::Display for FunctionCall_ { name, format_type_actuals(type_actuals) ), + FunctionCall_::CallFunctionPointer(ty) => write!(f, "call<{}>", ty), } } } @@ -1704,6 +1726,9 @@ impl fmt::Display for Exp_ { write!(f, "({})", intersperse(exps, ", ")) } } + Exp_::GetFunctionPointer { module, name, type_actuals } => { + writeln!(f, "get_function_pointer({}.{}{}", module, name, format_type_actuals(type_actuals)) + } } } } diff --git a/language/move-vm/test-utils/src/gas_schedule.rs b/language/move-vm/test-utils/src/gas_schedule.rs index 708c2935c0..a8221edf82 100644 --- a/language/move-vm/test-utils/src/gas_schedule.rs +++ b/language/move-vm/test-utils/src/gas_schedule.rs @@ -691,6 +691,9 @@ pub fn zero_cost_instruction_table() -> Vec<(Bytecode, GasCost)> { (CastU16, GasCost::new(0, 0)), (CastU32, GasCost::new(0, 0)), (CastU256, GasCost::new(0, 0)), + (GetFunctionPointer(FunctionHandleIndex::new(0)), GasCost::new(0, 0)), + (GetFunctionPointerGeneric(FunctionInstantiationIndex::new(0)), GasCost::new(0, 0)), + (CallFunctionPointer(SignatureIndex::new(0)), GasCost::new(0, 0)), ] } @@ -824,6 +827,9 @@ pub fn bytecode_instruction_costs() -> Vec<(Bytecode, GasCost)> { (CastU16, GasCost::new(2, 1)), (CastU32, GasCost::new(2, 1)), (CastU256, GasCost::new(2, 1)), + (GetFunctionPointer(FunctionHandleIndex::new(0)), GasCost::new(582, 0)), + (GetFunctionPointerGeneric(FunctionInstantiationIndex::new(0)), GasCost::new(1132, 0)), + (CallFunctionPointer(SignatureIndex::new(0)), GasCost::new(1132, 0)), ] } diff --git a/language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.exp b/language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.mvir b/language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.mvir new file mode 100644 index 0000000000..36cdbe9f79 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.mvir @@ -0,0 +1,21 @@ +//# publish +module 0x42.M { + public sum(a: u64, b: u64): u64 { + let c: u64; + label b0: + c = copy(a) + copy(b); + return copy(c); + } +} + +//# run +import 0x42.M; + +main() { + let func: |u64, u64| (u64); + let a: u64; +label b0: + func = get_function_pointer(M.sum); + a = call_function_pointer<|u64, u64| (u64)>(move(func), 0, 1); + assert(move(a) == 1, 0); +} From b5b567ac2c5240b984b2854938f3a485d42d3b18 Mon Sep 17 00:00:00 2001 From: Runtian Zhou Date: Tue, 21 Mar 2023 01:23:54 -0700 Subject: [PATCH 09/11] fixup! Implement IR support --- language/move-binary-format/src/deserializer.rs | 3 +++ language/move-bytecode-verifier/src/signature.rs | 6 ++++-- language/move-bytecode-verifier/src/stack_usage_verifier.rs | 2 +- .../tests/function_pointers/simple_get_and_call.exp | 1 + .../tests/function_pointers/simple_get_and_call.mvir | 3 ++- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/language/move-binary-format/src/deserializer.rs b/language/move-binary-format/src/deserializer.rs index d246cb3d0a..1dcea8dee4 100644 --- a/language/move-binary-format/src/deserializer.rs +++ b/language/move-binary-format/src/deserializer.rs @@ -1855,6 +1855,9 @@ impl Opcodes { 0x4B => Ok(Opcodes::CAST_U16), 0x4C => Ok(Opcodes::CAST_U32), 0x4D => Ok(Opcodes::CAST_U256), + 0x4E => Ok(Opcodes::GET_FUNC_PTR), + 0x4F => Ok(Opcodes::GET_FUNC_PTR_GENERIC), + 0x50 => Ok(Opcodes::CALL_FUNC_PTR), _ => Err(PartialVMError::new(StatusCode::UNKNOWN_OPCODE)), } } diff --git a/language/move-bytecode-verifier/src/signature.rs b/language/move-bytecode-verifier/src/signature.rs index 23f1ef125c..5f3f75e0a6 100644 --- a/language/move-bytecode-verifier/src/signature.rs +++ b/language/move-bytecode-verifier/src/signature.rs @@ -356,8 +356,10 @@ impl<'a> SignatureChecker<'a> { } Vector(ty) => self.check_signature_token(ty), StructInstantiation(_, type_arguments) => self.check_signature_tokens(type_arguments), - Function(_) => Err(PartialVMError::new(StatusCode::INVALID_SIGNATURE_TOKEN) - .with_message("function not allowed".to_string())), + Function(func_ty) => { + func_ty.parameters.iter().map(|ty| self.check_signature_token(ty)).collect::>()?; + func_ty.return_.iter().map(|ty| self.check_signature_token(ty)).collect::>() + } } } diff --git a/language/move-bytecode-verifier/src/stack_usage_verifier.rs b/language/move-bytecode-verifier/src/stack_usage_verifier.rs index e96deded3a..2ea3120a02 100644 --- a/language/move-bytecode-verifier/src/stack_usage_verifier.rs +++ b/language/move-bytecode-verifier/src/stack_usage_verifier.rs @@ -272,7 +272,7 @@ impl<'a> StackUsageVerifier<'a> { Bytecode::GetFunctionPointer(_) | Bytecode::GetFunctionPointerGeneric(_) => (0, 1), Bytecode::CallFunctionPointer(idx) => match &self.resolver.signature_at(*idx).0[0] { SignatureToken::Function(func_ty) => ( - func_ty.parameters.len() as u64, + func_ty.parameters.len() as u64 + 1, func_ty.return_.len() as u64, ), _ => { diff --git a/language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.exp b/language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.exp index e69de29bb2..5d92c423f3 100644 --- a/language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.exp +++ b/language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.exp @@ -0,0 +1 @@ +processed 2 tasks diff --git a/language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.mvir b/language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.mvir index 36cdbe9f79..584bdda427 100644 --- a/language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.mvir +++ b/language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.mvir @@ -16,6 +16,7 @@ main() { let a: u64; label b0: func = get_function_pointer(M.sum); - a = call_function_pointer<|u64, u64| (u64)>(move(func), 0, 1); + a = call_function_pointer<|u64, u64| (u64)>(0, 1, move(func)); assert(move(a) == 1, 0); + return; } From 571e49391cdf4e21070cc1832888ee05820ef071 Mon Sep 17 00:00:00 2001 From: Runtian Zhou Date: Tue, 21 Mar 2023 15:50:01 -0700 Subject: [PATCH 10/11] Add interface example --- .../move-binary-format/src/deserializer.rs | 17 ++- .../move-bytecode-verifier/src/signature.rs | 6 +- .../move-bytecode-verifier/src/struct_defs.rs | 10 +- .../move-compiler/src/interface_generator.rs | 2 +- .../copy.exp} | 0 .../function_pointers/abilities/copy.mvir | 20 +++ .../function_pointers/abilities/drop.exp | 1 + .../function_pointers/abilities/drop.mvir | 23 ++++ .../function_pointers/abilities/store.exp | 10 ++ .../function_pointers/abilities/store.mvir | 16 +++ .../example/coin_interface/coin_interface.exp | 1 + .../coin_interface/coin_interface.mvir | 125 ++++++++++++++++++ .../example/simple_get_and_call.exp | 1 + .../{ => example}/simple_get_and_call.mvir | 0 .../type_checking/call_no_return.exp | 1 + .../type_checking/call_no_return.mvir | 18 +++ .../type_checking/call_ptr_mismatch.exp | 10 ++ .../type_checking/call_ptr_mismatch.mvir | 21 +++ .../type_checking/get_ptr_mismatch.exp | 10 ++ .../type_checking/get_ptr_mismatch.mvir | 21 +++ .../type_checking/simple_call_args.exp | 19 +++ .../type_checking/simple_call_args.mvir | 55 ++++++++ 22 files changed, 368 insertions(+), 19 deletions(-) rename language/move-vm/transactional-tests/tests/function_pointers/{simple_get_and_call.exp => abilities/copy.exp} (100%) create mode 100644 language/move-vm/transactional-tests/tests/function_pointers/abilities/copy.mvir create mode 100644 language/move-vm/transactional-tests/tests/function_pointers/abilities/drop.exp create mode 100644 language/move-vm/transactional-tests/tests/function_pointers/abilities/drop.mvir create mode 100644 language/move-vm/transactional-tests/tests/function_pointers/abilities/store.exp create mode 100644 language/move-vm/transactional-tests/tests/function_pointers/abilities/store.mvir create mode 100644 language/move-vm/transactional-tests/tests/function_pointers/example/coin_interface/coin_interface.exp create mode 100644 language/move-vm/transactional-tests/tests/function_pointers/example/coin_interface/coin_interface.mvir create mode 100644 language/move-vm/transactional-tests/tests/function_pointers/example/simple_get_and_call.exp rename language/move-vm/transactional-tests/tests/function_pointers/{ => example}/simple_get_and_call.mvir (100%) create mode 100644 language/move-vm/transactional-tests/tests/function_pointers/type_checking/call_no_return.exp create mode 100644 language/move-vm/transactional-tests/tests/function_pointers/type_checking/call_no_return.mvir create mode 100644 language/move-vm/transactional-tests/tests/function_pointers/type_checking/call_ptr_mismatch.exp create mode 100644 language/move-vm/transactional-tests/tests/function_pointers/type_checking/call_ptr_mismatch.mvir create mode 100644 language/move-vm/transactional-tests/tests/function_pointers/type_checking/get_ptr_mismatch.exp create mode 100644 language/move-vm/transactional-tests/tests/function_pointers/type_checking/get_ptr_mismatch.mvir create mode 100644 language/move-vm/transactional-tests/tests/function_pointers/type_checking/simple_call_args.exp create mode 100644 language/move-vm/transactional-tests/tests/function_pointers/type_checking/simple_call_args.mvir diff --git a/language/move-binary-format/src/deserializer.rs b/language/move-binary-format/src/deserializer.rs index 1dcea8dee4..3955ac5b66 100644 --- a/language/move-binary-format/src/deserializer.rs +++ b/language/move-binary-format/src/deserializer.rs @@ -1026,11 +1026,18 @@ fn load_signature_token(cursor: &mut VersionedCursor) -> BinaryLoaderResult { if parameters.len() < params_len { parameters.push(tok); - T::Function { - params_len, - parameters, - return_len, - return_, + if parameters.len() == params_len && return_len == 0 { + T::Saturated(SignatureToken::Function(Box::new(FunctionType { + parameters, + return_, + }))) + } else { + T::Function { + params_len, + parameters, + return_len, + return_, + } } } else if return_.len() < return_len { return_.push(tok); diff --git a/language/move-bytecode-verifier/src/signature.rs b/language/move-bytecode-verifier/src/signature.rs index 5f3f75e0a6..556fad5310 100644 --- a/language/move-bytecode-verifier/src/signature.rs +++ b/language/move-bytecode-verifier/src/signature.rs @@ -347,7 +347,7 @@ impl<'a> SignatureChecker<'a> { use SignatureToken::*; match ty { U8 | U16 | U32 | U64 | U128 | U256 | Bool | Address | Signer | Struct(_) - | TypeParameter(_) => Ok(()), + | TypeParameter(_) | Function(_) => Ok(()), Reference(_) | MutableReference(_) => { // TODO: Prop tests expect us to NOT check the inner types. // Revisit this once we rework prop tests. @@ -356,10 +356,6 @@ impl<'a> SignatureChecker<'a> { } Vector(ty) => self.check_signature_token(ty), StructInstantiation(_, type_arguments) => self.check_signature_tokens(type_arguments), - Function(func_ty) => { - func_ty.parameters.iter().map(|ty| self.check_signature_token(ty)).collect::>()?; - func_ty.return_.iter().map(|ty| self.check_signature_token(ty)).collect::>() - } } } diff --git a/language/move-bytecode-verifier/src/struct_defs.rs b/language/move-bytecode-verifier/src/struct_defs.rs index e9b68d2d73..2e49e2ec22 100644 --- a/language/move-bytecode-verifier/src/struct_defs.rs +++ b/language/move-bytecode-verifier/src/struct_defs.rs @@ -132,14 +132,8 @@ impl<'a> StructDefGraphBuilder<'a> { .insert(*struct_def_idx); } } - T::Function(func_ty) => { - for t in &func_ty.parameters { - self.add_signature_token(neighbors, cur_idx, t)? - } - for t in &func_ty.return_ { - self.add_signature_token(neighbors, cur_idx, t)? - } - } + // TODO: Is this safe? + T::Function(_func_ty) => (), T::StructInstantiation(sh_idx, inners) => { if let Some(struct_def_idx) = self.handle_to_def.get(sh_idx) { neighbors diff --git a/language/move-compiler/src/interface_generator.rs b/language/move-compiler/src/interface_generator.rs index 71692dc9f2..07ac214740 100644 --- a/language/move-compiler/src/interface_generator.rs +++ b/language/move-compiler/src/interface_generator.rs @@ -369,7 +369,7 @@ fn write_signature_token(ctx: &mut Context, t: &SignatureToken) -> String { format!("&mut {}", write_signature_token(ctx, inner)) } SignatureToken::TypeParameter(idx) => write_type_parameter(*idx), - SignatureToken::Function(_) => unimplemented!(), + SignatureToken::Function(_) => "function_ptr".to_string(), } } diff --git a/language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.exp b/language/move-vm/transactional-tests/tests/function_pointers/abilities/copy.exp similarity index 100% rename from language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.exp rename to language/move-vm/transactional-tests/tests/function_pointers/abilities/copy.exp diff --git a/language/move-vm/transactional-tests/tests/function_pointers/abilities/copy.mvir b/language/move-vm/transactional-tests/tests/function_pointers/abilities/copy.mvir new file mode 100644 index 0000000000..1e320a85a6 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/function_pointers/abilities/copy.mvir @@ -0,0 +1,20 @@ +//# publish +module 0x42.M { + public sum(a: u64, b: u64): u64 { + let c: u64; + label b0: + c = copy(a) + copy(b); + return copy(c); + } +} + +//# run +import 0x42.M; + +main() { + let func: |u64, u64| (u64); + let a: u64; +label b0: + func = get_function_pointer(M.sum); + return; +} diff --git a/language/move-vm/transactional-tests/tests/function_pointers/abilities/drop.exp b/language/move-vm/transactional-tests/tests/function_pointers/abilities/drop.exp new file mode 100644 index 0000000000..5d92c423f3 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/function_pointers/abilities/drop.exp @@ -0,0 +1 @@ +processed 2 tasks diff --git a/language/move-vm/transactional-tests/tests/function_pointers/abilities/drop.mvir b/language/move-vm/transactional-tests/tests/function_pointers/abilities/drop.mvir new file mode 100644 index 0000000000..cdf3cec330 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/function_pointers/abilities/drop.mvir @@ -0,0 +1,23 @@ +//# publish +module 0x42.M { + public sum(a: u64, b: u64): u64 { + let c: u64; + label b0: + c = copy(a) + copy(b); + return copy(c); + } +} + +//# run +import 0x42.M; + + +main() { + let func: |u64, u64| (u64); + let a: u64; +label b0: + func = get_function_pointer(M.sum); + a = call_function_pointer<|u64, u64| (u64)>(0, 1, copy(func)); + assert(move(a) == 1, 0); + return; +} \ No newline at end of file diff --git a/language/move-vm/transactional-tests/tests/function_pointers/abilities/store.exp b/language/move-vm/transactional-tests/tests/function_pointers/abilities/store.exp new file mode 100644 index 0000000000..cbd4038ee2 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/function_pointers/abilities/store.exp @@ -0,0 +1,10 @@ +processed 2 tasks + +task 1 'publish'. lines 11-16: +Error: Unable to publish module '00000000000000000000000000000042::F'. Got VMError: { + major_status: FIELD_MISSING_TYPE_ABILITY, + sub_status: None, + location: 0x42::F, + indices: [(StructDefinition, 0)], + offsets: [], +} diff --git a/language/move-vm/transactional-tests/tests/function_pointers/abilities/store.mvir b/language/move-vm/transactional-tests/tests/function_pointers/abilities/store.mvir new file mode 100644 index 0000000000..f5d149f3b0 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/function_pointers/abilities/store.mvir @@ -0,0 +1,16 @@ +//# publish +module 0x42.M { + public sum(a: u64, b: u64): u64 { + let c: u64; + label b0: + c = copy(a) + copy(b); + return copy(c); + } +} + +//# publish +module 0x42.F { + struct T has store { + f: |u64, u64| (u64), + } +} diff --git a/language/move-vm/transactional-tests/tests/function_pointers/example/coin_interface/coin_interface.exp b/language/move-vm/transactional-tests/tests/function_pointers/example/coin_interface/coin_interface.exp new file mode 100644 index 0000000000..b134af1aea --- /dev/null +++ b/language/move-vm/transactional-tests/tests/function_pointers/example/coin_interface/coin_interface.exp @@ -0,0 +1 @@ +processed 5 tasks diff --git a/language/move-vm/transactional-tests/tests/function_pointers/example/coin_interface/coin_interface.mvir b/language/move-vm/transactional-tests/tests/function_pointers/example/coin_interface/coin_interface.mvir new file mode 100644 index 0000000000..47fd5137c1 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/function_pointers/example/coin_interface/coin_interface.mvir @@ -0,0 +1,125 @@ +//# publish +module 0x42.CoinInterface { + struct T has drop { + value: |&T| (u64), + split: |&mut T, u64| (T), + merge: |&mut T, T| (), + } + + public new_interface(value: |&T| (u64), split: |&mut T, u64| (T), merge: |&mut T, T| ()): Self.T { + label b0: + return T { value: move(value), split: move(split), merge: move(merge)}; + } + + public value_func(self: &Self.T): |&T| (u64) { + label b0: + return *&move(self).T::value; + } + + public split_func(self: &Self.T): |&mut T, u64| (T) { + label b0: + return *&move(self).T::split; + } + + public merge_func(self: &Self.T): |&mut T, T| () { + label b0: + return *&move(self).T::merge; + } +} + +//# publish +module 0x42.BasicCoin1 { + import 0x42.CoinInterface; + + struct Coin has store, drop { value: u64 } + + public zero(): Self.Coin { + label b0: + return Coin { value: 0 }; + } + + public mint(value: u64): Self.Coin { + label b0: + return Coin { value: move(value) }; + } + + public value(c: &Self.Coin): u64 { + label b0: + return *&move(c).Coin::value; + } + + public merge(c: &mut Self.Coin, other: Self.Coin) { + let value: u64; + label b0: + Coin { value } = move(other); + *&mut move(c).Coin::value = (*©(c).Coin::value) + 1; + return; + } + + + public split(c: &mut Self.Coin, value: u64): Self.Coin { + let coin_value: u64; + label b0: + coin_value = *©(c).Coin::value; + assert(copy(coin_value) >= copy(value), 0); + *&mut copy(c).Coin::value = move(coin_value) - copy(value); + return Coin { value: move(value) }; + } + + public coin_interface(): CoinInterface.T> { + let value: |&Self.Coin| (u64); + let split: |&mut Self.Coin, u64| (Self.Coin); + let merge: |&mut Self.Coin, Self.Coin| (); + let interface: CoinInterface.T>; + label b0: + value = get_function_pointer(Self.value); + split = get_function_pointer(Self.split); + merge = get_function_pointer(Self.merge); + interface = CoinInterface.new_interface>(move(value), move(split), move(merge)); + return move(interface); + } +} + +//# publish +module 0x42.CoinType { + struct Foo has drop { + v: bool, + } +} + +//# publish +module 0x42.GenericAdder { + import 0x42.CoinInterface; + public add_coins(interface: &CoinInterface.T, coin1: &T, coin2: &T): u64 { + let value: |&T| (u64); + let v1: u64; + let v2: u64; + label b0: + value = CoinInterface.value_func(move(interface)); + v1 = call_function_pointer<|&T| (u64)>(move(coin1), copy(value)); + v2 = call_function_pointer<|&T| (u64)>(move(coin2), copy(value)); + return move(v1) + move(v2); + } +} + +//# run +import 0x42.CoinInterface; +import 0x42.BasicCoin1; +import 0x42.GenericAdder; +import 0x42.CoinType; + +main() { + let interface: CoinInterface.T>; + let coin1: BasicCoin1.Coin; + let coin2: BasicCoin1.Coin; + let v: u64; +label b0: + coin1 = BasicCoin1.mint(10); + coin2 = BasicCoin1.mint(20); + interface = BasicCoin1.coin_interface(); + + v = GenericAdder.add_coins>(&interface, &coin1, &coin2); + assert(move(v) == 30, 0); + return; +} + diff --git a/language/move-vm/transactional-tests/tests/function_pointers/example/simple_get_and_call.exp b/language/move-vm/transactional-tests/tests/function_pointers/example/simple_get_and_call.exp new file mode 100644 index 0000000000..5d92c423f3 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/function_pointers/example/simple_get_and_call.exp @@ -0,0 +1 @@ +processed 2 tasks diff --git a/language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.mvir b/language/move-vm/transactional-tests/tests/function_pointers/example/simple_get_and_call.mvir similarity index 100% rename from language/move-vm/transactional-tests/tests/function_pointers/simple_get_and_call.mvir rename to language/move-vm/transactional-tests/tests/function_pointers/example/simple_get_and_call.mvir diff --git a/language/move-vm/transactional-tests/tests/function_pointers/type_checking/call_no_return.exp b/language/move-vm/transactional-tests/tests/function_pointers/type_checking/call_no_return.exp new file mode 100644 index 0000000000..5d92c423f3 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/function_pointers/type_checking/call_no_return.exp @@ -0,0 +1 @@ +processed 2 tasks diff --git a/language/move-vm/transactional-tests/tests/function_pointers/type_checking/call_no_return.mvir b/language/move-vm/transactional-tests/tests/function_pointers/type_checking/call_no_return.mvir new file mode 100644 index 0000000000..044fa742b4 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/function_pointers/type_checking/call_no_return.mvir @@ -0,0 +1,18 @@ +//# publish +module 0x42.M { + public sum(a: u64, b: bool) { + label b0: + return; + } +} + +//# run +import 0x42.M; + +main() { + let func: |u64, bool| (); +label b0: + func = get_function_pointer(M.sum); + call_function_pointer<|u64, bool| ()>(0, true, move(func)); + return; +} diff --git a/language/move-vm/transactional-tests/tests/function_pointers/type_checking/call_ptr_mismatch.exp b/language/move-vm/transactional-tests/tests/function_pointers/type_checking/call_ptr_mismatch.exp new file mode 100644 index 0000000000..9247bcf5c5 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/function_pointers/type_checking/call_ptr_mismatch.exp @@ -0,0 +1,10 @@ +processed 2 tasks + +task 1 'run'. lines 9-21: +Error: Script execution failed with VMError: { + major_status: TYPE_MISMATCH, + sub_status: None, + location: script, + indices: [], + offsets: [(FunctionDefinitionIndex(0), 5)], +} diff --git a/language/move-vm/transactional-tests/tests/function_pointers/type_checking/call_ptr_mismatch.mvir b/language/move-vm/transactional-tests/tests/function_pointers/type_checking/call_ptr_mismatch.mvir new file mode 100644 index 0000000000..64fcc7dab7 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/function_pointers/type_checking/call_ptr_mismatch.mvir @@ -0,0 +1,21 @@ +//# publish +module 0x42.M { + public sum(a: u64, b: bool): bool * u64 { + label b0: + return move(b), move(a); + } +} + +//# run +import 0x42.M; + +main() { + let func: |u64, bool| (bool, u64); + let a: u64; + let b: bool; +label b0: + func = get_function_pointer(M.sum); + b = call_function_pointer<|u64, bool| (bool)>(0, true, move(func)); + + return; +} diff --git a/language/move-vm/transactional-tests/tests/function_pointers/type_checking/get_ptr_mismatch.exp b/language/move-vm/transactional-tests/tests/function_pointers/type_checking/get_ptr_mismatch.exp new file mode 100644 index 0000000000..1061d5fdc2 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/function_pointers/type_checking/get_ptr_mismatch.exp @@ -0,0 +1,10 @@ +processed 2 tasks + +task 1 'run'. lines 9-21: +Error: Script execution failed with VMError: { + major_status: STLOC_TYPE_MISMATCH_ERROR, + sub_status: None, + location: script, + indices: [], + offsets: [(FunctionDefinitionIndex(0), 1)], +} diff --git a/language/move-vm/transactional-tests/tests/function_pointers/type_checking/get_ptr_mismatch.mvir b/language/move-vm/transactional-tests/tests/function_pointers/type_checking/get_ptr_mismatch.mvir new file mode 100644 index 0000000000..09e66308dc --- /dev/null +++ b/language/move-vm/transactional-tests/tests/function_pointers/type_checking/get_ptr_mismatch.mvir @@ -0,0 +1,21 @@ +//# publish +module 0x42.M { + public sum(a: u64, b: bool): bool * u64 { + label b0: + return move(b), move(a); + } +} + +//# run +import 0x42.M; + +main() { + let func: |u64, bool| (bool); + let a: u64; + let b: bool; +label b0: + func = get_function_pointer(M.sum); + b = call_function_pointer<|u64, bool| (bool)>(0, true, move(func)); + + return; +} diff --git a/language/move-vm/transactional-tests/tests/function_pointers/type_checking/simple_call_args.exp b/language/move-vm/transactional-tests/tests/function_pointers/type_checking/simple_call_args.exp new file mode 100644 index 0000000000..cce7e08941 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/function_pointers/type_checking/simple_call_args.exp @@ -0,0 +1,19 @@ +processed 4 tasks + +task 2 'run'. lines 25-39: +Error: Script execution failed with VMError: { + major_status: STLOC_TYPE_MISMATCH_ERROR, + sub_status: None, + location: script, + indices: [], + offsets: [(FunctionDefinitionIndex(0), 6)], +} + +task 3 'run'. lines 41-55: +Error: Script execution failed with VMError: { + major_status: CALL_TYPE_MISMATCH_ERROR, + sub_status: None, + location: script, + indices: [], + offsets: [(FunctionDefinitionIndex(0), 5)], +} diff --git a/language/move-vm/transactional-tests/tests/function_pointers/type_checking/simple_call_args.mvir b/language/move-vm/transactional-tests/tests/function_pointers/type_checking/simple_call_args.mvir new file mode 100644 index 0000000000..fbfcf61c0a --- /dev/null +++ b/language/move-vm/transactional-tests/tests/function_pointers/type_checking/simple_call_args.mvir @@ -0,0 +1,55 @@ +//# publish +module 0x42.M { + public sum(a: u64, b: bool): bool * u64 { + label b0: + return move(b), move(a); + } +} + +//# run +import 0x42.M; + +main() { + let func: |u64, bool| (bool, u64); + let a: u64; + let b: bool; +label b0: + func = get_function_pointer(M.sum); + b, a = call_function_pointer<|u64, bool| (bool, u64)>(0, true, move(func)); + + assert(move(a) == 0, 0); + assert(move(b) == true, 0); + return; +} + +//# run +import 0x42.M; + +main() { + let func: |u64, bool| (bool, u64); + let a: u64; + let b: bool; +label b0: + func = get_function_pointer(M.sum); + a, b = call_function_pointer<|u64, bool| (bool, u64)>(0, true, move(func)); + + assert(move(a) == 0, 0); + assert(move(b) == true, 0); + return; +} + +//# run +import 0x42.M; + +main() { + let func: |u64, bool| (bool, u64); + let a: u64; + let b: bool; +label b0: + func = get_function_pointer(M.sum); + b, a = call_function_pointer<|u64, bool| (bool, u64)>(true, 0, move(func)); + + assert(move(a) == 0, 0); + assert(move(b) == true, 0); + return; +} \ No newline at end of file From ca06b3fadd763c1a36b8a420f8d8b27f96ce118a Mon Sep 17 00:00:00 2001 From: Runtian Zhou Date: Wed, 22 Mar 2023 15:33:42 -0700 Subject: [PATCH 11/11] fixup! Add interface example --- .../example/coin_interface/coin_interface.exp | 2 +- .../coin_interface/coin_interface.mvir | 120 +++++++++++++++++- 2 files changed, 117 insertions(+), 5 deletions(-) diff --git a/language/move-vm/transactional-tests/tests/function_pointers/example/coin_interface/coin_interface.exp b/language/move-vm/transactional-tests/tests/function_pointers/example/coin_interface/coin_interface.exp index b134af1aea..f33e2b18fc 100644 --- a/language/move-vm/transactional-tests/tests/function_pointers/example/coin_interface/coin_interface.exp +++ b/language/move-vm/transactional-tests/tests/function_pointers/example/coin_interface/coin_interface.exp @@ -1 +1 @@ -processed 5 tasks +processed 8 tasks diff --git a/language/move-vm/transactional-tests/tests/function_pointers/example/coin_interface/coin_interface.mvir b/language/move-vm/transactional-tests/tests/function_pointers/example/coin_interface/coin_interface.mvir index 47fd5137c1..41336c5ca7 100644 --- a/language/move-vm/transactional-tests/tests/function_pointers/example/coin_interface/coin_interface.mvir +++ b/language/move-vm/transactional-tests/tests/function_pointers/example/coin_interface/coin_interface.mvir @@ -25,6 +25,24 @@ module 0x42.CoinInterface { label b0: return *&move(self).T::merge; } + + public value(self: &Self.T, coin: &T): u64 { + let value: |&T| (u64); + let ret: u64; + label b0: + value = *&move(self).T::value; + ret = call_function_pointer<|&T| (u64)>(move(coin), move(value)); + return move(ret); + } + + public split(self: &Self.T, coin: &mut T, amount: u64): T { + let split: |&mut T, u64| (T); + let ret: T; + label b0: + split = *&move(self).T::split; + ret = call_function_pointer<|&mut T, u64| (T)>(move(coin), move(amount), move(split)); + return move(ret); + } } //# publish @@ -80,6 +98,59 @@ module 0x42.BasicCoin1 { } } + +//# publish +module 0x42.BasicCoin2 { + import 0x42.CoinInterface; + + struct Coin has store, drop { value: u64 } + + public zero(): Self.Coin { + label b0: + return Coin { value: 0 }; + } + + public mint(value: u64): Self.Coin { + label b0: + return Coin { value: move(value) }; + } + + public value(c: &Self.Coin): u64 { + label b0: + return *&move(c).Coin::value; + } + + public merge(c: &mut Self.Coin, other: Self.Coin) { + let value: u64; + label b0: + Coin { value } = move(other); + *&mut move(c).Coin::value = (*©(c).Coin::value) + 1; + return; + } + + + public split(c: &mut Self.Coin, value: u64): Self.Coin { + let coin_value: u64; + label b0: + coin_value = *©(c).Coin::value; + assert(copy(coin_value) >= copy(value), 0); + *&mut copy(c).Coin::value = move(coin_value) - copy(value); + return Coin { value: move(value) }; + } + + public coin_interface(): CoinInterface.T { + let value: |&Self.Coin| (u64); + let split: |&mut Self.Coin, u64| (Self.Coin); + let merge: |&mut Self.Coin, Self.Coin| (); + let interface: CoinInterface.T; + label b0: + value = get_function_pointer(Self.value); + split = get_function_pointer(Self.split); + merge = get_function_pointer(Self.merge); + interface = CoinInterface.new_interface(move(value), move(split), move(merge)); + return move(interface); + } +} //# publish module 0x42.CoinType { struct Foo has drop { @@ -91,13 +162,11 @@ module 0x42.CoinType { module 0x42.GenericAdder { import 0x42.CoinInterface; public add_coins(interface: &CoinInterface.T, coin1: &T, coin2: &T): u64 { - let value: |&T| (u64); let v1: u64; let v2: u64; label b0: - value = CoinInterface.value_func(move(interface)); - v1 = call_function_pointer<|&T| (u64)>(move(coin1), copy(value)); - v2 = call_function_pointer<|&T| (u64)>(move(coin2), copy(value)); + v1 = CoinInterface.value(copy(interface), move(coin1)); + v2 = CoinInterface.value(move(interface), move(coin2)); return move(v1) + move(v2); } } @@ -123,3 +192,46 @@ label b0: return; } +//# run +import 0x42.CoinInterface; +import 0x42.BasicCoin2; +import 0x42.GenericAdder; + +main() { + let interface: CoinInterface.T; + let coin1: BasicCoin2.Coin; + let coin2: BasicCoin2.Coin; + let v: u64; +label b0: + coin1 = BasicCoin2.mint(10); + coin2 = BasicCoin2.mint(20); + interface = BasicCoin2.coin_interface(); + + v = GenericAdder.add_coins(&interface, &coin1, &coin2); + assert(move(v) == 30, 0); + return; +} + + + +//# run +import 0x42.CoinInterface; +import 0x42.BasicCoin2; +import 0x42.GenericAdder; + +main() { + let interface: CoinInterface.T; + let coin1: BasicCoin2.Coin; + let coin2: BasicCoin2.Coin; + let v: u64; +label b0: + coin1 = BasicCoin2.mint(10); + interface = BasicCoin2.coin_interface(); + + coin2 = CoinInterface.split(&interface, &mut coin1, 5); + v = BasicCoin2.value(&coin2); + + assert(move(v) == 5, 0); + return; +} +