Skip to content
This repository was 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
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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