Skip to content
This repository has been archived by the owner on May 4, 2024. It is now read-only.

[RFC][DRAFT][VM] Function pointer support in VM #989

Draft
wants to merge 11 commits into
base: aptos-main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -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! {
Expand Down Expand Up @@ -34,3 +34,27 @@ 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);
}
19 changes: 15 additions & 4 deletions language/move-binary-format/src/binary_views.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
FunctionInstantiation, FunctionInstantiationIndex, FunctionType, IdentifierIndex,
ModuleHandle, ModuleHandleIndex, Signature, SignatureIndex, SignatureToken,
StructDefInstantiation, StructDefInstantiationIndex, StructDefinition,
StructDefinitionIndex, StructHandle, StructHandleIndex,
},
CompiledModule,
};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -314,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![]);
Expand Down
12 changes: 7 additions & 5 deletions language/move-binary-format/src/check_bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -544,7 +545,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()) {
Expand Down Expand Up @@ -612,7 +613,8 @@ impl<'a> BoundsChecker<'a> {
| Reference(_)
| MutableReference(_)
| Vector(_)
| StructInstantiation(_, _) => (),
| StructInstantiation(_, _)
| Function(_) => (),
}
}
Ok(())
Expand Down
3 changes: 2 additions & 1 deletion language/move-binary-format/src/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ fn sig_to_ty(sig: &SignatureToken) -> Option<MoveTypeLayout> {
| SignatureToken::MutableReference(_)
| SignatureToken::Struct(_)
| SignatureToken::TypeParameter(_)
| SignatureToken::StructInstantiation(_, _) => None,
| SignatureToken::StructInstantiation(_, _)
| SignatureToken::Function(_) => None,
}
}

Expand Down
91 changes: 91 additions & 0 deletions language/move-binary-format/src/deserializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,12 @@ fn load_signature_token(cursor: &mut VersionedCursor) -> BinaryLoaderResult<Sign
arity: usize,
ty_args: Vec<SignatureToken>,
},
Function {
params_len: usize,
parameters: Vec<SignatureToken>,
return_len: usize,
return_: Vec<SignatureToken>,
},
}

impl TypeBuilder {
Expand All @@ -1012,6 +1018,46 @@ fn load_signature_token(cursor: &mut VersionedCursor) -> BinaryLoaderResult<Sign
}
}
}
T::Function {
mut return_,
return_len,
mut parameters,
params_len,
} => {
if parameters.len() < params_len {
parameters.push(tok);
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);
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"),
}
}
Expand Down Expand Up @@ -1041,6 +1087,14 @@ fn load_signature_token(cursor: &mut VersionedCursor) -> BinaryLoaderResult<Sign
)),
);
}
S::FUNCTION if (cursor.version() < VERSION_7) => {
return Err(
PartialVMError::new(StatusCode::MALFORMED).with_message(format!(
"u16, u32, u256 integers not supported in bytecode version {}",
cursor.version()
)),
);
}
_ => (),
};

Expand Down Expand Up @@ -1078,6 +1132,17 @@ fn load_signature_token(cursor: &mut VersionedCursor) -> BinaryLoaderResult<Sign
let idx = load_type_parameter_index(cursor)?;
T::Saturated(SignatureToken::TypeParameter(idx))
}
S::FUNCTION => {
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)
Expand Down Expand Up @@ -1478,6 +1543,20 @@ fn load_code(cursor: &mut VersionedCursor, code: &mut Vec<Bytecode>) -> 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,
Expand Down Expand Up @@ -1594,6 +1673,14 @@ fn load_code(cursor: &mut VersionedCursor, code: &mut Vec<Bytecode>) -> 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(load_signature_index(cursor)?),
};
code.push(bytecode);
}
Expand Down Expand Up @@ -1641,6 +1728,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)),
}
}
Expand Down Expand Up @@ -1774,6 +1862,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)),
}
}
Expand Down
Loading