From ba9062c54f81356c0882f0c65232398b3d86717f Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 12 Jul 2023 15:39:43 -0600 Subject: [PATCH] Move move-native modules to files (#234) --- language/move-native/src/conv.rs | 772 ++++++++++ language/move-native/src/lib.rs | 1861 +---------------------- language/move-native/src/rt.rs | 194 +++ language/move-native/src/rt_types.rs | 284 ++++ language/move-native/src/std.rs | 510 +++++++ language/move-native/src/target_defs.rs | 110 ++ 6 files changed, 1875 insertions(+), 1856 deletions(-) create mode 100644 language/move-native/src/conv.rs create mode 100644 language/move-native/src/rt.rs create mode 100644 language/move-native/src/rt_types.rs create mode 100644 language/move-native/src/std.rs create mode 100644 language/move-native/src/target_defs.rs diff --git a/language/move-native/src/conv.rs b/language/move-native/src/conv.rs new file mode 100644 index 0000000000..8d8d18b4fe --- /dev/null +++ b/language/move-native/src/conv.rs @@ -0,0 +1,772 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +use crate::rt_types::*; +use alloc::vec::Vec; +use core::marker::PhantomData; +use core::mem; +use core::ops::{Deref, DerefMut}; +use core::ptr; +use core::slice; +use ethnum::U256; + +/// This is a placeholder for the unstable `ptr::invalid_mut`. +/// +/// It is a potential future way to create invalid pointers, which is +/// required for correctly initializing empty vectors. +/// +/// This crate initializes empty vectors knowing only the alignment of their +/// elements, but not the full type. +pub const fn invalid_mut(addr: usize) -> *mut T { + // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + // We use transmute rather than a cast so tools like Miri can tell that this + // is *not* the same as from_exposed_addr. + // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that + // pointer). + unsafe { mem::transmute(addr) } +} + +pub unsafe fn move_byte_vec_to_rust_vec(mv: MoveByteVector) -> Vec { + let ret = MoveUntypedVector { + ptr: mv.ptr, + capacity: mv.capacity, + length: mv.length, + }; + disarm_drop_bomb(mv); + move_vec_to_rust_vec(ret) +} + +pub fn rust_vec_to_move_byte_vec(rv: Vec) -> MoveByteVector { + let mv = rust_vec_to_move_vec(rv); + let r = MoveByteVector { + ptr: mv.ptr, + capacity: mv.capacity, + length: mv.length, + }; + disarm_drop_bomb(mv); + r +} + +pub fn borrow_move_byte_vec_as_rust_vec<'mv>( + mv: &'mv MoveByteVector, +) -> MoveBorrowedRustVec<'mv, u8> { + assert_eq!( + mem::size_of::(), + mem::size_of::() + ); + assert_eq!( + mem::align_of::(), + mem::align_of::() + ); + // Safety: both repr(c) with same layout, probably ok + let mv: &'mv MoveUntypedVector = unsafe { mem::transmute(mv) }; + unsafe { borrow_move_vec_as_rust_vec(mv) } +} + +pub unsafe fn move_vec_to_rust_vec(mv: MoveUntypedVector) -> Vec { + let rv = Vec::from_raw_parts( + mv.ptr as *mut T, + usize::try_from(mv.length).expect("overflow"), + usize::try_from(mv.capacity).expect("overflow"), + ); + disarm_drop_bomb(mv); + rv +} + +pub fn rust_vec_to_move_vec(mut rv: Vec) -> MoveUntypedVector { + let mv = MoveUntypedVector { + ptr: rv.as_mut_ptr() as *mut u8, + capacity: u64::try_from(rv.capacity()).expect("overflow"), + length: u64::try_from(rv.len()).expect("overflow"), + }; + mem::forget(rv); + mv +} + +pub unsafe fn borrow_move_vec_as_rust_vec<'mv, T>( + mv: &'mv MoveUntypedVector, +) -> MoveBorrowedRustVec<'mv, T> { + let rv = Vec::from_raw_parts( + mv.ptr as *mut T, + usize::try_from(mv.length).expect("overflow"), + usize::try_from(mv.capacity).expect("overflow"), + ); + MoveBorrowedRustVec { + inner: rv, + _lifetime: PhantomData, + } +} + +pub unsafe fn borrow_move_vec_as_rust_vec_mut<'mv, T>( + mv: &'mv mut MoveUntypedVector, +) -> MoveBorrowedRustVecMut<'mv, T> { + let rv = Vec::from_raw_parts( + mv.ptr as *mut T, + usize::try_from(mv.length).expect("overflow"), + usize::try_from(mv.capacity).expect("overflow"), + ); + MoveBorrowedRustVecMut { + inner: rv, + original: mv, + } +} + +pub struct MoveBorrowedRustVec<'mv, T> { + inner: Vec, + _lifetime: PhantomData<&'mv ()>, +} + +#[derive(Debug)] +pub struct MoveBorrowedRustVecMut<'mv, T> { + inner: Vec, + original: &'mv mut MoveUntypedVector, +} + +impl<'mv, T> Drop for MoveBorrowedRustVec<'mv, T> { + fn drop(&mut self) { + let rv = mem::replace(&mut self.inner, Vec::new()); + mem::forget(rv); + } +} + +impl<'mv, T> Drop for MoveBorrowedRustVecMut<'mv, T> { + fn drop(&mut self) { + let mut rv = mem::replace(&mut self.inner, Vec::new()); + + self.original.length = u64::try_from(rv.len()).expect("overflow"); + self.original.capacity = u64::try_from(rv.capacity()).expect("overflow"); + self.original.ptr = rv.as_mut_ptr() as *mut u8; + + mem::forget(rv); + } +} + +impl<'mv, T> Deref for MoveBorrowedRustVec<'mv, T> { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl<'mv, T> Deref for MoveBorrowedRustVecMut<'mv, T> { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl<'mv, T> DerefMut for MoveBorrowedRustVecMut<'mv, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +/// A vector of Move structs. +/// +/// Since we can't instantiate Move structs as Rust structs, this is a +/// container that unsafely implements exactly the ops needed to deal with +/// Move's `vector`. +#[derive(Debug)] +pub struct MoveBorrowedRustVecOfStruct<'mv> { + pub inner: &'mv MoveUntypedVector, + pub name: StaticTypeName, + pub type_: &'mv StructTypeInfo, +} + +#[derive(Debug)] +pub struct MoveBorrowedRustVecOfStructMut<'mv> { + pub inner: &'mv mut MoveUntypedVector, + pub name: StaticTypeName, + pub type_: &'mv StructTypeInfo, +} + +impl<'mv> MoveBorrowedRustVecOfStruct<'mv> { + pub unsafe fn iter<'s>(&'s self) -> impl Iterator { + let struct_size = usize::try_from(self.type_.size).expect("overflow"); + let vec_len = usize::try_from(self.inner.length).expect("overflow"); + (0..vec_len).map(move |i| { + let base_ptr = self.inner.ptr; + let offset = i.checked_mul(struct_size).expect("overflow"); + let offset = isize::try_from(offset).expect("overflow"); + let element_ptr = base_ptr.offset(offset); + let element_ref = &*(element_ptr as *const AnyValue); + element_ref + }) + } + + pub unsafe fn get(&self, i: usize) -> &'mv AnyValue { + let struct_size = usize::try_from(self.type_.size).expect("overflow"); + let vec_len = usize::try_from(self.inner.length).expect("overflow"); + + if i >= vec_len { + panic!("index out of bounds"); + } + + let base_ptr = self.inner.ptr; + let offset = i.checked_mul(struct_size).expect("overflow"); + let offset = isize::try_from(offset).expect("overflow"); + let element_ptr = base_ptr.offset(offset); + let element_ref = &*(element_ptr as *const AnyValue); + element_ref + } +} + +impl<'mv> MoveBorrowedRustVecOfStructMut<'mv> { + pub unsafe fn get_mut(&mut self, i: usize) -> &'mv mut AnyValue { + let struct_size = usize::try_from(self.type_.size).expect("overflow"); + let vec_len = usize::try_from(self.inner.length).expect("overflow"); + + if i >= vec_len { + panic!("index out of bounds"); + } + + let base_ptr = self.inner.ptr; + let offset = i.checked_mul(struct_size).expect("overflow"); + let offset = isize::try_from(offset).expect("overflow"); + let element_ptr = base_ptr.offset(offset); + let element_ref = &mut *(element_ptr as *mut AnyValue); + element_ref + } + + /// Get a pointer to a possibly-uninitialized element. + pub unsafe fn get_mut_unchecked_raw(&mut self, i: usize) -> *mut AnyValue { + let struct_size = usize::try_from(self.type_.size).expect("overflow"); + let vec_capacity = usize::try_from(self.inner.capacity).expect("overflow"); + + if i >= vec_capacity { + panic!("index out of bounds"); + } + + let base_ptr = self.inner.ptr; + let offset = i.checked_mul(struct_size).expect("overflow"); + let offset = isize::try_from(offset).expect("overflow"); + let element_ptr = base_ptr.offset(offset); + let element_ptr = element_ptr as *mut AnyValue; + element_ptr + } + + pub unsafe fn set_length(&mut self, len: usize) { + let vec_capacity = usize::try_from(self.inner.capacity).expect("overflow"); + + if len > vec_capacity { + panic!("index greater than capacity"); + } + + let len = u64::try_from(len).expect("overflow"); + self.inner.length = len; + } + + pub unsafe fn push(&mut self, ptr: *mut AnyValue) { + self.maybe_grow(); + + let struct_size = usize::try_from(self.type_.size).expect("overflow"); + let vec_len = usize::try_from(self.inner.length).expect("overflow"); + let vec_cap = usize::try_from(self.inner.capacity).expect("overflow"); + + assert!(vec_len < vec_cap); + + let i = vec_len; + + let base_ptr = self.inner.ptr; + let offset = i.checked_mul(struct_size).expect("overflow"); + let offset = isize::try_from(offset).expect("overflow"); + let element_ptr = base_ptr.offset(offset); + + let src_ptr = ptr as *mut u8; + ptr::copy_nonoverlapping(src_ptr, element_ptr, struct_size); + + self.inner.length = self.inner.length.checked_add(1).expect("overflow"); + } + + pub unsafe fn maybe_grow(&mut self) { + let struct_size = usize::try_from(self.type_.size).expect("overflow"); + let vec_len = usize::try_from(self.inner.length).expect("overflow"); + let vec_cap = usize::try_from(self.inner.capacity).expect("overflow"); + + if vec_len < vec_cap { + return; + } + + assert_eq!(vec_len, vec_cap); + + self.grow_amortized(); + } + + /// This is approximately like `RawVec::grow_amortized`. + /// + /// It always produces a power-of-two capacity. + #[cold] + pub unsafe fn grow_amortized(&mut self) { + let struct_size = usize::try_from(self.type_.size).expect("overflow"); + let struct_align = usize::try_from(self.type_.alignment).expect("overflow"); + let vec_len = usize::try_from(self.inner.length).expect("overflow"); + let vec_cap = usize::try_from(self.inner.capacity).expect("overflow"); + + assert_eq!(vec_len, vec_cap); + + // Same as RawVec + let min_non_zero_cap = if struct_size == 1 { + 8 + } else if struct_size <= 1024 { + 4 + } else { + 1 + }; + + let new_cap = vec_cap.checked_mul(2).expect("overflow"); + let new_cap = core::cmp::max(new_cap, min_non_zero_cap); + + self.reserve_exact(new_cap); + } + + pub unsafe fn reserve_exact(&mut self, new_cap: usize) { + let struct_size = usize::try_from(self.type_.size).expect("overflow"); + let struct_align = usize::try_from(self.type_.alignment).expect("overflow"); + let vec_len = usize::try_from(self.inner.length).expect("overflow"); + let vec_cap = usize::try_from(self.inner.capacity).expect("overflow"); + let new_cap_u64 = u64::try_from(new_cap).expect("overflow"); + + assert!(struct_size != 0); // can't handle ZSTs + assert!(new_cap >= vec_cap); + + let old_vec_byte_size = vec_cap.checked_mul(struct_size).expect("overflow"); + let new_vec_byte_size = new_cap.checked_mul(struct_size).expect("overflow"); + let new_layout = alloc::alloc::Layout::from_size_align(new_vec_byte_size, struct_align) + .expect("bad size or alignment"); + + if vec_cap == 0 { + let new_ptr = alloc::alloc::alloc(new_layout); + if new_ptr.is_null() { + alloc::alloc::handle_alloc_error(new_layout); + } + self.inner.ptr = new_ptr; + self.inner.capacity = new_cap_u64; + } else { + let old_layout = + alloc::alloc::Layout::from_size_align(old_vec_byte_size, struct_align) + .expect("bad size or alignment"); + + let new_ptr = alloc::alloc::realloc(self.inner.ptr, old_layout, new_vec_byte_size); + if new_ptr.is_null() { + alloc::alloc::handle_alloc_error(new_layout); + } + self.inner.ptr = new_ptr; + self.inner.capacity = new_cap_u64; + } + } + + pub unsafe fn pop_into(&mut self, ptr: *mut AnyValue) { + let struct_size = usize::try_from(self.type_.size).expect("overflow"); + let vec_len = usize::try_from(self.inner.length).expect("overflow"); + + let i = vec_len.checked_sub(1).expect("popping empty vector"); + + let base_ptr = self.inner.ptr; + let offset = i.checked_mul(struct_size).expect("overflow"); + let offset = isize::try_from(offset).expect("overflow"); + let element_ptr = base_ptr.offset(offset); + + let dest_ptr = ptr as *mut u8; + ptr::copy_nonoverlapping(element_ptr, dest_ptr, struct_size); + + self.inner.length = self.inner.length.checked_sub(1).expect("overflow"); + } + + pub unsafe fn swap(&mut self, i: usize, j: usize) { + let struct_size = usize::try_from(self.type_.size).expect("overflow"); + let vec_len = usize::try_from(self.inner.length).expect("overflow"); + + if i >= vec_len || j >= vec_len { + panic!("index out of bounds"); + } + + // Safety: must avoid overlapping pointers in swap_nonoverlapping + // below. + if i == j { + return; + } + + let base_ptr = self.inner.ptr; + + let i_offset = i.checked_mul(struct_size).expect("overflow"); + let i_offset = isize::try_from(i_offset).expect("overflow"); + let i_element_ptr = base_ptr.offset(i_offset); + let j_offset = j.checked_mul(struct_size).expect("overflow"); + let j_offset = isize::try_from(j_offset).expect("overflow"); + let j_element_ptr = base_ptr.offset(j_offset); + + // Safety: because of the presense of uninitialized padding bytes, + // we must (I think) do this swap with raw pointers, not slices. + ptr::swap_nonoverlapping(i_element_ptr, j_element_ptr, struct_size); + } +} + +pub enum BorrowedTypedMoveValue<'mv> { + Bool(&'mv bool), + U8(&'mv u8), + U16(&'mv u16), + U32(&'mv u32), + U64(&'mv u64), + U128(&'mv u128), + U256(&'mv U256), + Address(&'mv MoveAddress), + Signer(&'mv MoveSigner), + Vector(MoveType, &'mv MoveUntypedVector), + Struct(MoveType, &'mv AnyValue), + Reference(MoveType, &'mv MoveUntypedReference), + // todo +} + +pub unsafe fn borrow_move_value_as_rust_value<'mv>( + type_: &MoveType, + value: &'mv AnyValue, +) -> BorrowedTypedMoveValue<'mv> { + // todo need to think about the soundness of this transmute + match type_.type_desc { + TypeDesc::Bool => BorrowedTypedMoveValue::Bool(mem::transmute(value)), + TypeDesc::U8 => BorrowedTypedMoveValue::U8(mem::transmute(value)), + TypeDesc::U16 => BorrowedTypedMoveValue::U16(mem::transmute(value)), + TypeDesc::U32 => BorrowedTypedMoveValue::U32(mem::transmute(value)), + TypeDesc::U64 => BorrowedTypedMoveValue::U64(mem::transmute(value)), + TypeDesc::U128 => BorrowedTypedMoveValue::U128(mem::transmute(value)), + TypeDesc::U256 => BorrowedTypedMoveValue::U256(mem::transmute(value)), + TypeDesc::Address => BorrowedTypedMoveValue::Address(mem::transmute(value)), + TypeDesc::Signer => BorrowedTypedMoveValue::Signer(mem::transmute(value)), + TypeDesc::Vector => { + let element_type = *(*type_.type_info).vector.element_type; + let move_ref = mem::transmute(value); + BorrowedTypedMoveValue::Vector(element_type, move_ref) + } + TypeDesc::Struct => { + // Previously we stored the StructTypeInfo here. But passing the enclosing + // MoveType instead gives routines access to the struct name (i.e., more + // context). Otherwise we would need an uplevel pointer in StructTypeInfo or + // to redundantly store the name there. + BorrowedTypedMoveValue::Struct(*type_, value) + } + TypeDesc::Reference => { + let element_type = *(*type_.type_info).reference.element_type; + let move_ref = mem::transmute(value); + BorrowedTypedMoveValue::Reference(element_type, move_ref) + } + } +} + +/// The same as `BorrowedTypedMoveValue` but with raw pointers. +/// +/// Allows for uninitialized values. +pub enum RawBorrowedTypedMoveValue { + Bool(*mut bool), + U8(*mut u8), + U16(*mut u16), + U32(*mut u32), + U64(*mut u64), + U128(*mut u128), + U256(*mut U256), + Address(*mut MoveAddress), + Signer(*mut MoveSigner), + Vector(MoveType, *mut MoveUntypedVector), + Struct(MoveType, *mut AnyValue), + Reference(MoveType, *mut MoveUntypedReference), +} + +pub unsafe fn raw_borrow_move_value_as_rust_value( + type_: &MoveType, + value: *mut AnyValue, +) -> RawBorrowedTypedMoveValue { + match type_.type_desc { + TypeDesc::Bool => RawBorrowedTypedMoveValue::Bool(mem::transmute(value)), + TypeDesc::U8 => RawBorrowedTypedMoveValue::U8(mem::transmute(value)), + TypeDesc::U16 => RawBorrowedTypedMoveValue::U16(mem::transmute(value)), + TypeDesc::U32 => RawBorrowedTypedMoveValue::U32(mem::transmute(value)), + TypeDesc::U64 => RawBorrowedTypedMoveValue::U64(mem::transmute(value)), + TypeDesc::U128 => RawBorrowedTypedMoveValue::U128(mem::transmute(value)), + TypeDesc::U256 => RawBorrowedTypedMoveValue::U256(mem::transmute(value)), + TypeDesc::Address => RawBorrowedTypedMoveValue::Address(mem::transmute(value)), + TypeDesc::Signer => RawBorrowedTypedMoveValue::Signer(mem::transmute(value)), + TypeDesc::Vector => { + let element_type = *(*type_.type_info).vector.element_type; + let move_ref = mem::transmute(value); + RawBorrowedTypedMoveValue::Vector(element_type, move_ref) + } + TypeDesc::Struct => { + RawBorrowedTypedMoveValue::Struct(*type_, value) + } + TypeDesc::Reference => { + let element_type = *(*type_.type_info).reference.element_type; + let move_ref = mem::transmute(value); + RawBorrowedTypedMoveValue::Reference(element_type, move_ref) + } + } +} + +pub enum TypedMoveBorrowedRustVec<'mv> { + Bool(MoveBorrowedRustVec<'mv, bool>), + U8(MoveBorrowedRustVec<'mv, u8>), + U16(MoveBorrowedRustVec<'mv, u16>), + U32(MoveBorrowedRustVec<'mv, u32>), + U64(MoveBorrowedRustVec<'mv, u64>), + U128(MoveBorrowedRustVec<'mv, u128>), + U256(MoveBorrowedRustVec<'mv, U256>), + Address(MoveBorrowedRustVec<'mv, MoveAddress>), + Signer(MoveBorrowedRustVec<'mv, MoveSigner>), + Vector(MoveType, MoveBorrowedRustVec<'mv, MoveUntypedVector>), + Struct(MoveBorrowedRustVecOfStruct<'mv>), + Reference(MoveType, MoveBorrowedRustVec<'mv, MoveUntypedReference>), + // todo +} + +#[derive(Debug)] +pub enum TypedMoveBorrowedRustVecMut<'mv> { + Bool(MoveBorrowedRustVecMut<'mv, bool>), + U8(MoveBorrowedRustVecMut<'mv, u8>), + U16(MoveBorrowedRustVecMut<'mv, u16>), + U32(MoveBorrowedRustVecMut<'mv, u32>), + U64(MoveBorrowedRustVecMut<'mv, u64>), + U128(MoveBorrowedRustVecMut<'mv, u128>), + U256(MoveBorrowedRustVecMut<'mv, U256>), + Address(MoveBorrowedRustVecMut<'mv, MoveAddress>), + Signer(MoveBorrowedRustVecMut<'mv, MoveSigner>), + Vector(MoveType, MoveBorrowedRustVecMut<'mv, MoveUntypedVector>), + Struct(MoveBorrowedRustVecOfStructMut<'mv>), + Reference(MoveType, MoveBorrowedRustVecMut<'mv, MoveUntypedReference>), + // todo +} + +#[rustfmt::skip] +pub unsafe fn borrow_typed_move_vec_as_rust_vec<'mv>( + type_: &'mv MoveType, + mv: &'mv MoveUntypedVector, +) -> TypedMoveBorrowedRustVec<'mv> { + match type_.type_desc { + TypeDesc::Bool => { + TypedMoveBorrowedRustVec::Bool(borrow_move_vec_as_rust_vec::(mv)) + } + TypeDesc::U8 => { + TypedMoveBorrowedRustVec::U8(borrow_move_vec_as_rust_vec::(mv)) + } + TypeDesc::U16 => { + TypedMoveBorrowedRustVec::U16(borrow_move_vec_as_rust_vec::(mv)) + } + TypeDesc::U32 => { + TypedMoveBorrowedRustVec::U32(borrow_move_vec_as_rust_vec::(mv)) + } + TypeDesc::U64 => { + TypedMoveBorrowedRustVec::U64(borrow_move_vec_as_rust_vec::(mv)) + } + TypeDesc::U128 => { + TypedMoveBorrowedRustVec::U128(borrow_move_vec_as_rust_vec::(mv)) + } + TypeDesc::U256 => { + TypedMoveBorrowedRustVec::U256(borrow_move_vec_as_rust_vec::(mv)) + } + TypeDesc::Address => { + TypedMoveBorrowedRustVec::Address(borrow_move_vec_as_rust_vec::(mv)) + } + TypeDesc::Signer => { + TypedMoveBorrowedRustVec::Signer(borrow_move_vec_as_rust_vec::(mv)) + } + TypeDesc::Vector => { + TypedMoveBorrowedRustVec::Vector( + *(*type_.type_info).vector.element_type, + borrow_move_vec_as_rust_vec::(mv), + ) + } + TypeDesc::Struct => { + TypedMoveBorrowedRustVec::Struct( + MoveBorrowedRustVecOfStruct { + inner: mv, + name: type_.name, + type_: &(*type_.type_info).struct_, + } + ) + } + TypeDesc::Reference => { + TypedMoveBorrowedRustVec::Reference( + *(*type_.type_info).reference.element_type, + borrow_move_vec_as_rust_vec::(mv), + ) + } + } +} + +#[rustfmt::skip] +pub unsafe fn borrow_typed_move_vec_as_rust_vec_mut<'mv>( + type_: &'mv MoveType, + mv: &'mv mut MoveUntypedVector, +) -> TypedMoveBorrowedRustVecMut<'mv> { + match type_.type_desc { + TypeDesc::Bool => { + TypedMoveBorrowedRustVecMut::Bool(borrow_move_vec_as_rust_vec_mut::(mv)) + } + TypeDesc::U8 => { + TypedMoveBorrowedRustVecMut::U8(borrow_move_vec_as_rust_vec_mut::(mv)) + } + TypeDesc::U16 => { + TypedMoveBorrowedRustVecMut::U16(borrow_move_vec_as_rust_vec_mut::(mv)) + } + TypeDesc::U32 => { + TypedMoveBorrowedRustVecMut::U32(borrow_move_vec_as_rust_vec_mut::(mv)) + } + TypeDesc::U64 => { + TypedMoveBorrowedRustVecMut::U64(borrow_move_vec_as_rust_vec_mut::(mv)) + } + TypeDesc::U128 => { + TypedMoveBorrowedRustVecMut::U128(borrow_move_vec_as_rust_vec_mut::(mv)) + } + TypeDesc::U256 => { + TypedMoveBorrowedRustVecMut::U256(borrow_move_vec_as_rust_vec_mut::(mv)) + } + TypeDesc::Address => { + TypedMoveBorrowedRustVecMut::Address(borrow_move_vec_as_rust_vec_mut::(mv)) + } + TypeDesc::Signer => { + TypedMoveBorrowedRustVecMut::Signer(borrow_move_vec_as_rust_vec_mut::(mv)) + } + TypeDesc::Vector => { + TypedMoveBorrowedRustVecMut::Vector( + *(*type_.type_info).vector.element_type, + borrow_move_vec_as_rust_vec_mut::(mv), + ) + } + TypeDesc::Struct => { + TypedMoveBorrowedRustVecMut::Struct( + MoveBorrowedRustVecOfStructMut { + inner: mv, + name: type_.name, + type_: &(*type_.type_info).struct_, + } + ) + } + TypeDesc::Reference => { + TypedMoveBorrowedRustVecMut::Reference( + *(*type_.type_info).reference.element_type, + borrow_move_vec_as_rust_vec_mut::(mv), + ) + } + } +} + +pub unsafe fn walk_struct_fields<'mv>( + info: &'mv StructTypeInfo, + struct_ref: &'mv AnyValue, +) -> impl Iterator { + let field_len = usize::try_from(info.field_array_len).expect("overflow"); + let fields: &'mv [StructFieldInfo] = slice::from_raw_parts(info.field_array_ptr, field_len); + + fields.iter().map(|field| { + let struct_base_ptr: *const AnyValue = struct_ref as _; + let field_offset = isize::try_from(field.offset).expect("overflow"); + let field_ptr = struct_base_ptr.offset(field_offset); + let field_ref: &'mv AnyValue = &*field_ptr; + (&field.type_, field_ref, &field.name) + }) +} + +pub unsafe fn walk_struct_fields_mut<'mv>( + info: &'mv StructTypeInfo, + struct_ref: *mut AnyValue, +) -> impl Iterator { + let field_len = usize::try_from(info.field_array_len).expect("overflow"); + let fields: &'mv [StructFieldInfo] = slice::from_raw_parts(info.field_array_ptr, field_len); + + fields.iter().map(move |field| { + let struct_base_ptr: *mut AnyValue = struct_ref as _; + let field_offset = isize::try_from(field.offset).expect("overflow"); + let field_ptr = struct_base_ptr.offset(field_offset); + (&field.type_, field_ptr, &field.name) + }) +} + +impl<'mv> core::fmt::Debug for BorrowedTypedMoveValue<'mv> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + BorrowedTypedMoveValue::Bool(v) => v.fmt(f), + BorrowedTypedMoveValue::U8(v) => v.fmt(f), + BorrowedTypedMoveValue::U16(v) => v.fmt(f), + BorrowedTypedMoveValue::U32(v) => v.fmt(f), + BorrowedTypedMoveValue::U64(v) => v.fmt(f), + BorrowedTypedMoveValue::U128(v) => v.fmt(f), + BorrowedTypedMoveValue::U256(v) => v.fmt(f), + BorrowedTypedMoveValue::Address(v) => v.fmt(f), + BorrowedTypedMoveValue::Signer(v) => v.fmt(f), + BorrowedTypedMoveValue::Vector(t, v) => unsafe { + let rv = borrow_typed_move_vec_as_rust_vec(t, v); + rv.fmt(f) + }, + BorrowedTypedMoveValue::Struct(t, v) => unsafe { + let st = (*(t.type_info)).struct_; + write!(f, "{} {{ ", t.name.as_ascii_str()); + let fields = walk_struct_fields(&st, v); + for (type_, ref_, fld_name) in fields { + let rv = borrow_move_value_as_rust_value(type_, ref_); + write!(f, "{}: ", fld_name.as_ascii_str()); + rv.fmt(f); + f.write_str(", "); + } + f.write_str("}"); + Ok(()) + }, + BorrowedTypedMoveValue::Reference(t, v) => unsafe { + let rv = borrow_move_value_as_rust_value(t, &*v.0); + rv.fmt(f) + }, + } + } +} + +impl<'mv> core::fmt::Debug for TypedMoveBorrowedRustVec<'mv> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + TypedMoveBorrowedRustVec::Bool(v) => v.fmt(f), + TypedMoveBorrowedRustVec::U8(v) => v.fmt(f), + TypedMoveBorrowedRustVec::U16(v) => v.fmt(f), + TypedMoveBorrowedRustVec::U32(v) => v.fmt(f), + TypedMoveBorrowedRustVec::U64(v) => v.fmt(f), + TypedMoveBorrowedRustVec::U128(v) => v.fmt(f), + TypedMoveBorrowedRustVec::U256(v) => v.fmt(f), + TypedMoveBorrowedRustVec::Address(v) => v.fmt(f), + TypedMoveBorrowedRustVec::Signer(v) => v.fmt(f), + TypedMoveBorrowedRustVec::Vector(t, v) => { + let mut dbg = f.debug_list(); + for e in v.iter() { + unsafe { + let e = borrow_typed_move_vec_as_rust_vec(t, e); + dbg.entry(&e); + } + } + dbg.finish() + } + TypedMoveBorrowedRustVec::Struct(s) => { + f.write_str("["); + unsafe { + for vref in s.iter() { + let type_ = MoveType { + name: s.name, + type_desc: TypeDesc::Struct, + type_info: &TypeInfo { struct_: *s.type_ }, + }; + let e = borrow_move_value_as_rust_value(&type_, vref); + e.fmt(f); + f.write_str(", "); + } + } + f.write_str("]"); + Ok(()) + } + TypedMoveBorrowedRustVec::Reference(t, v) => { + let mut dbg = f.debug_list(); + for e in v.iter() { + unsafe { + let e = borrow_move_value_as_rust_value(t, &*e.0); + dbg.entry(&e); + } + } + dbg.finish() + } + } + } +} diff --git a/language/move-native/src/lib.rs b/language/move-native/src/lib.rs index b0cc925d15..62e5730e2b 100644 --- a/language/move-native/src/lib.rs +++ b/language/move-native/src/lib.rs @@ -285,1871 +285,20 @@ pub mod shared { } /// Types known to the compiler. -pub(crate) mod rt_types { - use crate::target_defs; - - /// A Move vector with an untyped buffer. - /// - /// Used in the API for generic vector arguments. - /// - /// The only way to interact with these is to convert them from / to Rust - /// vectors or references to Rust vectors, with functions in the [`conv`] - /// module. - /// - /// The only way to create and destroy them is with the - /// [`move_native_vec_empty`] and [`move_native_vec_destroy_empty`] native - /// calls. - #[repr(C)] - #[derive(Debug)] - pub struct MoveUntypedVector { - pub ptr: *mut u8, // Safety: must be correctly aligned per type - pub capacity: u64, // in typed elements, not u8 - pub length: u64, // in typed elements, not u8 - } - pub const MOVE_UNTYPED_VEC_DESC_SIZE: u64 = core::mem::size_of::() as u64; - - /// A Move vector of bytes. - /// - /// These occur in the API enough to warrant their own type, and there are - /// dedicated functions to convert them to Rust vectors. - #[repr(C)] - pub struct MoveByteVector { - pub ptr: *mut u8, - pub capacity: u64, - pub length: u64, - } - - /// A Move vector of signers. - /// - /// This type occurs in the native API, but it will probably be removed, in - /// favor of just using `MoveUntypedVector`. - #[repr(C)] - pub struct MoveSignerVector { - pub ptr: *mut MoveSigner, - pub capacity: u64, - pub length: u64, - } - - /// A reification of the Move runtime type description. - /// - /// This is structured as a `TypeDesc` indicating which type a thing is, - /// and an undiscriminated union holding additional information about the - /// type. - /// - /// cc runtime_types::Type - /// - /// # Safety - /// - /// The pointer must be to static memory and never mutated. - #[repr(C)] - #[derive(Copy, Clone)] - pub struct MoveType { - pub name: StaticTypeName, - pub type_desc: TypeDesc, - pub type_info: *const TypeInfo, - } - pub const MOVE_TYPE_DESC_SIZE: u64 = core::mem::size_of::() as u64; - - // Needed to make the MoveType, which contains raw pointers, - // Sync, so that it can be stored in statics for test cases. - unsafe impl Sync for MoveType { } - - impl core::fmt::Debug for MoveType { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - // fixme: implement this better - unsafe { - write!(f, "{}", self.name.as_ascii_str()); - } - Ok(()) - } - } - - /// # Safety - /// - /// The pointer must be to static memory and never mutated. - #[repr(C)] - #[derive(Copy, Clone, Debug)] - pub struct StaticTypeName { - pub ptr: *const u8, - pub len: u64, - } - - impl StaticTypeName { - pub unsafe fn as_ascii_str<'a>(&'a self) -> &'a str { - core::str::from_utf8_unchecked(core::slice::from_raw_parts( - self.ptr, - usize::try_from(self.len).expect("overflow"), - )) - } - } - - pub type StaticName = StaticTypeName; - - static DUMMY_TYPE_NAME_SLICE: &[u8] = b"dummy"; - pub static DUMMY_TYPE_NAME: StaticTypeName = StaticTypeName { - ptr: DUMMY_TYPE_NAME_SLICE as *const [u8] as *const u8, - len: 5, - }; - - unsafe impl Sync for StaticTypeName {} - - #[repr(u64)] - #[derive(Copy, Clone, Debug)] - pub enum TypeDesc { - Bool = 1, - U8 = 2, - U16 = 3, - U32 = 4, - U64 = 5, - U128 = 6, - U256 = 7, - Address = 8, - Signer = 9, - Vector = 10, - Struct = 11, - Reference = 12, - //MutableReference = 13, - } - - #[repr(C)] - #[derive(Copy, Clone)] - pub union TypeInfo { - pub nothing: u8, // if no type info is needed - pub vector: VectorTypeInfo, - pub struct_: StructTypeInfo, - pub struct_instantiation: u8, // todo - pub reference: ReferenceTypeInfo, - pub mutable_reference: ReferenceTypeInfo, - pub ty_param: u8, // todo - } - - #[repr(C)] - #[derive(Copy, Clone)] - pub struct VectorTypeInfo { - pub element_type: &'static MoveType, - } - - /// # Safety - /// - /// This type is `Sync` so that it can be declared statically. The value - /// pointed to by `field_array_ptr` should not be mutated, or `Sync` will be - /// violated. - #[repr(C)] - #[derive(Copy, Clone, Debug)] - pub struct StructTypeInfo { - /// Pointer to an array of field infos. - /// - /// This would ideally be a Rust static slice, but the layout is - /// seemingly undefined. - pub field_array_ptr: *const StructFieldInfo, - pub field_array_len: u64, - /// Size of the struct within an array. - pub size: u64, - /// Alignment of the struct. - pub alignment: u64, - } - - unsafe impl Sync for StructTypeInfo {} - - #[repr(C)] - #[derive(Copy, Clone, Debug)] - pub struct StructFieldInfo { - pub type_: MoveType, - /// Offset in bytes within the struct. - pub offset: u64, - pub name: StaticName, - } - - #[repr(C)] - #[derive(Copy, Clone, Debug)] - pub struct ReferenceTypeInfo { - pub element_type: &'static MoveType, - } - - #[repr(transparent)] - pub struct AnyValue(u8); - - #[repr(transparent)] - #[derive(Debug, PartialEq)] - #[derive(borsh::BorshSerialize, borsh::BorshDeserialize)] - pub struct MoveSigner(pub MoveAddress); - - /// A Move address. - /// - /// This is mapped to the address size of the target platform, and may - /// differ from Move VM. - /// - /// Bytes are in little-endian order. - #[repr(transparent)] - #[derive(PartialEq)] - #[derive(borsh::BorshSerialize, borsh::BorshDeserialize)] - pub struct MoveAddress(pub [u8; target_defs::ACCOUNT_ADDRESS_LENGTH]); - - impl core::fmt::Debug for MoveAddress { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.write_str("@")?; - for byte in self.0.iter().rev() { - f.write_fmt(core::format_args!("{byte:02X?}"))?; - } - Ok(()) - } - } - - // Defined in std::type_name; not a primitive. - // - // todo how is drop glue handled? - #[repr(C)] - pub struct TypeName { - pub name: MoveAsciiString, - } - - // Defined in std::ascii; not a primitive. - // - // todo how is drop glue handled? - #[repr(C)] - pub struct MoveAsciiString { - pub bytes: MoveByteVector, - } - - // todo this would be more correct with a lifetime attached - #[repr(transparent)] - #[derive(Debug)] - pub struct MoveUntypedReference(pub *const AnyValue); - - mod drop_bomb { - // fixme this is pretty awkward - this is intended to be a no-std crate - #[cfg(test)] - extern crate std; - - #[cfg(not(test))] - pub fn run(name: &str) { - panic!("forgot to destroy {}", name); - } - - #[cfg(test)] - pub fn run(name: &str) { - if !std::thread::panicking() { - panic!("forgot to destroy {}", name); - } else { - std::eprintln!("forgot to destroy {}", name); - } - } - } - - // Drop-bomb. Catch errors in test cases. - impl Drop for MoveUntypedVector { - fn drop(&mut self) { - drop_bomb::run("MoveUntypedVector"); - } - } - - // Drop-bomb. Catch errors in test cases. - impl Drop for MoveByteVector { - fn drop(&mut self) { - drop_bomb::run("MoveByteVector"); - } - } - - // Drop-bomb. Catch errors in test cases. - impl Drop for MoveSignerVector { - fn drop(&mut self) { - drop_bomb::run("MoveSignerVector"); - } - } - - /// Don't run destructors. - /// - /// Some of these runtime types hold allocations, and need to be destroyed - /// in specific ways. If they are not, then they panic. These types must be - /// destroyed by calling `mem::forget`, for which this function is a - /// synonym. - pub fn disarm_drop_bomb(v: T) { - core::mem::forget(v) - } -} +mod rt_types; /// Runtime calls emitted by the compiler. /// Reference: move/language/documentation/book/src/abort-and-assert.md -mod rt { - use crate::rt_types::{AnyValue, MoveType, MoveUntypedVector, MoveAddress, MoveSigner}; - - #[export_name = "move_rt_abort"] - fn abort(code: u64) -> ! { - crate::target_defs::abort(code); - } - - #[export_name = "move_rt_vec_destroy"] - unsafe fn vec_destroy(type_ve: &MoveType, v: MoveUntypedVector) { - assert_eq!(0, v.length, "can't destroy vectors with elements yet"); - crate::std::vector::destroy_empty(type_ve, v); - } - - #[export_name = "move_rt_vec_empty"] - unsafe fn vec_empty(type_ve: &MoveType) -> MoveUntypedVector { - crate::std::vector::empty(type_ve) - } - - #[export_name = "move_rt_vec_copy"] - unsafe fn vec_copy(type_ve: &MoveType, dstv: &mut MoveUntypedVector, srcv: &MoveUntypedVector) { - use crate::std::vector as V; - let src_len = V::length(type_ve, srcv); - let dst_len = V::length(type_ve, dstv); - - // Drain the destination first. - for i in 0..dst_len { - crate::rt::pop_back_discard(type_ve, dstv); - } - - // Now copy. - for i in 0..src_len { - let se = V::borrow(type_ve, srcv, i); - let septr = se as *const AnyValue as *mut AnyValue; - V::push_back(type_ve, dstv, septr); - } - } - - unsafe fn pop_back_discard( - type_ve: &MoveType, - v: &mut MoveUntypedVector, - ) { - use crate::conv::{TypedMoveBorrowedRustVecMut, borrow_typed_move_vec_as_rust_vec_mut}; - let mut rust_vec = borrow_typed_move_vec_as_rust_vec_mut(type_ve, v); - - let msg = "popping from empty vec"; - match rust_vec { - TypedMoveBorrowedRustVecMut::Bool(mut v) => { v.pop().expect(msg); } - TypedMoveBorrowedRustVecMut::U8(mut v) => { v.pop().expect(msg); } - TypedMoveBorrowedRustVecMut::U16(mut v) => { v.pop().expect(msg); } - TypedMoveBorrowedRustVecMut::U32(mut v) => { v.pop().expect(msg); } - TypedMoveBorrowedRustVecMut::U64(mut v) => { v.pop().expect(msg); } - TypedMoveBorrowedRustVecMut::U128(mut v) => { v.pop().expect(msg); } - TypedMoveBorrowedRustVecMut::U256(mut v) => { v.pop().expect(msg); } - TypedMoveBorrowedRustVecMut::Address(mut v) => { v.pop().expect(msg);} - TypedMoveBorrowedRustVecMut::Signer(mut v) => { v.pop().expect(msg); } - TypedMoveBorrowedRustVecMut::Vector(_t, mut v) => { v.pop().expect(msg); } - TypedMoveBorrowedRustVecMut::Struct(mut v) => { todo!(); } - TypedMoveBorrowedRustVecMut::Reference(_t, mut v) => { v.pop().expect(msg); } - }; - } - - #[export_name = "move_rt_vec_cmp_eq"] - unsafe fn vec_cmp_eq(type_ve: &MoveType, v1: &MoveUntypedVector, v2: &MoveUntypedVector) -> bool { - use crate::conv::borrow_move_vec_as_rust_vec; - use ethnum::U256; - use crate::rt_types::TypeDesc; - use crate::std::vector as V; - use core::ops::Deref; - - let v1_len = V::length(type_ve, v1); - let v2_len = V::length(type_ve, v2); - - if v1_len != v2_len { - return false; - } - - let is_eq = match type_ve.type_desc { - TypeDesc::Bool => { - let mut rv1 = borrow_move_vec_as_rust_vec::(v1); - let mut rv2 = borrow_move_vec_as_rust_vec::(v2); - rv1.deref().eq(rv2.deref()) - } - TypeDesc::U8 => { - let mut rv1 = borrow_move_vec_as_rust_vec::(v1); - let mut rv2 = borrow_move_vec_as_rust_vec::(v2); - rv1.deref().eq(rv2.deref()) - } - TypeDesc::U16 => { - let mut rv1 = borrow_move_vec_as_rust_vec::(v1); - let mut rv2 = borrow_move_vec_as_rust_vec::(v2); - rv1.deref().eq(rv2.deref()) - } - TypeDesc::U32 => { - let mut rv1 = borrow_move_vec_as_rust_vec::(v1); - let mut rv2 = borrow_move_vec_as_rust_vec::(v2); - rv1.deref().eq(rv2.deref()) - } - TypeDesc::U64 => { - let mut rv1 = borrow_move_vec_as_rust_vec::(v1); - let mut rv2 = borrow_move_vec_as_rust_vec::(v2); - rv1.deref().eq(rv2.deref()) - } - TypeDesc::U128 => { - let mut rv1 = borrow_move_vec_as_rust_vec::(v1); - let mut rv2 = borrow_move_vec_as_rust_vec::(v2); - rv1.deref().eq(rv2.deref()) - } - TypeDesc::U256 => { - let mut rv1 = borrow_move_vec_as_rust_vec::(v1); - let mut rv2 = borrow_move_vec_as_rust_vec::(v2); - rv1.deref().eq(rv2.deref()) - } - TypeDesc::Address => { - let mut rv1 = borrow_move_vec_as_rust_vec::(v1); - let mut rv2 = borrow_move_vec_as_rust_vec::(v2); - rv1.deref().eq(rv2.deref()) - } - TypeDesc::Signer => { - let mut rv1 = borrow_move_vec_as_rust_vec::(v1); - let mut rv2 = borrow_move_vec_as_rust_vec::(v2); - rv1.deref().eq(rv2.deref()) - } - TypeDesc::Vector => { - assert!(v1_len == v2_len, "unexpected vec cmp lengths"); - let inner_element_type = *(*type_ve.type_info).vector.element_type; - let mut tmp_result = true; - for i in 0..v1_len { - let anyval_ref1 = V::borrow(type_ve, v1, i); - let anyval_ref2 = V::borrow(type_ve, v2, i); - let mv_ut_vec1 = &*(anyval_ref1 as *const AnyValue as *const MoveUntypedVector); - let mv_ut_vec2 = &*(anyval_ref2 as *const AnyValue as *const MoveUntypedVector); - tmp_result = vec_cmp_eq(&inner_element_type, mv_ut_vec1, mv_ut_vec2); - if !tmp_result { break; } - } - tmp_result - } - TypeDesc::Struct => { - assert!(v1_len == v2_len, "unexpected vec cmp lengths"); - let mut tmp_result = true; - for i in 0..v1_len { - let anyval_ref1 = V::borrow(type_ve, v1, i); - let anyval_ref2 = V::borrow(type_ve, v2, i); - tmp_result = struct_cmp_eq(type_ve, anyval_ref1, anyval_ref2); - if !tmp_result { break; } - } - tmp_result - } - _ => todo!("vec_cmp_eq: unhandled element type: {:?}", type_ve.type_desc) - }; - is_eq - } - - #[export_name = "move_rt_struct_cmp_eq"] - unsafe fn struct_cmp_eq(type_ve: &MoveType, s1: &AnyValue, s2: &AnyValue) -> bool { - use crate::conv::walk_struct_fields; - use crate::conv::{BorrowedTypedMoveValue as BTMV, borrow_move_value_as_rust_value}; - - let st_info = (*(type_ve.type_info)).struct_; - let fields1 = walk_struct_fields(&st_info, s1); - let fields2 = walk_struct_fields(&st_info, s2); - for ((fld_ty1, fld_ref1, fld_name1), (fld_ty2, fld_ref2, fld_name2)) in Iterator::zip(fields1, fields2) { - let rv1 = borrow_move_value_as_rust_value(fld_ty1, fld_ref1); - let rv2 = borrow_move_value_as_rust_value(fld_ty2, fld_ref2); - - let is_eq = match (rv1, rv2) { - (BTMV::Bool(val1), BTMV::Bool(val2)) => { val1 == val2 } - (BTMV::U8(val1), BTMV::U8(val2)) => { val1 == val2 } - (BTMV::U16(val1), BTMV::U16(val2)) => { val1 == val2 } - (BTMV::U32(val1), BTMV::U32(val2)) => { val1 == val2 } - (BTMV::U64(val1), BTMV::U64(val2)) => { val1 == val2 } - (BTMV::U128(val1), BTMV::U128(val2)) => { val1 == val2 } - (BTMV::U256(val1), BTMV::U256(val2)) => { val1 == val2 } - (BTMV::Address(val1), BTMV::Address(val2)) => { val1 == val2 } - (BTMV::Signer(val1), BTMV::Signer(val2)) => { val1 == val2 } - (BTMV::Vector(t1, utv1), BTMV::Vector(_t2, utv2)) => { - vec_cmp_eq(&t1, utv1, utv2) - } - (BTMV::Struct(t1, anyv1), BTMV::Struct(_t2, anyv2)) => { - struct_cmp_eq(&t1, anyv1, anyv2) - } - (BTMV::Reference(_, _), BTMV::Reference(_, _)) => { - unreachable!("reference in struct field impossible") - } - _ => { unreachable!("struct_cmp_eq unexpected value combination") } - }; - - if !is_eq { return false } - } - true - } -} +mod rt; /// Implementations of native calls for `std`. -mod std { - mod bcs { - use crate::conv::*; - use crate::rt_types::*; - - /// Serialize any value. - /// - /// This does not necessarily produce the same serializations that - /// the Move VM does, as the Rust types are not the same, and - /// the serialization format may not actually be bcs. - /// - /// # References - /// - /// - `move-vm-types::values::Value` - /// - `move-core-types::value` - #[export_name = "move_native_bcs_to_bytes"] - unsafe extern "C" fn to_bytes(type_v: &MoveType, v: &AnyValue) -> MoveByteVector { - crate::serialization::serialize(type_v, v) - } - - /// Deserialize any value. - /// - /// This is not actually in move std, but is used for testing. - #[export_name = "move_native_bcs_test_from_bytes"] - unsafe extern "C" fn test_from_bytes(type_v: &MoveType, bytes: &MoveByteVector, v: *mut AnyValue) { - crate::serialization::deserialize(type_v, bytes, v) - } - } - - // nursery - mod debug { - use crate::conv::*; - use crate::rt_types::*; - use crate::target_defs; - use alloc::format; - use alloc::string::String; - use core::fmt::Write; - - #[export_name = "move_native_debug_print"] - unsafe extern "C" fn print(type_x: &MoveType, x: &AnyValue) { - let v = borrow_move_value_as_rust_value(type_x, x); - target_defs::print_string(&format!("{:?}", v)); - } - - #[export_name = "move_native_print_stack_trace"] - extern "C" fn print_stack_trace() { - target_defs::print_stack_trace(); - } - } - - // nursery - mod event { - use crate::rt_types::*; - - #[export_name = "move_native_event_write_to_event_store"] - unsafe extern "C" fn write_to_event_store( - type_msg: &MoveType, - guid: MoveByteVector, - count: u64, - msg: *mut AnyValue, - ) { - todo!() - } - } - - mod hash { - use crate::conv::{move_byte_vec_to_rust_vec, rust_vec_to_move_byte_vec}; - use crate::rt_types::*; - use sha2::{Digest, Sha256}; - use sha3::Sha3_256; - - #[export_name = "move_native_hash_sha2_256"] - unsafe extern "C" fn sha2_256(ptr: MoveByteVector) -> MoveByteVector { - let rust_vec = move_byte_vec_to_rust_vec(ptr); - - let hash_vec = Sha256::digest(rust_vec.as_slice()).to_vec(); - let move_vec = rust_vec_to_move_byte_vec(hash_vec); - - move_vec - } - - #[export_name = "move_native_hash_sha3_256"] - unsafe extern "C" fn sha3_256(ptr: MoveByteVector) -> MoveByteVector { - let rust_vec = move_byte_vec_to_rust_vec(ptr); - - let hash_vec = Sha3_256::digest(rust_vec.as_slice()).to_vec(); - let move_vec = rust_vec_to_move_byte_vec(hash_vec); - - move_vec - } - } - - mod signer { - use crate::rt_types::*; - - #[export_name = "move_native_signer_borrow_address"] - extern "C" fn borrow_address(s: &MoveSigner) -> &MoveAddress { - &s.0 - } - } - - pub(crate) mod string { - use crate::conv::*; - use crate::rt_types::*; - use alloc::vec::Vec; - use core::str; - - #[export_name = "move_native_string_internal_check_utf8"] - pub unsafe extern "C" fn internal_check_utf8(v: &MoveByteVector) -> bool { - let rust_vec = borrow_move_byte_vec_as_rust_vec(v); - let res = str::from_utf8(&rust_vec); - - match res { - Ok(_) => true, - Err(_) => false, - } - } - - #[export_name = "move_native_string_internal_is_char_boundary"] - pub unsafe extern "C" fn internal_is_char_boundary(v: &MoveByteVector, i: u64) -> bool { - let rust_vec = borrow_move_byte_vec_as_rust_vec(v); - let i = usize::try_from(i).expect("usize"); - - let rust_str = str::from_utf8(&rust_vec).expect("invalid utf8"); - rust_str.is_char_boundary(i) - } - - #[export_name = "move_native_string_internal_sub_string"] - pub unsafe extern "C" fn internal_sub_string( - v: &MoveByteVector, - i: u64, - j: u64, - ) -> MoveByteVector { - let rust_vec = borrow_move_byte_vec_as_rust_vec(v); - let i = usize::try_from(i).expect("usize"); - let j = usize::try_from(j).expect("usize"); - - let rust_str = str::from_utf8(&rust_vec).expect("invalid utf8"); - - let sub_rust_vec = rust_str[i..j].as_bytes().to_vec(); - rust_vec_to_move_byte_vec(sub_rust_vec) - } - - #[export_name = "move_native_string_internal_index_of"] - pub unsafe extern "C" fn internal_index_of(s: &MoveByteVector, r: &MoveByteVector) -> u64 { - let s_rust_vec = borrow_move_byte_vec_as_rust_vec(s); - let s_rust_str = str::from_utf8(&s_rust_vec).expect("invalid utf8"); - let r_rust_vec = borrow_move_byte_vec_as_rust_vec(r); - let r_rust_str = str::from_utf8(&r_rust_vec).expect("invalid utf8"); - - let res = s_rust_str.find(r_rust_str); - - u64::try_from(match res { - Some(i) => i, - None => s_rust_str.len(), - }) - .expect("u64") - } - } - - mod type_name { - use crate::conv::*; - use crate::rt_types::*; - - #[export_name = "move_native_type_name_get"] - unsafe extern "C" fn get(type_: &MoveType) -> TypeName { - let name_slice = type_.name.as_ascii_str(); - let byte_type = MoveType { - name: DUMMY_TYPE_NAME, - type_desc: TypeDesc::U8, - type_info: &TypeInfo { nothing: 0 }, - }; - let mut byte_vector = super::vector::empty(&byte_type); - { - let mut rust_byte_vector = borrow_move_vec_as_rust_vec_mut::(&mut byte_vector); - rust_byte_vector.reserve_exact(name_slice.len()); - for byte in name_slice.bytes() { - rust_byte_vector.push(byte); - } - } - TypeName { - name: MoveAsciiString { - // safety: MoveUntypedVector and MoveByteVector have the same representation - bytes: core::mem::transmute::(byte_vector), - }, - } - } - } - - mod unit_test { - use crate::rt_types::*; - - #[export_name = "move_native_unit_test_create_signers_for_testing"] - extern "C" fn create_signers_for_testing(num_signers: u64) -> MoveSignerVector { - todo!() - } - } - - pub(crate) mod vector { - use crate::conv::*; - use crate::rt_types::*; - use alloc::vec::Vec; - use core::{mem, ptr}; - use ethnum::U256; - - // Safety: Even empty Rust vectors have non-null buffer pointers, - // which must be correctly aligned. This function crates empty Rust vecs - // of the correct type and converts them to untyped move vecs. - #[export_name = "move_native_vector_empty"] - pub extern "C" fn empty(type_r: &MoveType) -> MoveUntypedVector { - let move_vec = match type_r.type_desc { - TypeDesc::Bool => rust_vec_to_move_vec::(Vec::new()), - TypeDesc::U8 => rust_vec_to_move_vec::(Vec::new()), - TypeDesc::U16 => rust_vec_to_move_vec::(Vec::new()), - TypeDesc::U32 => rust_vec_to_move_vec::(Vec::new()), - TypeDesc::U64 => rust_vec_to_move_vec::(Vec::new()), - TypeDesc::U128 => rust_vec_to_move_vec::(Vec::new()), - TypeDesc::U256 => rust_vec_to_move_vec::(Vec::new()), - TypeDesc::Address => rust_vec_to_move_vec::(Vec::new()), - TypeDesc::Signer => rust_vec_to_move_vec::(Vec::new()), - TypeDesc::Vector => { - // Safety: need correct alignment for the internal vector - // pointer of the outer vector, which is non-null even for - // an unallocated vector. `MoveUntypedVector` has the same - // size and alignment regardless of the type it contains, so - // no need to interpret the vector type. - rust_vec_to_move_vec::(Vec::new()) - } - TypeDesc::Struct => unsafe { - // Safety: this gets pretty sketchy, and relies on internal - // Vec details that probably are not guaranteed. The most - // _correct_ way to initialize a Vec is to call its - // constructor. - // - // That is pretty tough with a type of any dynamically sized - // layout, so we're going to munge the pointers ourselves. - // - // The critical thing to know about Vec's pointers is: - // - // - They must always be aligned correctly - // - They are _never_ 0, even for empty Vec's, to allow null - // pointer optimizations. - // - // Vec uses `NonNull::dangling` to create invalid non-null - // pointers, but that requires a concrete type of the - // correct alignment. We dig even deeper and use - // `ptr::invalid_mut`, which is an unstable function from - // the pointer provenance project. As it is unstable we just - // duplicate it in our `conv` module until it becomes - // stable. - // - // This should be the only location in this crate where we - // need to fabricate a pointer from an integer. - let size = (*type_r.type_info).struct_.size; - let size = usize::try_from(size).expect("overflow"); - let alignment = (*type_r.type_info).struct_.alignment; - let alignment = usize::try_from(alignment).expect("overflow"); - - assert!(size != 0); // can't handle ZSTs - assert!(alignment != 0); // must have alignment - assert!(alignment.is_power_of_two()); - - let ptr = invalid_mut::(alignment); - MoveUntypedVector { - ptr, - capacity: 0, - length: 0, - } - }, - TypeDesc::Reference => rust_vec_to_move_vec::(Vec::new()), - }; - - move_vec - } - - #[export_name = "move_native_vector_length"] - pub unsafe extern "C" fn length(type_ve: &MoveType, v: &MoveUntypedVector) -> u64 { - // It is not strictly necessary to convert the vec for this op. - // Doing it for consistency. - let rust_vec = borrow_typed_move_vec_as_rust_vec(type_ve, v); - - let len = match rust_vec { - TypedMoveBorrowedRustVec::Bool(v) => v.len(), - TypedMoveBorrowedRustVec::U8(v) => v.len(), - TypedMoveBorrowedRustVec::U16(v) => v.len(), - TypedMoveBorrowedRustVec::U32(v) => v.len(), - TypedMoveBorrowedRustVec::U64(v) => v.len(), - TypedMoveBorrowedRustVec::U128(v) => v.len(), - TypedMoveBorrowedRustVec::U256(v) => v.len(), - TypedMoveBorrowedRustVec::Address(v) => v.len(), - TypedMoveBorrowedRustVec::Signer(v) => v.len(), - TypedMoveBorrowedRustVec::Vector(_t, v) => v.len(), - TypedMoveBorrowedRustVec::Struct(s) => { - usize::try_from(s.inner.length).expect("overflow") - } - TypedMoveBorrowedRustVec::Reference(_t, v) => v.len(), - }; - - u64::try_from(len).expect("u64") - } - - #[export_name = "move_native_vector_borrow"] - pub unsafe extern "C" fn borrow<'v>( - type_ve: &'v MoveType, - v: &'v MoveUntypedVector, - i: u64, - ) -> &'v AnyValue { - let rust_vec = borrow_typed_move_vec_as_rust_vec(type_ve, v); - - let i = usize::try_from(i).expect("usize"); - let value = match rust_vec { - TypedMoveBorrowedRustVec::Bool(v) => mem::transmute(&v[i]), - TypedMoveBorrowedRustVec::U8(v) => mem::transmute(&v[i]), - TypedMoveBorrowedRustVec::U16(v) => mem::transmute(&v[i]), - TypedMoveBorrowedRustVec::U32(v) => mem::transmute(&v[i]), - TypedMoveBorrowedRustVec::U64(v) => mem::transmute(&v[i]), - TypedMoveBorrowedRustVec::U128(v) => mem::transmute(&v[i]), - TypedMoveBorrowedRustVec::U256(v) => mem::transmute(&v[i]), - TypedMoveBorrowedRustVec::Address(v) => mem::transmute(&v[i]), - TypedMoveBorrowedRustVec::Signer(v) => mem::transmute(&v[i]), - TypedMoveBorrowedRustVec::Vector(_t, v) => mem::transmute(&v[i]), - TypedMoveBorrowedRustVec::Struct(s) => s.get(i), - TypedMoveBorrowedRustVec::Reference(_t, v) => mem::transmute(&v[i]), - }; - - value - } - - #[rustfmt::skip] - #[export_name = "move_native_vector_push_back"] - pub unsafe extern "C" fn push_back( - type_ve: &MoveType, - v: &mut MoveUntypedVector, - e: *mut AnyValue - ) { - let mut rust_vec = borrow_typed_move_vec_as_rust_vec_mut(type_ve, v); - - match rust_vec { - TypedMoveBorrowedRustVecMut::Bool(mut v) => v.push(ptr::read(e as *const bool)), - TypedMoveBorrowedRustVecMut::U8(mut v) => v.push(ptr::read(e as *const u8)), - TypedMoveBorrowedRustVecMut::U16(mut v) => v.push(ptr::read(e as *const u16)), - TypedMoveBorrowedRustVecMut::U32(mut v) => v.push(ptr::read(e as *const u32)), - TypedMoveBorrowedRustVecMut::U64(mut v) => v.push(ptr::read(e as *const u64)), - TypedMoveBorrowedRustVecMut::U128(mut v) => v.push(ptr::read(e as *const u128)), - TypedMoveBorrowedRustVecMut::U256(mut v) => v.push(ptr::read(e as *const U256)), - TypedMoveBorrowedRustVecMut::Address(mut v) => v.push(ptr::read(e as *const MoveAddress)), - TypedMoveBorrowedRustVecMut::Signer(mut v) => v.push(ptr::read(e as *const MoveSigner)), - TypedMoveBorrowedRustVecMut::Vector(_t, mut v) => v.push(ptr::read(e as *const MoveUntypedVector)), - TypedMoveBorrowedRustVecMut::Struct(mut s) => s.push(e), - TypedMoveBorrowedRustVecMut::Reference(_t, mut v) => v.push(ptr::read(e as *const MoveUntypedReference)), - } - } - - #[rustfmt::skip] - #[export_name = "move_native_vector_borrow_mut"] - unsafe extern "C" fn borrow_mut<'v>( - type_ve: &'v MoveType, - v: &'v mut MoveUntypedVector, - i: u64 - ) -> &'v mut AnyValue { - let mut rust_vec = borrow_typed_move_vec_as_rust_vec_mut(type_ve, v); - - let i = usize::try_from(i).expect("usize"); - let value = match rust_vec { - TypedMoveBorrowedRustVecMut::Bool(mut v) => mem::transmute(&mut v[i]), - TypedMoveBorrowedRustVecMut::U8(mut v) => mem::transmute(&mut v[i]), - TypedMoveBorrowedRustVecMut::U16(mut v) => mem::transmute(&mut v[i]), - TypedMoveBorrowedRustVecMut::U32(mut v) => mem::transmute(&mut v[i]), - TypedMoveBorrowedRustVecMut::U64(mut v) => mem::transmute(&mut v[i]), - TypedMoveBorrowedRustVecMut::U128(mut v) => mem::transmute(&mut v[i]), - TypedMoveBorrowedRustVecMut::U256(mut v) => mem::transmute(&mut v[i]), - TypedMoveBorrowedRustVecMut::Address(mut v) => mem::transmute(&mut v[i]), - TypedMoveBorrowedRustVecMut::Signer(mut v) => mem::transmute(&mut v[i]), - TypedMoveBorrowedRustVecMut::Vector(_t, mut v) => mem::transmute(&mut v[i]), - TypedMoveBorrowedRustVecMut::Struct(mut s) => s.get_mut(i), - TypedMoveBorrowedRustVecMut::Reference(_t, mut v) => mem::transmute(&mut v[i]), - }; - - value - } - - #[export_name = "move_native_vector_pop_back"] - pub unsafe extern "C" fn pop_back( - type_ve: &MoveType, - v: &mut MoveUntypedVector, - r: *mut AnyValue, - ) { - let mut rust_vec = borrow_typed_move_vec_as_rust_vec_mut(type_ve, v); - - let msg = "popping from empty vec"; - match rust_vec { - TypedMoveBorrowedRustVecMut::Bool(mut v) => { - ptr::write(r as *mut bool, v.pop().expect(msg)); - } - TypedMoveBorrowedRustVecMut::U8(mut v) => { - ptr::write(r as *mut u8, v.pop().expect(msg)); - } - TypedMoveBorrowedRustVecMut::U16(mut v) => { - ptr::write(r as *mut u16, v.pop().expect(msg)); - } - TypedMoveBorrowedRustVecMut::U32(mut v) => { - ptr::write(r as *mut u32, v.pop().expect(msg)); - } - TypedMoveBorrowedRustVecMut::U64(mut v) => { - ptr::write(r as *mut u64, v.pop().expect(msg)); - } - TypedMoveBorrowedRustVecMut::U128(mut v) => { - ptr::write(r as *mut u128, v.pop().expect(msg)); - } - TypedMoveBorrowedRustVecMut::U256(mut v) => { - ptr::write(r as *mut U256, v.pop().expect(msg)); - } - TypedMoveBorrowedRustVecMut::Address(mut v) => { - ptr::write(r as *mut MoveAddress, v.pop().expect(msg)); - } - TypedMoveBorrowedRustVecMut::Signer(mut v) => { - ptr::write(r as *mut MoveSigner, v.pop().expect(msg)); - } - TypedMoveBorrowedRustVecMut::Vector(_t, mut v) => { - ptr::write(r as *mut MoveUntypedVector, v.pop().expect(msg)); - } - TypedMoveBorrowedRustVecMut::Struct(mut s) => s.pop_into(r), - TypedMoveBorrowedRustVecMut::Reference(_t, mut v) => { - ptr::write(r as *mut MoveUntypedReference, v.pop().expect(msg)); - } - } - } - - #[export_name = "move_native_vector_destroy_empty"] - pub unsafe extern "C" fn destroy_empty(type_ve: &MoveType, v: MoveUntypedVector) { - assert_eq!(v.length, 0); - match type_ve.type_desc { - TypeDesc::Bool => drop(move_vec_to_rust_vec::(v)), - TypeDesc::U8 => drop(move_vec_to_rust_vec::(v)), - TypeDesc::U16 => drop(move_vec_to_rust_vec::(v)), - TypeDesc::U32 => drop(move_vec_to_rust_vec::(v)), - TypeDesc::U64 => drop(move_vec_to_rust_vec::(v)), - TypeDesc::U128 => drop(move_vec_to_rust_vec::(v)), - TypeDesc::U256 => drop(move_vec_to_rust_vec::(v)), - TypeDesc::Address => drop(move_vec_to_rust_vec::(v)), - TypeDesc::Signer => drop(move_vec_to_rust_vec::(v)), - TypeDesc::Vector => { - // Safety: need the correct internal pointer alignment to - // deallocate; need the outer vector to be empty to avoid - // dropping the inner vectors. As in `empty`, - // MoveUntypedVector should have the same size/alignment - // regardless of the contained type, so no need to interpret - // the vector type. - drop(move_vec_to_rust_vec::(v)) - } - TypeDesc::Struct => { - // Safety: like in `empty` we want to deallocate here without - // creating a `Vec` of a concrete type, since handling the - // alignment would requiring enumerating many types. - // - // So here we're just going to free the pointer ourselves, - // constructing a correct `Layout` value to pass to the - // allocator. - // - // Note that this function can only be called on empty vecs, - // so we don't need to care about dropping elements. - - let size = (*type_ve.type_info).struct_.size; - let size = usize::try_from(size).expect("overflow"); - let alignment = (*type_ve.type_info).struct_.alignment; - let alignment = usize::try_from(alignment).expect("overflow"); - let capacity = usize::try_from(v.capacity).expect("overflow"); - - assert!(size != 0); // can't handle ZSTs - - if capacity != 0 { - let vec_byte_size = capacity.checked_mul(size).expect("overflow"); - let layout = - alloc::alloc::Layout::from_size_align(vec_byte_size, alignment) - .expect("bad size or alignment"); - alloc::alloc::dealloc(v.ptr, layout); - } - - disarm_drop_bomb(v); - } - TypeDesc::Reference => drop(move_vec_to_rust_vec::(v)), - } - } - - #[export_name = "move_native_vector_swap"] - unsafe extern "C" fn swap(type_ve: &MoveType, v: &mut MoveUntypedVector, i: u64, j: u64) { - let i = usize::try_from(i).expect("usize"); - let j = usize::try_from(j).expect("usize"); - - let mut rust_vec = borrow_typed_move_vec_as_rust_vec_mut(type_ve, v); - - match rust_vec { - TypedMoveBorrowedRustVecMut::Bool(mut v) => v.swap(i, j), - TypedMoveBorrowedRustVecMut::U8(mut v) => v.swap(i, j), - TypedMoveBorrowedRustVecMut::U16(mut v) => v.swap(i, j), - TypedMoveBorrowedRustVecMut::U32(mut v) => v.swap(i, j), - TypedMoveBorrowedRustVecMut::U64(mut v) => v.swap(i, j), - TypedMoveBorrowedRustVecMut::U128(mut v) => v.swap(i, j), - TypedMoveBorrowedRustVecMut::U256(mut v) => v.swap(i, j), - TypedMoveBorrowedRustVecMut::Address(mut v) => v.swap(i, j), - TypedMoveBorrowedRustVecMut::Signer(mut v) => v.swap(i, j), - TypedMoveBorrowedRustVecMut::Vector(_t, mut v) => v.swap(i, j), - TypedMoveBorrowedRustVecMut::Struct(mut v) => v.swap(i, j), - TypedMoveBorrowedRustVecMut::Reference(_t, mut v) => v.swap(i, j), - } - } - } -} +mod std; /// Conversion from move types to rust types. -pub(crate) mod conv { - use crate::rt_types::*; - use alloc::vec::Vec; - use core::marker::PhantomData; - use core::mem; - use core::ops::{Deref, DerefMut}; - use core::ptr; - use core::slice; - use ethnum::U256; - - /// This is a placeholder for the unstable `ptr::invalid_mut`. - /// - /// It is a potential future way to create invalid pointers, which is - /// required for correctly initializing empty vectors. - /// - /// This crate initializes empty vectors knowing only the alignment of their - /// elements, but not the full type. - pub const fn invalid_mut(addr: usize) -> *mut T { - // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. - // We use transmute rather than a cast so tools like Miri can tell that this - // is *not* the same as from_exposed_addr. - // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that - // pointer). - unsafe { mem::transmute(addr) } - } - - pub unsafe fn move_byte_vec_to_rust_vec(mv: MoveByteVector) -> Vec { - let ret = MoveUntypedVector { - ptr: mv.ptr, - capacity: mv.capacity, - length: mv.length, - }; - disarm_drop_bomb(mv); - move_vec_to_rust_vec(ret) - } - - pub fn rust_vec_to_move_byte_vec(rv: Vec) -> MoveByteVector { - let mv = rust_vec_to_move_vec(rv); - let r = MoveByteVector { - ptr: mv.ptr, - capacity: mv.capacity, - length: mv.length, - }; - disarm_drop_bomb(mv); - r - } - - pub fn borrow_move_byte_vec_as_rust_vec<'mv>( - mv: &'mv MoveByteVector, - ) -> MoveBorrowedRustVec<'mv, u8> { - assert_eq!( - mem::size_of::(), - mem::size_of::() - ); - assert_eq!( - mem::align_of::(), - mem::align_of::() - ); - // Safety: both repr(c) with same layout, probably ok - let mv: &'mv MoveUntypedVector = unsafe { mem::transmute(mv) }; - unsafe { borrow_move_vec_as_rust_vec(mv) } - } - - pub unsafe fn move_vec_to_rust_vec(mv: MoveUntypedVector) -> Vec { - let rv = Vec::from_raw_parts( - mv.ptr as *mut T, - usize::try_from(mv.length).expect("overflow"), - usize::try_from(mv.capacity).expect("overflow"), - ); - disarm_drop_bomb(mv); - rv - } - - pub fn rust_vec_to_move_vec(mut rv: Vec) -> MoveUntypedVector { - let mv = MoveUntypedVector { - ptr: rv.as_mut_ptr() as *mut u8, - capacity: u64::try_from(rv.capacity()).expect("overflow"), - length: u64::try_from(rv.len()).expect("overflow"), - }; - mem::forget(rv); - mv - } - - pub unsafe fn borrow_move_vec_as_rust_vec<'mv, T>( - mv: &'mv MoveUntypedVector, - ) -> MoveBorrowedRustVec<'mv, T> { - let rv = Vec::from_raw_parts( - mv.ptr as *mut T, - usize::try_from(mv.length).expect("overflow"), - usize::try_from(mv.capacity).expect("overflow"), - ); - MoveBorrowedRustVec { - inner: rv, - _lifetime: PhantomData, - } - } - - pub unsafe fn borrow_move_vec_as_rust_vec_mut<'mv, T>( - mv: &'mv mut MoveUntypedVector, - ) -> MoveBorrowedRustVecMut<'mv, T> { - let rv = Vec::from_raw_parts( - mv.ptr as *mut T, - usize::try_from(mv.length).expect("overflow"), - usize::try_from(mv.capacity).expect("overflow"), - ); - MoveBorrowedRustVecMut { - inner: rv, - original: mv, - } - } - - pub struct MoveBorrowedRustVec<'mv, T> { - inner: Vec, - _lifetime: PhantomData<&'mv ()>, - } - - #[derive(Debug)] - pub struct MoveBorrowedRustVecMut<'mv, T> { - inner: Vec, - original: &'mv mut MoveUntypedVector, - } - - impl<'mv, T> Drop for MoveBorrowedRustVec<'mv, T> { - fn drop(&mut self) { - let rv = mem::replace(&mut self.inner, Vec::new()); - mem::forget(rv); - } - } - - impl<'mv, T> Drop for MoveBorrowedRustVecMut<'mv, T> { - fn drop(&mut self) { - let mut rv = mem::replace(&mut self.inner, Vec::new()); - - self.original.length = u64::try_from(rv.len()).expect("overflow"); - self.original.capacity = u64::try_from(rv.capacity()).expect("overflow"); - self.original.ptr = rv.as_mut_ptr() as *mut u8; - - mem::forget(rv); - } - } - - impl<'mv, T> Deref for MoveBorrowedRustVec<'mv, T> { - type Target = Vec; - - fn deref(&self) -> &Self::Target { - &self.inner - } - } - - impl<'mv, T> Deref for MoveBorrowedRustVecMut<'mv, T> { - type Target = Vec; - - fn deref(&self) -> &Self::Target { - &self.inner - } - } - - impl<'mv, T> DerefMut for MoveBorrowedRustVecMut<'mv, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner - } - } - - /// A vector of Move structs. - /// - /// Since we can't instantiate Move structs as Rust structs, this is a - /// container that unsafely implements exactly the ops needed to deal with - /// Move's `vector`. - #[derive(Debug)] - pub struct MoveBorrowedRustVecOfStruct<'mv> { - pub inner: &'mv MoveUntypedVector, - pub name: StaticTypeName, - pub type_: &'mv StructTypeInfo, - } - - #[derive(Debug)] - pub struct MoveBorrowedRustVecOfStructMut<'mv> { - pub inner: &'mv mut MoveUntypedVector, - pub name: StaticTypeName, - pub type_: &'mv StructTypeInfo, - } - - impl<'mv> MoveBorrowedRustVecOfStruct<'mv> { - pub unsafe fn iter<'s>(&'s self) -> impl Iterator { - let struct_size = usize::try_from(self.type_.size).expect("overflow"); - let vec_len = usize::try_from(self.inner.length).expect("overflow"); - (0..vec_len).map(move |i| { - let base_ptr = self.inner.ptr; - let offset = i.checked_mul(struct_size).expect("overflow"); - let offset = isize::try_from(offset).expect("overflow"); - let element_ptr = base_ptr.offset(offset); - let element_ref = &*(element_ptr as *const AnyValue); - element_ref - }) - } - - pub unsafe fn get(&self, i: usize) -> &'mv AnyValue { - let struct_size = usize::try_from(self.type_.size).expect("overflow"); - let vec_len = usize::try_from(self.inner.length).expect("overflow"); - - if i >= vec_len { - panic!("index out of bounds"); - } - - let base_ptr = self.inner.ptr; - let offset = i.checked_mul(struct_size).expect("overflow"); - let offset = isize::try_from(offset).expect("overflow"); - let element_ptr = base_ptr.offset(offset); - let element_ref = &*(element_ptr as *const AnyValue); - element_ref - } - } - - impl<'mv> MoveBorrowedRustVecOfStructMut<'mv> { - pub unsafe fn get_mut(&mut self, i: usize) -> &'mv mut AnyValue { - let struct_size = usize::try_from(self.type_.size).expect("overflow"); - let vec_len = usize::try_from(self.inner.length).expect("overflow"); - - if i >= vec_len { - panic!("index out of bounds"); - } - - let base_ptr = self.inner.ptr; - let offset = i.checked_mul(struct_size).expect("overflow"); - let offset = isize::try_from(offset).expect("overflow"); - let element_ptr = base_ptr.offset(offset); - let element_ref = &mut *(element_ptr as *mut AnyValue); - element_ref - } - - /// Get a pointer to a possibly-uninitialized element. - pub unsafe fn get_mut_unchecked_raw(&mut self, i: usize) -> *mut AnyValue { - let struct_size = usize::try_from(self.type_.size).expect("overflow"); - let vec_capacity = usize::try_from(self.inner.capacity).expect("overflow"); - - if i >= vec_capacity { - panic!("index out of bounds"); - } - - let base_ptr = self.inner.ptr; - let offset = i.checked_mul(struct_size).expect("overflow"); - let offset = isize::try_from(offset).expect("overflow"); - let element_ptr = base_ptr.offset(offset); - let element_ptr = element_ptr as *mut AnyValue; - element_ptr - } - - pub unsafe fn set_length(&mut self, len: usize) { - let vec_capacity = usize::try_from(self.inner.capacity).expect("overflow"); - - if len > vec_capacity { - panic!("index greater than capacity"); - } - - let len = u64::try_from(len).expect("overflow"); - self.inner.length = len; - } - - pub unsafe fn push(&mut self, ptr: *mut AnyValue) { - self.maybe_grow(); - - let struct_size = usize::try_from(self.type_.size).expect("overflow"); - let vec_len = usize::try_from(self.inner.length).expect("overflow"); - let vec_cap = usize::try_from(self.inner.capacity).expect("overflow"); - - assert!(vec_len < vec_cap); - - let i = vec_len; - - let base_ptr = self.inner.ptr; - let offset = i.checked_mul(struct_size).expect("overflow"); - let offset = isize::try_from(offset).expect("overflow"); - let element_ptr = base_ptr.offset(offset); - - let src_ptr = ptr as *mut u8; - ptr::copy_nonoverlapping(src_ptr, element_ptr, struct_size); - - self.inner.length = self.inner.length.checked_add(1).expect("overflow"); - } - - pub unsafe fn maybe_grow(&mut self) { - let struct_size = usize::try_from(self.type_.size).expect("overflow"); - let vec_len = usize::try_from(self.inner.length).expect("overflow"); - let vec_cap = usize::try_from(self.inner.capacity).expect("overflow"); - - if vec_len < vec_cap { - return; - } - - assert_eq!(vec_len, vec_cap); - - self.grow_amortized(); - } - - /// This is approximately like `RawVec::grow_amortized`. - /// - /// It always produces a power-of-two capacity. - #[cold] - pub unsafe fn grow_amortized(&mut self) { - let struct_size = usize::try_from(self.type_.size).expect("overflow"); - let struct_align = usize::try_from(self.type_.alignment).expect("overflow"); - let vec_len = usize::try_from(self.inner.length).expect("overflow"); - let vec_cap = usize::try_from(self.inner.capacity).expect("overflow"); - - assert_eq!(vec_len, vec_cap); - - // Same as RawVec - let min_non_zero_cap = if struct_size == 1 { - 8 - } else if struct_size <= 1024 { - 4 - } else { - 1 - }; - - let new_cap = vec_cap.checked_mul(2).expect("overflow"); - let new_cap = core::cmp::max(new_cap, min_non_zero_cap); - - self.reserve_exact(new_cap); - } - - pub unsafe fn reserve_exact(&mut self, new_cap: usize) { - let struct_size = usize::try_from(self.type_.size).expect("overflow"); - let struct_align = usize::try_from(self.type_.alignment).expect("overflow"); - let vec_len = usize::try_from(self.inner.length).expect("overflow"); - let vec_cap = usize::try_from(self.inner.capacity).expect("overflow"); - let new_cap_u64 = u64::try_from(new_cap).expect("overflow"); - - assert!(struct_size != 0); // can't handle ZSTs - assert!(new_cap >= vec_cap); - - let old_vec_byte_size = vec_cap.checked_mul(struct_size).expect("overflow"); - let new_vec_byte_size = new_cap.checked_mul(struct_size).expect("overflow"); - let new_layout = alloc::alloc::Layout::from_size_align(new_vec_byte_size, struct_align) - .expect("bad size or alignment"); - - if vec_cap == 0 { - let new_ptr = alloc::alloc::alloc(new_layout); - if new_ptr.is_null() { - alloc::alloc::handle_alloc_error(new_layout); - } - self.inner.ptr = new_ptr; - self.inner.capacity = new_cap_u64; - } else { - let old_layout = - alloc::alloc::Layout::from_size_align(old_vec_byte_size, struct_align) - .expect("bad size or alignment"); - - let new_ptr = alloc::alloc::realloc(self.inner.ptr, old_layout, new_vec_byte_size); - if new_ptr.is_null() { - alloc::alloc::handle_alloc_error(new_layout); - } - self.inner.ptr = new_ptr; - self.inner.capacity = new_cap_u64; - } - } - - pub unsafe fn pop_into(&mut self, ptr: *mut AnyValue) { - let struct_size = usize::try_from(self.type_.size).expect("overflow"); - let vec_len = usize::try_from(self.inner.length).expect("overflow"); - - let i = vec_len.checked_sub(1).expect("popping empty vector"); - - let base_ptr = self.inner.ptr; - let offset = i.checked_mul(struct_size).expect("overflow"); - let offset = isize::try_from(offset).expect("overflow"); - let element_ptr = base_ptr.offset(offset); - - let dest_ptr = ptr as *mut u8; - ptr::copy_nonoverlapping(element_ptr, dest_ptr, struct_size); - - self.inner.length = self.inner.length.checked_sub(1).expect("overflow"); - } - - pub unsafe fn swap(&mut self, i: usize, j: usize) { - let struct_size = usize::try_from(self.type_.size).expect("overflow"); - let vec_len = usize::try_from(self.inner.length).expect("overflow"); - - if i >= vec_len || j >= vec_len { - panic!("index out of bounds"); - } - - // Safety: must avoid overlapping pointers in swap_nonoverlapping - // below. - if i == j { - return; - } - - let base_ptr = self.inner.ptr; - - let i_offset = i.checked_mul(struct_size).expect("overflow"); - let i_offset = isize::try_from(i_offset).expect("overflow"); - let i_element_ptr = base_ptr.offset(i_offset); - let j_offset = j.checked_mul(struct_size).expect("overflow"); - let j_offset = isize::try_from(j_offset).expect("overflow"); - let j_element_ptr = base_ptr.offset(j_offset); - - // Safety: because of the presense of uninitialized padding bytes, - // we must (I think) do this swap with raw pointers, not slices. - ptr::swap_nonoverlapping(i_element_ptr, j_element_ptr, struct_size); - } - } - - pub enum BorrowedTypedMoveValue<'mv> { - Bool(&'mv bool), - U8(&'mv u8), - U16(&'mv u16), - U32(&'mv u32), - U64(&'mv u64), - U128(&'mv u128), - U256(&'mv U256), - Address(&'mv MoveAddress), - Signer(&'mv MoveSigner), - Vector(MoveType, &'mv MoveUntypedVector), - Struct(MoveType, &'mv AnyValue), - Reference(MoveType, &'mv MoveUntypedReference), - // todo - } - - pub unsafe fn borrow_move_value_as_rust_value<'mv>( - type_: &MoveType, - value: &'mv AnyValue, - ) -> BorrowedTypedMoveValue<'mv> { - // todo need to think about the soundness of this transmute - match type_.type_desc { - TypeDesc::Bool => BorrowedTypedMoveValue::Bool(mem::transmute(value)), - TypeDesc::U8 => BorrowedTypedMoveValue::U8(mem::transmute(value)), - TypeDesc::U16 => BorrowedTypedMoveValue::U16(mem::transmute(value)), - TypeDesc::U32 => BorrowedTypedMoveValue::U32(mem::transmute(value)), - TypeDesc::U64 => BorrowedTypedMoveValue::U64(mem::transmute(value)), - TypeDesc::U128 => BorrowedTypedMoveValue::U128(mem::transmute(value)), - TypeDesc::U256 => BorrowedTypedMoveValue::U256(mem::transmute(value)), - TypeDesc::Address => BorrowedTypedMoveValue::Address(mem::transmute(value)), - TypeDesc::Signer => BorrowedTypedMoveValue::Signer(mem::transmute(value)), - TypeDesc::Vector => { - let element_type = *(*type_.type_info).vector.element_type; - let move_ref = mem::transmute(value); - BorrowedTypedMoveValue::Vector(element_type, move_ref) - } - TypeDesc::Struct => { - // Previously we stored the StructTypeInfo here. But passing the enclosing - // MoveType instead gives routines access to the struct name (i.e., more - // context). Otherwise we would need an uplevel pointer in StructTypeInfo or - // to redundantly store the name there. - BorrowedTypedMoveValue::Struct(*type_, value) - } - TypeDesc::Reference => { - let element_type = *(*type_.type_info).reference.element_type; - let move_ref = mem::transmute(value); - BorrowedTypedMoveValue::Reference(element_type, move_ref) - } - } - } - - /// The same as `BorrowedTypedMoveValue` but with raw pointers. - /// - /// Allows for uninitialized values. - pub enum RawBorrowedTypedMoveValue { - Bool(*mut bool), - U8(*mut u8), - U16(*mut u16), - U32(*mut u32), - U64(*mut u64), - U128(*mut u128), - U256(*mut U256), - Address(*mut MoveAddress), - Signer(*mut MoveSigner), - Vector(MoveType, *mut MoveUntypedVector), - Struct(MoveType, *mut AnyValue), - Reference(MoveType, *mut MoveUntypedReference), - } - - pub unsafe fn raw_borrow_move_value_as_rust_value( - type_: &MoveType, - value: *mut AnyValue, - ) -> RawBorrowedTypedMoveValue { - match type_.type_desc { - TypeDesc::Bool => RawBorrowedTypedMoveValue::Bool(mem::transmute(value)), - TypeDesc::U8 => RawBorrowedTypedMoveValue::U8(mem::transmute(value)), - TypeDesc::U16 => RawBorrowedTypedMoveValue::U16(mem::transmute(value)), - TypeDesc::U32 => RawBorrowedTypedMoveValue::U32(mem::transmute(value)), - TypeDesc::U64 => RawBorrowedTypedMoveValue::U64(mem::transmute(value)), - TypeDesc::U128 => RawBorrowedTypedMoveValue::U128(mem::transmute(value)), - TypeDesc::U256 => RawBorrowedTypedMoveValue::U256(mem::transmute(value)), - TypeDesc::Address => RawBorrowedTypedMoveValue::Address(mem::transmute(value)), - TypeDesc::Signer => RawBorrowedTypedMoveValue::Signer(mem::transmute(value)), - TypeDesc::Vector => { - let element_type = *(*type_.type_info).vector.element_type; - let move_ref = mem::transmute(value); - RawBorrowedTypedMoveValue::Vector(element_type, move_ref) - } - TypeDesc::Struct => { - RawBorrowedTypedMoveValue::Struct(*type_, value) - } - TypeDesc::Reference => { - let element_type = *(*type_.type_info).reference.element_type; - let move_ref = mem::transmute(value); - RawBorrowedTypedMoveValue::Reference(element_type, move_ref) - } - } - } - - pub enum TypedMoveBorrowedRustVec<'mv> { - Bool(MoveBorrowedRustVec<'mv, bool>), - U8(MoveBorrowedRustVec<'mv, u8>), - U16(MoveBorrowedRustVec<'mv, u16>), - U32(MoveBorrowedRustVec<'mv, u32>), - U64(MoveBorrowedRustVec<'mv, u64>), - U128(MoveBorrowedRustVec<'mv, u128>), - U256(MoveBorrowedRustVec<'mv, U256>), - Address(MoveBorrowedRustVec<'mv, MoveAddress>), - Signer(MoveBorrowedRustVec<'mv, MoveSigner>), - Vector(MoveType, MoveBorrowedRustVec<'mv, MoveUntypedVector>), - Struct(MoveBorrowedRustVecOfStruct<'mv>), - Reference(MoveType, MoveBorrowedRustVec<'mv, MoveUntypedReference>), - // todo - } - - #[derive(Debug)] - pub enum TypedMoveBorrowedRustVecMut<'mv> { - Bool(MoveBorrowedRustVecMut<'mv, bool>), - U8(MoveBorrowedRustVecMut<'mv, u8>), - U16(MoveBorrowedRustVecMut<'mv, u16>), - U32(MoveBorrowedRustVecMut<'mv, u32>), - U64(MoveBorrowedRustVecMut<'mv, u64>), - U128(MoveBorrowedRustVecMut<'mv, u128>), - U256(MoveBorrowedRustVecMut<'mv, U256>), - Address(MoveBorrowedRustVecMut<'mv, MoveAddress>), - Signer(MoveBorrowedRustVecMut<'mv, MoveSigner>), - Vector(MoveType, MoveBorrowedRustVecMut<'mv, MoveUntypedVector>), - Struct(MoveBorrowedRustVecOfStructMut<'mv>), - Reference(MoveType, MoveBorrowedRustVecMut<'mv, MoveUntypedReference>), - // todo - } - - #[rustfmt::skip] - pub unsafe fn borrow_typed_move_vec_as_rust_vec<'mv>( - type_: &'mv MoveType, - mv: &'mv MoveUntypedVector, - ) -> TypedMoveBorrowedRustVec<'mv> { - match type_.type_desc { - TypeDesc::Bool => { - TypedMoveBorrowedRustVec::Bool(borrow_move_vec_as_rust_vec::(mv)) - } - TypeDesc::U8 => { - TypedMoveBorrowedRustVec::U8(borrow_move_vec_as_rust_vec::(mv)) - } - TypeDesc::U16 => { - TypedMoveBorrowedRustVec::U16(borrow_move_vec_as_rust_vec::(mv)) - } - TypeDesc::U32 => { - TypedMoveBorrowedRustVec::U32(borrow_move_vec_as_rust_vec::(mv)) - } - TypeDesc::U64 => { - TypedMoveBorrowedRustVec::U64(borrow_move_vec_as_rust_vec::(mv)) - } - TypeDesc::U128 => { - TypedMoveBorrowedRustVec::U128(borrow_move_vec_as_rust_vec::(mv)) - } - TypeDesc::U256 => { - TypedMoveBorrowedRustVec::U256(borrow_move_vec_as_rust_vec::(mv)) - } - TypeDesc::Address => { - TypedMoveBorrowedRustVec::Address(borrow_move_vec_as_rust_vec::(mv)) - } - TypeDesc::Signer => { - TypedMoveBorrowedRustVec::Signer(borrow_move_vec_as_rust_vec::(mv)) - } - TypeDesc::Vector => { - TypedMoveBorrowedRustVec::Vector( - *(*type_.type_info).vector.element_type, - borrow_move_vec_as_rust_vec::(mv), - ) - } - TypeDesc::Struct => { - TypedMoveBorrowedRustVec::Struct( - MoveBorrowedRustVecOfStruct { - inner: mv, - name: type_.name, - type_: &(*type_.type_info).struct_, - } - ) - } - TypeDesc::Reference => { - TypedMoveBorrowedRustVec::Reference( - *(*type_.type_info).reference.element_type, - borrow_move_vec_as_rust_vec::(mv), - ) - } - } - } - - #[rustfmt::skip] - pub unsafe fn borrow_typed_move_vec_as_rust_vec_mut<'mv>( - type_: &'mv MoveType, - mv: &'mv mut MoveUntypedVector, - ) -> TypedMoveBorrowedRustVecMut<'mv> { - match type_.type_desc { - TypeDesc::Bool => { - TypedMoveBorrowedRustVecMut::Bool(borrow_move_vec_as_rust_vec_mut::(mv)) - } - TypeDesc::U8 => { - TypedMoveBorrowedRustVecMut::U8(borrow_move_vec_as_rust_vec_mut::(mv)) - } - TypeDesc::U16 => { - TypedMoveBorrowedRustVecMut::U16(borrow_move_vec_as_rust_vec_mut::(mv)) - } - TypeDesc::U32 => { - TypedMoveBorrowedRustVecMut::U32(borrow_move_vec_as_rust_vec_mut::(mv)) - } - TypeDesc::U64 => { - TypedMoveBorrowedRustVecMut::U64(borrow_move_vec_as_rust_vec_mut::(mv)) - } - TypeDesc::U128 => { - TypedMoveBorrowedRustVecMut::U128(borrow_move_vec_as_rust_vec_mut::(mv)) - } - TypeDesc::U256 => { - TypedMoveBorrowedRustVecMut::U256(borrow_move_vec_as_rust_vec_mut::(mv)) - } - TypeDesc::Address => { - TypedMoveBorrowedRustVecMut::Address(borrow_move_vec_as_rust_vec_mut::(mv)) - } - TypeDesc::Signer => { - TypedMoveBorrowedRustVecMut::Signer(borrow_move_vec_as_rust_vec_mut::(mv)) - } - TypeDesc::Vector => { - TypedMoveBorrowedRustVecMut::Vector( - *(*type_.type_info).vector.element_type, - borrow_move_vec_as_rust_vec_mut::(mv), - ) - } - TypeDesc::Struct => { - TypedMoveBorrowedRustVecMut::Struct( - MoveBorrowedRustVecOfStructMut { - inner: mv, - name: type_.name, - type_: &(*type_.type_info).struct_, - } - ) - } - TypeDesc::Reference => { - TypedMoveBorrowedRustVecMut::Reference( - *(*type_.type_info).reference.element_type, - borrow_move_vec_as_rust_vec_mut::(mv), - ) - } - } - } - - pub unsafe fn walk_struct_fields<'mv>( - info: &'mv StructTypeInfo, - struct_ref: &'mv AnyValue, - ) -> impl Iterator { - let field_len = usize::try_from(info.field_array_len).expect("overflow"); - let fields: &'mv [StructFieldInfo] = slice::from_raw_parts(info.field_array_ptr, field_len); - - fields.iter().map(|field| { - let struct_base_ptr: *const AnyValue = struct_ref as _; - let field_offset = isize::try_from(field.offset).expect("overflow"); - let field_ptr = struct_base_ptr.offset(field_offset); - let field_ref: &'mv AnyValue = &*field_ptr; - (&field.type_, field_ref, &field.name) - }) - } - - pub unsafe fn walk_struct_fields_mut<'mv>( - info: &'mv StructTypeInfo, - struct_ref: *mut AnyValue, - ) -> impl Iterator { - let field_len = usize::try_from(info.field_array_len).expect("overflow"); - let fields: &'mv [StructFieldInfo] = slice::from_raw_parts(info.field_array_ptr, field_len); - - fields.iter().map(move |field| { - let struct_base_ptr: *mut AnyValue = struct_ref as _; - let field_offset = isize::try_from(field.offset).expect("overflow"); - let field_ptr = struct_base_ptr.offset(field_offset); - (&field.type_, field_ptr, &field.name) - }) - } - - impl<'mv> core::fmt::Debug for BorrowedTypedMoveValue<'mv> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - BorrowedTypedMoveValue::Bool(v) => v.fmt(f), - BorrowedTypedMoveValue::U8(v) => v.fmt(f), - BorrowedTypedMoveValue::U16(v) => v.fmt(f), - BorrowedTypedMoveValue::U32(v) => v.fmt(f), - BorrowedTypedMoveValue::U64(v) => v.fmt(f), - BorrowedTypedMoveValue::U128(v) => v.fmt(f), - BorrowedTypedMoveValue::U256(v) => v.fmt(f), - BorrowedTypedMoveValue::Address(v) => v.fmt(f), - BorrowedTypedMoveValue::Signer(v) => v.fmt(f), - BorrowedTypedMoveValue::Vector(t, v) => unsafe { - let rv = borrow_typed_move_vec_as_rust_vec(t, v); - rv.fmt(f) - }, - BorrowedTypedMoveValue::Struct(t, v) => unsafe { - let st = (*(t.type_info)).struct_; - write!(f, "{} {{ ", t.name.as_ascii_str()); - let fields = walk_struct_fields(&st, v); - for (type_, ref_, fld_name) in fields { - let rv = borrow_move_value_as_rust_value(type_, ref_); - write!(f, "{}: ", fld_name.as_ascii_str()); - rv.fmt(f); - f.write_str(", "); - } - f.write_str("}"); - Ok(()) - }, - BorrowedTypedMoveValue::Reference(t, v) => unsafe { - let rv = borrow_move_value_as_rust_value(t, &*v.0); - rv.fmt(f) - }, - } - } - } - - impl<'mv> core::fmt::Debug for TypedMoveBorrowedRustVec<'mv> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - TypedMoveBorrowedRustVec::Bool(v) => v.fmt(f), - TypedMoveBorrowedRustVec::U8(v) => v.fmt(f), - TypedMoveBorrowedRustVec::U16(v) => v.fmt(f), - TypedMoveBorrowedRustVec::U32(v) => v.fmt(f), - TypedMoveBorrowedRustVec::U64(v) => v.fmt(f), - TypedMoveBorrowedRustVec::U128(v) => v.fmt(f), - TypedMoveBorrowedRustVec::U256(v) => v.fmt(f), - TypedMoveBorrowedRustVec::Address(v) => v.fmt(f), - TypedMoveBorrowedRustVec::Signer(v) => v.fmt(f), - TypedMoveBorrowedRustVec::Vector(t, v) => { - let mut dbg = f.debug_list(); - for e in v.iter() { - unsafe { - let e = borrow_typed_move_vec_as_rust_vec(t, e); - dbg.entry(&e); - } - } - dbg.finish() - } - TypedMoveBorrowedRustVec::Struct(s) => { - f.write_str("["); - unsafe { - for vref in s.iter() { - let type_ = MoveType { - name: s.name, - type_desc: TypeDesc::Struct, - type_info: &TypeInfo { struct_: *s.type_ }, - }; - let e = borrow_move_value_as_rust_value(&type_, vref); - e.fmt(f); - f.write_str(", "); - } - } - f.write_str("]"); - Ok(()) - } - TypedMoveBorrowedRustVec::Reference(t, v) => { - let mut dbg = f.debug_list(); - for e in v.iter() { - unsafe { - let e = borrow_move_value_as_rust_value(t, &*e.0); - dbg.entry(&e); - } - } - dbg.finish() - } - } - } - } -} +mod conv; /// Compatibility with the target platform, e.g. Solana. -#[cfg(not(feature = "solana"))] -pub(crate) mod target_defs { - // Move addresses are 16 bytes by default, but can be made 20 or 32 at compile time. - pub const ACCOUNT_ADDRESS_LENGTH: usize = 16; - - pub fn print_string(s: &str) { - todo!() - } - - pub fn print_stack_trace() { - todo!() - } - - pub fn abort(code: u64) -> ! { - todo!() - } -} - -#[cfg(feature = "solana")] -pub(crate) mod target_defs { - // Solana pubkeys are 32 bytes. - // Move addresses are 16 bytes by default, but can be made 20 or 32 at compile time. - pub const ACCOUNT_ADDRESS_LENGTH: usize = 32; - - pub fn print_string(s: &str) { - unsafe { - syscalls::sol_log_(s.as_ptr(), s.len() as u64); - } - } - - pub fn print_stack_trace() { - todo!() - } - - pub fn abort(code: u64) -> ! { - unsafe { - syscalls::sol_log_64_( - code, code, code, code, code, - ); - syscalls::abort() - } - } - - // NB: not using the "static-syscalls" sbf feature - mod syscalls { - extern "C" { - pub fn abort() -> !; - pub fn sol_log_(msg: *const u8, len: u64); - pub fn sol_log_64_(_: u64, _: u64, _: u64, _: u64, _: u64); - } - } - - mod globals { - use alloc::alloc::{GlobalAlloc, Layout}; - use alloc::format; - use core::mem::size_of; - use core::ptr::null_mut; - - const PANIC_ABORT_CODE: u64 = 101; - - #[panic_handler] - fn panic(info: &core::panic::PanicInfo) -> ! { - super::print_string(&format!("{}", info)); - super::abort(PANIC_ABORT_CODE); - } - - #[global_allocator] - static A: BumpAllocator = BumpAllocator { - start: HEAP_START_ADDRESS as usize, - len: HEAP_LENGTH, - }; - - pub struct BumpAllocator { - pub start: usize, - pub len: usize, - } - - unsafe impl GlobalAlloc for BumpAllocator { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - let pos_ptr = self.start as *mut usize; - - let mut pos = *pos_ptr; - if pos == 0 { - // First time, set starting position - pos = self.start + self.len; - } - pos = pos.saturating_sub(layout.size()); - pos &= !(layout.align().wrapping_sub(1)); - if pos < self.start + size_of::<*mut u8>() { - return null_mut(); - } - *pos_ptr = pos; - pos as *mut u8 - } - #[inline] - unsafe fn dealloc(&self, _: *mut u8, _: Layout) { - // I'm a bump allocator, I don't free - } - } - pub const HEAP_START_ADDRESS: u64 = 0x300000000; - pub const HEAP_LENGTH: usize = 32 * 1024; - } -} +mod target_defs; #[cfg(test)] mod tests; diff --git a/language/move-native/src/rt.rs b/language/move-native/src/rt.rs new file mode 100644 index 0000000000..b593214c6d --- /dev/null +++ b/language/move-native/src/rt.rs @@ -0,0 +1,194 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +use crate::rt_types::{AnyValue, MoveType, MoveUntypedVector, MoveAddress, MoveSigner}; + +#[export_name = "move_rt_abort"] +fn abort(code: u64) -> ! { + crate::target_defs::abort(code); +} + +#[export_name = "move_rt_vec_destroy"] +unsafe fn vec_destroy(type_ve: &MoveType, v: MoveUntypedVector) { + assert_eq!(0, v.length, "can't destroy vectors with elements yet"); + crate::std::vector::destroy_empty(type_ve, v); +} + +#[export_name = "move_rt_vec_empty"] +unsafe fn vec_empty(type_ve: &MoveType) -> MoveUntypedVector { + crate::std::vector::empty(type_ve) +} + +#[export_name = "move_rt_vec_copy"] +unsafe fn vec_copy(type_ve: &MoveType, dstv: &mut MoveUntypedVector, srcv: &MoveUntypedVector) { + use crate::std::vector as V; + let src_len = V::length(type_ve, srcv); + let dst_len = V::length(type_ve, dstv); + + // Drain the destination first. + for i in 0..dst_len { + crate::rt::pop_back_discard(type_ve, dstv); + } + + // Now copy. + for i in 0..src_len { + let se = V::borrow(type_ve, srcv, i); + let septr = se as *const AnyValue as *mut AnyValue; + V::push_back(type_ve, dstv, septr); + } +} + +unsafe fn pop_back_discard( + type_ve: &MoveType, + v: &mut MoveUntypedVector, +) { + use crate::conv::{TypedMoveBorrowedRustVecMut, borrow_typed_move_vec_as_rust_vec_mut}; + let mut rust_vec = borrow_typed_move_vec_as_rust_vec_mut(type_ve, v); + + let msg = "popping from empty vec"; + match rust_vec { + TypedMoveBorrowedRustVecMut::Bool(mut v) => { v.pop().expect(msg); } + TypedMoveBorrowedRustVecMut::U8(mut v) => { v.pop().expect(msg); } + TypedMoveBorrowedRustVecMut::U16(mut v) => { v.pop().expect(msg); } + TypedMoveBorrowedRustVecMut::U32(mut v) => { v.pop().expect(msg); } + TypedMoveBorrowedRustVecMut::U64(mut v) => { v.pop().expect(msg); } + TypedMoveBorrowedRustVecMut::U128(mut v) => { v.pop().expect(msg); } + TypedMoveBorrowedRustVecMut::U256(mut v) => { v.pop().expect(msg); } + TypedMoveBorrowedRustVecMut::Address(mut v) => { v.pop().expect(msg);} + TypedMoveBorrowedRustVecMut::Signer(mut v) => { v.pop().expect(msg); } + TypedMoveBorrowedRustVecMut::Vector(_t, mut v) => { v.pop().expect(msg); } + TypedMoveBorrowedRustVecMut::Struct(mut v) => { todo!(); } + TypedMoveBorrowedRustVecMut::Reference(_t, mut v) => { v.pop().expect(msg); } + }; +} + +#[export_name = "move_rt_vec_cmp_eq"] +unsafe fn vec_cmp_eq(type_ve: &MoveType, v1: &MoveUntypedVector, v2: &MoveUntypedVector) -> bool { + use crate::conv::borrow_move_vec_as_rust_vec; + use ethnum::U256; + use crate::rt_types::TypeDesc; + use crate::std::vector as V; + use core::ops::Deref; + + let v1_len = V::length(type_ve, v1); + let v2_len = V::length(type_ve, v2); + + if v1_len != v2_len { + return false; + } + + let is_eq = match type_ve.type_desc { + TypeDesc::Bool => { + let mut rv1 = borrow_move_vec_as_rust_vec::(v1); + let mut rv2 = borrow_move_vec_as_rust_vec::(v2); + rv1.deref().eq(rv2.deref()) + } + TypeDesc::U8 => { + let mut rv1 = borrow_move_vec_as_rust_vec::(v1); + let mut rv2 = borrow_move_vec_as_rust_vec::(v2); + rv1.deref().eq(rv2.deref()) + } + TypeDesc::U16 => { + let mut rv1 = borrow_move_vec_as_rust_vec::(v1); + let mut rv2 = borrow_move_vec_as_rust_vec::(v2); + rv1.deref().eq(rv2.deref()) + } + TypeDesc::U32 => { + let mut rv1 = borrow_move_vec_as_rust_vec::(v1); + let mut rv2 = borrow_move_vec_as_rust_vec::(v2); + rv1.deref().eq(rv2.deref()) + } + TypeDesc::U64 => { + let mut rv1 = borrow_move_vec_as_rust_vec::(v1); + let mut rv2 = borrow_move_vec_as_rust_vec::(v2); + rv1.deref().eq(rv2.deref()) + } + TypeDesc::U128 => { + let mut rv1 = borrow_move_vec_as_rust_vec::(v1); + let mut rv2 = borrow_move_vec_as_rust_vec::(v2); + rv1.deref().eq(rv2.deref()) + } + TypeDesc::U256 => { + let mut rv1 = borrow_move_vec_as_rust_vec::(v1); + let mut rv2 = borrow_move_vec_as_rust_vec::(v2); + rv1.deref().eq(rv2.deref()) + } + TypeDesc::Address => { + let mut rv1 = borrow_move_vec_as_rust_vec::(v1); + let mut rv2 = borrow_move_vec_as_rust_vec::(v2); + rv1.deref().eq(rv2.deref()) + } + TypeDesc::Signer => { + let mut rv1 = borrow_move_vec_as_rust_vec::(v1); + let mut rv2 = borrow_move_vec_as_rust_vec::(v2); + rv1.deref().eq(rv2.deref()) + } + TypeDesc::Vector => { + assert!(v1_len == v2_len, "unexpected vec cmp lengths"); + let inner_element_type = *(*type_ve.type_info).vector.element_type; + let mut tmp_result = true; + for i in 0..v1_len { + let anyval_ref1 = V::borrow(type_ve, v1, i); + let anyval_ref2 = V::borrow(type_ve, v2, i); + let mv_ut_vec1 = &*(anyval_ref1 as *const AnyValue as *const MoveUntypedVector); + let mv_ut_vec2 = &*(anyval_ref2 as *const AnyValue as *const MoveUntypedVector); + tmp_result = vec_cmp_eq(&inner_element_type, mv_ut_vec1, mv_ut_vec2); + if !tmp_result { break; } + } + tmp_result + } + TypeDesc::Struct => { + assert!(v1_len == v2_len, "unexpected vec cmp lengths"); + let mut tmp_result = true; + for i in 0..v1_len { + let anyval_ref1 = V::borrow(type_ve, v1, i); + let anyval_ref2 = V::borrow(type_ve, v2, i); + tmp_result = struct_cmp_eq(type_ve, anyval_ref1, anyval_ref2); + if !tmp_result { break; } + } + tmp_result + } + _ => todo!("vec_cmp_eq: unhandled element type: {:?}", type_ve.type_desc) + }; + is_eq +} + +#[export_name = "move_rt_struct_cmp_eq"] +unsafe fn struct_cmp_eq(type_ve: &MoveType, s1: &AnyValue, s2: &AnyValue) -> bool { + use crate::conv::walk_struct_fields; + use crate::conv::{BorrowedTypedMoveValue as BTMV, borrow_move_value_as_rust_value}; + + let st_info = (*(type_ve.type_info)).struct_; + let fields1 = walk_struct_fields(&st_info, s1); + let fields2 = walk_struct_fields(&st_info, s2); + for ((fld_ty1, fld_ref1, fld_name1), (fld_ty2, fld_ref2, fld_name2)) in Iterator::zip(fields1, fields2) { + let rv1 = borrow_move_value_as_rust_value(fld_ty1, fld_ref1); + let rv2 = borrow_move_value_as_rust_value(fld_ty2, fld_ref2); + + let is_eq = match (rv1, rv2) { + (BTMV::Bool(val1), BTMV::Bool(val2)) => { val1 == val2 } + (BTMV::U8(val1), BTMV::U8(val2)) => { val1 == val2 } + (BTMV::U16(val1), BTMV::U16(val2)) => { val1 == val2 } + (BTMV::U32(val1), BTMV::U32(val2)) => { val1 == val2 } + (BTMV::U64(val1), BTMV::U64(val2)) => { val1 == val2 } + (BTMV::U128(val1), BTMV::U128(val2)) => { val1 == val2 } + (BTMV::U256(val1), BTMV::U256(val2)) => { val1 == val2 } + (BTMV::Address(val1), BTMV::Address(val2)) => { val1 == val2 } + (BTMV::Signer(val1), BTMV::Signer(val2)) => { val1 == val2 } + (BTMV::Vector(t1, utv1), BTMV::Vector(_t2, utv2)) => { + vec_cmp_eq(&t1, utv1, utv2) + } + (BTMV::Struct(t1, anyv1), BTMV::Struct(_t2, anyv2)) => { + struct_cmp_eq(&t1, anyv1, anyv2) + } + (BTMV::Reference(_, _), BTMV::Reference(_, _)) => { + unreachable!("reference in struct field impossible") + } + _ => { unreachable!("struct_cmp_eq unexpected value combination") } + }; + + if !is_eq { return false } + } + true +} diff --git a/language/move-native/src/rt_types.rs b/language/move-native/src/rt_types.rs new file mode 100644 index 0000000000..81878f10b6 --- /dev/null +++ b/language/move-native/src/rt_types.rs @@ -0,0 +1,284 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +use crate::target_defs; + +/// A Move vector with an untyped buffer. +/// +/// Used in the API for generic vector arguments. +/// +/// The only way to interact with these is to convert them from / to Rust +/// vectors or references to Rust vectors, with functions in the [`conv`] +/// module. +/// +/// The only way to create and destroy them is with the +/// [`move_native_vec_empty`] and [`move_native_vec_destroy_empty`] native +/// calls. +#[repr(C)] +#[derive(Debug)] +pub struct MoveUntypedVector { + pub ptr: *mut u8, // Safety: must be correctly aligned per type + pub capacity: u64, // in typed elements, not u8 + pub length: u64, // in typed elements, not u8 +} +pub const MOVE_UNTYPED_VEC_DESC_SIZE: u64 = core::mem::size_of::() as u64; + +/// A Move vector of bytes. +/// +/// These occur in the API enough to warrant their own type, and there are +/// dedicated functions to convert them to Rust vectors. +#[repr(C)] +pub struct MoveByteVector { + pub ptr: *mut u8, + pub capacity: u64, + pub length: u64, +} + +/// A Move vector of signers. +/// +/// This type occurs in the native API, but it will probably be removed, in +/// favor of just using `MoveUntypedVector`. +#[repr(C)] +pub struct MoveSignerVector { + pub ptr: *mut MoveSigner, + pub capacity: u64, + pub length: u64, +} + +/// A reification of the Move runtime type description. +/// +/// This is structured as a `TypeDesc` indicating which type a thing is, +/// and an undiscriminated union holding additional information about the +/// type. +/// +/// cc runtime_types::Type +/// +/// # Safety +/// +/// The pointer must be to static memory and never mutated. +#[repr(C)] +#[derive(Copy, Clone)] +pub struct MoveType { + pub name: StaticTypeName, + pub type_desc: TypeDesc, + pub type_info: *const TypeInfo, +} +pub const MOVE_TYPE_DESC_SIZE: u64 = core::mem::size_of::() as u64; + +// Needed to make the MoveType, which contains raw pointers, +// Sync, so that it can be stored in statics for test cases. +unsafe impl Sync for MoveType { } + +impl core::fmt::Debug for MoveType { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + // fixme: implement this better + unsafe { + write!(f, "{}", self.name.as_ascii_str()); + } + Ok(()) + } +} + +/// # Safety +/// +/// The pointer must be to static memory and never mutated. +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct StaticTypeName { + pub ptr: *const u8, + pub len: u64, +} + +impl StaticTypeName { + pub unsafe fn as_ascii_str<'a>(&'a self) -> &'a str { + core::str::from_utf8_unchecked(core::slice::from_raw_parts( + self.ptr, + usize::try_from(self.len).expect("overflow"), + )) + } +} + +pub type StaticName = StaticTypeName; + +static DUMMY_TYPE_NAME_SLICE: &[u8] = b"dummy"; +pub static DUMMY_TYPE_NAME: StaticTypeName = StaticTypeName { + ptr: DUMMY_TYPE_NAME_SLICE as *const [u8] as *const u8, + len: 5, +}; + +unsafe impl Sync for StaticTypeName {} + +#[repr(u64)] +#[derive(Copy, Clone, Debug)] +pub enum TypeDesc { + Bool = 1, + U8 = 2, + U16 = 3, + U32 = 4, + U64 = 5, + U128 = 6, + U256 = 7, + Address = 8, + Signer = 9, + Vector = 10, + Struct = 11, + Reference = 12, + //MutableReference = 13, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub union TypeInfo { + pub nothing: u8, // if no type info is needed + pub vector: VectorTypeInfo, + pub struct_: StructTypeInfo, + pub struct_instantiation: u8, // todo + pub reference: ReferenceTypeInfo, + pub mutable_reference: ReferenceTypeInfo, + pub ty_param: u8, // todo +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct VectorTypeInfo { + pub element_type: &'static MoveType, +} + +/// # Safety +/// +/// This type is `Sync` so that it can be declared statically. The value +/// pointed to by `field_array_ptr` should not be mutated, or `Sync` will be +/// violated. +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct StructTypeInfo { + /// Pointer to an array of field infos. + /// + /// This would ideally be a Rust static slice, but the layout is + /// seemingly undefined. + pub field_array_ptr: *const StructFieldInfo, + pub field_array_len: u64, + /// Size of the struct within an array. + pub size: u64, + /// Alignment of the struct. + pub alignment: u64, +} + +unsafe impl Sync for StructTypeInfo {} + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct StructFieldInfo { + pub type_: MoveType, + /// Offset in bytes within the struct. + pub offset: u64, + pub name: StaticName, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct ReferenceTypeInfo { + pub element_type: &'static MoveType, +} + +#[repr(transparent)] +pub struct AnyValue(u8); + +#[repr(transparent)] +#[derive(Debug, PartialEq)] +#[derive(borsh::BorshSerialize, borsh::BorshDeserialize)] +pub struct MoveSigner(pub MoveAddress); + +/// A Move address. +/// +/// This is mapped to the address size of the target platform, and may +/// differ from Move VM. +/// +/// Bytes are in little-endian order. +#[repr(transparent)] +#[derive(PartialEq)] +#[derive(borsh::BorshSerialize, borsh::BorshDeserialize)] +pub struct MoveAddress(pub [u8; target_defs::ACCOUNT_ADDRESS_LENGTH]); + +impl core::fmt::Debug for MoveAddress { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str("@")?; + for byte in self.0.iter().rev() { + f.write_fmt(core::format_args!("{byte:02X?}"))?; + } + Ok(()) + } +} + +// Defined in std::type_name; not a primitive. +// +// todo how is drop glue handled? +#[repr(C)] +pub struct TypeName { + pub name: MoveAsciiString, +} + +// Defined in std::ascii; not a primitive. +// +// todo how is drop glue handled? +#[repr(C)] +pub struct MoveAsciiString { + pub bytes: MoveByteVector, +} + +// todo this would be more correct with a lifetime attached +#[repr(transparent)] +#[derive(Debug)] +pub struct MoveUntypedReference(pub *const AnyValue); + +mod drop_bomb { + // fixme this is pretty awkward - this is intended to be a no-std crate + #[cfg(test)] + extern crate std; + + #[cfg(not(test))] + pub fn run(name: &str) { + panic!("forgot to destroy {}", name); + } + + #[cfg(test)] + pub fn run(name: &str) { + if !std::thread::panicking() { + panic!("forgot to destroy {}", name); + } else { + std::eprintln!("forgot to destroy {}", name); + } + } +} + +// Drop-bomb. Catch errors in test cases. +impl Drop for MoveUntypedVector { + fn drop(&mut self) { + drop_bomb::run("MoveUntypedVector"); + } +} + +// Drop-bomb. Catch errors in test cases. +impl Drop for MoveByteVector { + fn drop(&mut self) { + drop_bomb::run("MoveByteVector"); + } +} + +// Drop-bomb. Catch errors in test cases. +impl Drop for MoveSignerVector { + fn drop(&mut self) { + drop_bomb::run("MoveSignerVector"); + } +} + +/// Don't run destructors. +/// +/// Some of these runtime types hold allocations, and need to be destroyed +/// in specific ways. If they are not, then they panic. These types must be +/// destroyed by calling `mem::forget`, for which this function is a +/// synonym. +pub fn disarm_drop_bomb(v: T) { + core::mem::forget(v) +} diff --git a/language/move-native/src/std.rs b/language/move-native/src/std.rs new file mode 100644 index 0000000000..c2ac3c67f7 --- /dev/null +++ b/language/move-native/src/std.rs @@ -0,0 +1,510 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +mod bcs { + use crate::conv::*; + use crate::rt_types::*; + + /// Serialize any value. + /// + /// This does not necessarily produce the same serializations that + /// the Move VM does, as the Rust types are not the same, and + /// the serialization format may not actually be bcs. + /// + /// # References + /// + /// - `move-vm-types::values::Value` + /// - `move-core-types::value` + #[export_name = "move_native_bcs_to_bytes"] + unsafe extern "C" fn to_bytes(type_v: &MoveType, v: &AnyValue) -> MoveByteVector { + crate::serialization::serialize(type_v, v) + } + + /// Deserialize any value. + /// + /// This is not actually in move std, but is used for testing. + #[export_name = "move_native_bcs_test_from_bytes"] + unsafe extern "C" fn test_from_bytes(type_v: &MoveType, bytes: &MoveByteVector, v: *mut AnyValue) { + crate::serialization::deserialize(type_v, bytes, v) + } +} + +// nursery +mod debug { + use crate::conv::*; + use crate::rt_types::*; + use crate::target_defs; + use alloc::format; + use alloc::string::String; + use core::fmt::Write; + + #[export_name = "move_native_debug_print"] + unsafe extern "C" fn print(type_x: &MoveType, x: &AnyValue) { + let v = borrow_move_value_as_rust_value(type_x, x); + target_defs::print_string(&format!("{:?}", v)); + } + + #[export_name = "move_native_print_stack_trace"] + extern "C" fn print_stack_trace() { + target_defs::print_stack_trace(); + } +} + +// nursery +mod event { + use crate::rt_types::*; + + #[export_name = "move_native_event_write_to_event_store"] + unsafe extern "C" fn write_to_event_store( + type_msg: &MoveType, + guid: MoveByteVector, + count: u64, + msg: *mut AnyValue, + ) { + todo!() + } +} + +mod hash { + use crate::conv::{move_byte_vec_to_rust_vec, rust_vec_to_move_byte_vec}; + use crate::rt_types::*; + use sha2::{Digest, Sha256}; + use sha3::Sha3_256; + + #[export_name = "move_native_hash_sha2_256"] + unsafe extern "C" fn sha2_256(ptr: MoveByteVector) -> MoveByteVector { + let rust_vec = move_byte_vec_to_rust_vec(ptr); + + let hash_vec = Sha256::digest(rust_vec.as_slice()).to_vec(); + let move_vec = rust_vec_to_move_byte_vec(hash_vec); + + move_vec + } + + #[export_name = "move_native_hash_sha3_256"] + unsafe extern "C" fn sha3_256(ptr: MoveByteVector) -> MoveByteVector { + let rust_vec = move_byte_vec_to_rust_vec(ptr); + + let hash_vec = Sha3_256::digest(rust_vec.as_slice()).to_vec(); + let move_vec = rust_vec_to_move_byte_vec(hash_vec); + + move_vec + } +} + +mod signer { + use crate::rt_types::*; + + #[export_name = "move_native_signer_borrow_address"] + extern "C" fn borrow_address(s: &MoveSigner) -> &MoveAddress { + &s.0 + } +} + +pub(crate) mod string { + use crate::conv::*; + use crate::rt_types::*; + use alloc::vec::Vec; + use core::str; + + #[export_name = "move_native_string_internal_check_utf8"] + pub unsafe extern "C" fn internal_check_utf8(v: &MoveByteVector) -> bool { + let rust_vec = borrow_move_byte_vec_as_rust_vec(v); + let res = str::from_utf8(&rust_vec); + + match res { + Ok(_) => true, + Err(_) => false, + } + } + + #[export_name = "move_native_string_internal_is_char_boundary"] + pub unsafe extern "C" fn internal_is_char_boundary(v: &MoveByteVector, i: u64) -> bool { + let rust_vec = borrow_move_byte_vec_as_rust_vec(v); + let i = usize::try_from(i).expect("usize"); + + let rust_str = str::from_utf8(&rust_vec).expect("invalid utf8"); + rust_str.is_char_boundary(i) + } + + #[export_name = "move_native_string_internal_sub_string"] + pub unsafe extern "C" fn internal_sub_string( + v: &MoveByteVector, + i: u64, + j: u64, + ) -> MoveByteVector { + let rust_vec = borrow_move_byte_vec_as_rust_vec(v); + let i = usize::try_from(i).expect("usize"); + let j = usize::try_from(j).expect("usize"); + + let rust_str = str::from_utf8(&rust_vec).expect("invalid utf8"); + + let sub_rust_vec = rust_str[i..j].as_bytes().to_vec(); + rust_vec_to_move_byte_vec(sub_rust_vec) + } + + #[export_name = "move_native_string_internal_index_of"] + pub unsafe extern "C" fn internal_index_of(s: &MoveByteVector, r: &MoveByteVector) -> u64 { + let s_rust_vec = borrow_move_byte_vec_as_rust_vec(s); + let s_rust_str = str::from_utf8(&s_rust_vec).expect("invalid utf8"); + let r_rust_vec = borrow_move_byte_vec_as_rust_vec(r); + let r_rust_str = str::from_utf8(&r_rust_vec).expect("invalid utf8"); + + let res = s_rust_str.find(r_rust_str); + + u64::try_from(match res { + Some(i) => i, + None => s_rust_str.len(), + }) + .expect("u64") + } +} + +mod type_name { + use crate::conv::*; + use crate::rt_types::*; + + #[export_name = "move_native_type_name_get"] + unsafe extern "C" fn get(type_: &MoveType) -> TypeName { + let name_slice = type_.name.as_ascii_str(); + let byte_type = MoveType { + name: DUMMY_TYPE_NAME, + type_desc: TypeDesc::U8, + type_info: &TypeInfo { nothing: 0 }, + }; + let mut byte_vector = super::vector::empty(&byte_type); + { + let mut rust_byte_vector = borrow_move_vec_as_rust_vec_mut::(&mut byte_vector); + rust_byte_vector.reserve_exact(name_slice.len()); + for byte in name_slice.bytes() { + rust_byte_vector.push(byte); + } + } + TypeName { + name: MoveAsciiString { + // safety: MoveUntypedVector and MoveByteVector have the same representation + bytes: core::mem::transmute::(byte_vector), + }, + } + } +} + +mod unit_test { + use crate::rt_types::*; + + #[export_name = "move_native_unit_test_create_signers_for_testing"] + extern "C" fn create_signers_for_testing(num_signers: u64) -> MoveSignerVector { + todo!() + } +} + +pub(crate) mod vector { + use crate::conv::*; + use crate::rt_types::*; + use alloc::vec::Vec; + use core::{mem, ptr}; + use ethnum::U256; + + // Safety: Even empty Rust vectors have non-null buffer pointers, + // which must be correctly aligned. This function crates empty Rust vecs + // of the correct type and converts them to untyped move vecs. + #[export_name = "move_native_vector_empty"] + pub extern "C" fn empty(type_r: &MoveType) -> MoveUntypedVector { + let move_vec = match type_r.type_desc { + TypeDesc::Bool => rust_vec_to_move_vec::(Vec::new()), + TypeDesc::U8 => rust_vec_to_move_vec::(Vec::new()), + TypeDesc::U16 => rust_vec_to_move_vec::(Vec::new()), + TypeDesc::U32 => rust_vec_to_move_vec::(Vec::new()), + TypeDesc::U64 => rust_vec_to_move_vec::(Vec::new()), + TypeDesc::U128 => rust_vec_to_move_vec::(Vec::new()), + TypeDesc::U256 => rust_vec_to_move_vec::(Vec::new()), + TypeDesc::Address => rust_vec_to_move_vec::(Vec::new()), + TypeDesc::Signer => rust_vec_to_move_vec::(Vec::new()), + TypeDesc::Vector => { + // Safety: need correct alignment for the internal vector + // pointer of the outer vector, which is non-null even for + // an unallocated vector. `MoveUntypedVector` has the same + // size and alignment regardless of the type it contains, so + // no need to interpret the vector type. + rust_vec_to_move_vec::(Vec::new()) + } + TypeDesc::Struct => unsafe { + // Safety: this gets pretty sketchy, and relies on internal + // Vec details that probably are not guaranteed. The most + // _correct_ way to initialize a Vec is to call its + // constructor. + // + // That is pretty tough with a type of any dynamically sized + // layout, so we're going to munge the pointers ourselves. + // + // The critical thing to know about Vec's pointers is: + // + // - They must always be aligned correctly + // - They are _never_ 0, even for empty Vec's, to allow null + // pointer optimizations. + // + // Vec uses `NonNull::dangling` to create invalid non-null + // pointers, but that requires a concrete type of the + // correct alignment. We dig even deeper and use + // `ptr::invalid_mut`, which is an unstable function from + // the pointer provenance project. As it is unstable we just + // duplicate it in our `conv` module until it becomes + // stable. + // + // This should be the only location in this crate where we + // need to fabricate a pointer from an integer. + let size = (*type_r.type_info).struct_.size; + let size = usize::try_from(size).expect("overflow"); + let alignment = (*type_r.type_info).struct_.alignment; + let alignment = usize::try_from(alignment).expect("overflow"); + + assert!(size != 0); // can't handle ZSTs + assert!(alignment != 0); // must have alignment + assert!(alignment.is_power_of_two()); + + let ptr = invalid_mut::(alignment); + MoveUntypedVector { + ptr, + capacity: 0, + length: 0, + } + }, + TypeDesc::Reference => rust_vec_to_move_vec::(Vec::new()), + }; + + move_vec + } + + #[export_name = "move_native_vector_length"] + pub unsafe extern "C" fn length(type_ve: &MoveType, v: &MoveUntypedVector) -> u64 { + // It is not strictly necessary to convert the vec for this op. + // Doing it for consistency. + let rust_vec = borrow_typed_move_vec_as_rust_vec(type_ve, v); + + let len = match rust_vec { + TypedMoveBorrowedRustVec::Bool(v) => v.len(), + TypedMoveBorrowedRustVec::U8(v) => v.len(), + TypedMoveBorrowedRustVec::U16(v) => v.len(), + TypedMoveBorrowedRustVec::U32(v) => v.len(), + TypedMoveBorrowedRustVec::U64(v) => v.len(), + TypedMoveBorrowedRustVec::U128(v) => v.len(), + TypedMoveBorrowedRustVec::U256(v) => v.len(), + TypedMoveBorrowedRustVec::Address(v) => v.len(), + TypedMoveBorrowedRustVec::Signer(v) => v.len(), + TypedMoveBorrowedRustVec::Vector(_t, v) => v.len(), + TypedMoveBorrowedRustVec::Struct(s) => { + usize::try_from(s.inner.length).expect("overflow") + } + TypedMoveBorrowedRustVec::Reference(_t, v) => v.len(), + }; + + u64::try_from(len).expect("u64") + } + + #[export_name = "move_native_vector_borrow"] + pub unsafe extern "C" fn borrow<'v>( + type_ve: &'v MoveType, + v: &'v MoveUntypedVector, + i: u64, + ) -> &'v AnyValue { + let rust_vec = borrow_typed_move_vec_as_rust_vec(type_ve, v); + + let i = usize::try_from(i).expect("usize"); + let value = match rust_vec { + TypedMoveBorrowedRustVec::Bool(v) => mem::transmute(&v[i]), + TypedMoveBorrowedRustVec::U8(v) => mem::transmute(&v[i]), + TypedMoveBorrowedRustVec::U16(v) => mem::transmute(&v[i]), + TypedMoveBorrowedRustVec::U32(v) => mem::transmute(&v[i]), + TypedMoveBorrowedRustVec::U64(v) => mem::transmute(&v[i]), + TypedMoveBorrowedRustVec::U128(v) => mem::transmute(&v[i]), + TypedMoveBorrowedRustVec::U256(v) => mem::transmute(&v[i]), + TypedMoveBorrowedRustVec::Address(v) => mem::transmute(&v[i]), + TypedMoveBorrowedRustVec::Signer(v) => mem::transmute(&v[i]), + TypedMoveBorrowedRustVec::Vector(_t, v) => mem::transmute(&v[i]), + TypedMoveBorrowedRustVec::Struct(s) => s.get(i), + TypedMoveBorrowedRustVec::Reference(_t, v) => mem::transmute(&v[i]), + }; + + value + } + + #[rustfmt::skip] + #[export_name = "move_native_vector_push_back"] + pub unsafe extern "C" fn push_back( + type_ve: &MoveType, + v: &mut MoveUntypedVector, + e: *mut AnyValue + ) { + let mut rust_vec = borrow_typed_move_vec_as_rust_vec_mut(type_ve, v); + + match rust_vec { + TypedMoveBorrowedRustVecMut::Bool(mut v) => v.push(ptr::read(e as *const bool)), + TypedMoveBorrowedRustVecMut::U8(mut v) => v.push(ptr::read(e as *const u8)), + TypedMoveBorrowedRustVecMut::U16(mut v) => v.push(ptr::read(e as *const u16)), + TypedMoveBorrowedRustVecMut::U32(mut v) => v.push(ptr::read(e as *const u32)), + TypedMoveBorrowedRustVecMut::U64(mut v) => v.push(ptr::read(e as *const u64)), + TypedMoveBorrowedRustVecMut::U128(mut v) => v.push(ptr::read(e as *const u128)), + TypedMoveBorrowedRustVecMut::U256(mut v) => v.push(ptr::read(e as *const U256)), + TypedMoveBorrowedRustVecMut::Address(mut v) => v.push(ptr::read(e as *const MoveAddress)), + TypedMoveBorrowedRustVecMut::Signer(mut v) => v.push(ptr::read(e as *const MoveSigner)), + TypedMoveBorrowedRustVecMut::Vector(_t, mut v) => v.push(ptr::read(e as *const MoveUntypedVector)), + TypedMoveBorrowedRustVecMut::Struct(mut s) => s.push(e), + TypedMoveBorrowedRustVecMut::Reference(_t, mut v) => v.push(ptr::read(e as *const MoveUntypedReference)), + } + } + + #[rustfmt::skip] + #[export_name = "move_native_vector_borrow_mut"] + unsafe extern "C" fn borrow_mut<'v>( + type_ve: &'v MoveType, + v: &'v mut MoveUntypedVector, + i: u64 + ) -> &'v mut AnyValue { + let mut rust_vec = borrow_typed_move_vec_as_rust_vec_mut(type_ve, v); + + let i = usize::try_from(i).expect("usize"); + let value = match rust_vec { + TypedMoveBorrowedRustVecMut::Bool(mut v) => mem::transmute(&mut v[i]), + TypedMoveBorrowedRustVecMut::U8(mut v) => mem::transmute(&mut v[i]), + TypedMoveBorrowedRustVecMut::U16(mut v) => mem::transmute(&mut v[i]), + TypedMoveBorrowedRustVecMut::U32(mut v) => mem::transmute(&mut v[i]), + TypedMoveBorrowedRustVecMut::U64(mut v) => mem::transmute(&mut v[i]), + TypedMoveBorrowedRustVecMut::U128(mut v) => mem::transmute(&mut v[i]), + TypedMoveBorrowedRustVecMut::U256(mut v) => mem::transmute(&mut v[i]), + TypedMoveBorrowedRustVecMut::Address(mut v) => mem::transmute(&mut v[i]), + TypedMoveBorrowedRustVecMut::Signer(mut v) => mem::transmute(&mut v[i]), + TypedMoveBorrowedRustVecMut::Vector(_t, mut v) => mem::transmute(&mut v[i]), + TypedMoveBorrowedRustVecMut::Struct(mut s) => s.get_mut(i), + TypedMoveBorrowedRustVecMut::Reference(_t, mut v) => mem::transmute(&mut v[i]), + }; + + value + } + + #[export_name = "move_native_vector_pop_back"] + pub unsafe extern "C" fn pop_back( + type_ve: &MoveType, + v: &mut MoveUntypedVector, + r: *mut AnyValue, + ) { + let mut rust_vec = borrow_typed_move_vec_as_rust_vec_mut(type_ve, v); + + let msg = "popping from empty vec"; + match rust_vec { + TypedMoveBorrowedRustVecMut::Bool(mut v) => { + ptr::write(r as *mut bool, v.pop().expect(msg)); + } + TypedMoveBorrowedRustVecMut::U8(mut v) => { + ptr::write(r as *mut u8, v.pop().expect(msg)); + } + TypedMoveBorrowedRustVecMut::U16(mut v) => { + ptr::write(r as *mut u16, v.pop().expect(msg)); + } + TypedMoveBorrowedRustVecMut::U32(mut v) => { + ptr::write(r as *mut u32, v.pop().expect(msg)); + } + TypedMoveBorrowedRustVecMut::U64(mut v) => { + ptr::write(r as *mut u64, v.pop().expect(msg)); + } + TypedMoveBorrowedRustVecMut::U128(mut v) => { + ptr::write(r as *mut u128, v.pop().expect(msg)); + } + TypedMoveBorrowedRustVecMut::U256(mut v) => { + ptr::write(r as *mut U256, v.pop().expect(msg)); + } + TypedMoveBorrowedRustVecMut::Address(mut v) => { + ptr::write(r as *mut MoveAddress, v.pop().expect(msg)); + } + TypedMoveBorrowedRustVecMut::Signer(mut v) => { + ptr::write(r as *mut MoveSigner, v.pop().expect(msg)); + } + TypedMoveBorrowedRustVecMut::Vector(_t, mut v) => { + ptr::write(r as *mut MoveUntypedVector, v.pop().expect(msg)); + } + TypedMoveBorrowedRustVecMut::Struct(mut s) => s.pop_into(r), + TypedMoveBorrowedRustVecMut::Reference(_t, mut v) => { + ptr::write(r as *mut MoveUntypedReference, v.pop().expect(msg)); + } + } + } + + #[export_name = "move_native_vector_destroy_empty"] + pub unsafe extern "C" fn destroy_empty(type_ve: &MoveType, v: MoveUntypedVector) { + assert_eq!(v.length, 0); + match type_ve.type_desc { + TypeDesc::Bool => drop(move_vec_to_rust_vec::(v)), + TypeDesc::U8 => drop(move_vec_to_rust_vec::(v)), + TypeDesc::U16 => drop(move_vec_to_rust_vec::(v)), + TypeDesc::U32 => drop(move_vec_to_rust_vec::(v)), + TypeDesc::U64 => drop(move_vec_to_rust_vec::(v)), + TypeDesc::U128 => drop(move_vec_to_rust_vec::(v)), + TypeDesc::U256 => drop(move_vec_to_rust_vec::(v)), + TypeDesc::Address => drop(move_vec_to_rust_vec::(v)), + TypeDesc::Signer => drop(move_vec_to_rust_vec::(v)), + TypeDesc::Vector => { + // Safety: need the correct internal pointer alignment to + // deallocate; need the outer vector to be empty to avoid + // dropping the inner vectors. As in `empty`, + // MoveUntypedVector should have the same size/alignment + // regardless of the contained type, so no need to interpret + // the vector type. + drop(move_vec_to_rust_vec::(v)) + } + TypeDesc::Struct => { + // Safety: like in `empty` we want to deallocate here without + // creating a `Vec` of a concrete type, since handling the + // alignment would requiring enumerating many types. + // + // So here we're just going to free the pointer ourselves, + // constructing a correct `Layout` value to pass to the + // allocator. + // + // Note that this function can only be called on empty vecs, + // so we don't need to care about dropping elements. + + let size = (*type_ve.type_info).struct_.size; + let size = usize::try_from(size).expect("overflow"); + let alignment = (*type_ve.type_info).struct_.alignment; + let alignment = usize::try_from(alignment).expect("overflow"); + let capacity = usize::try_from(v.capacity).expect("overflow"); + + assert!(size != 0); // can't handle ZSTs + + if capacity != 0 { + let vec_byte_size = capacity.checked_mul(size).expect("overflow"); + let layout = + alloc::alloc::Layout::from_size_align(vec_byte_size, alignment) + .expect("bad size or alignment"); + alloc::alloc::dealloc(v.ptr, layout); + } + + disarm_drop_bomb(v); + } + TypeDesc::Reference => drop(move_vec_to_rust_vec::(v)), + } + } + + #[export_name = "move_native_vector_swap"] + unsafe extern "C" fn swap(type_ve: &MoveType, v: &mut MoveUntypedVector, i: u64, j: u64) { + let i = usize::try_from(i).expect("usize"); + let j = usize::try_from(j).expect("usize"); + + let mut rust_vec = borrow_typed_move_vec_as_rust_vec_mut(type_ve, v); + + match rust_vec { + TypedMoveBorrowedRustVecMut::Bool(mut v) => v.swap(i, j), + TypedMoveBorrowedRustVecMut::U8(mut v) => v.swap(i, j), + TypedMoveBorrowedRustVecMut::U16(mut v) => v.swap(i, j), + TypedMoveBorrowedRustVecMut::U32(mut v) => v.swap(i, j), + TypedMoveBorrowedRustVecMut::U64(mut v) => v.swap(i, j), + TypedMoveBorrowedRustVecMut::U128(mut v) => v.swap(i, j), + TypedMoveBorrowedRustVecMut::U256(mut v) => v.swap(i, j), + TypedMoveBorrowedRustVecMut::Address(mut v) => v.swap(i, j), + TypedMoveBorrowedRustVecMut::Signer(mut v) => v.swap(i, j), + TypedMoveBorrowedRustVecMut::Vector(_t, mut v) => v.swap(i, j), + TypedMoveBorrowedRustVecMut::Struct(mut v) => v.swap(i, j), + TypedMoveBorrowedRustVecMut::Reference(_t, mut v) => v.swap(i, j), + } + } +} diff --git a/language/move-native/src/target_defs.rs b/language/move-native/src/target_defs.rs new file mode 100644 index 0000000000..155771e449 --- /dev/null +++ b/language/move-native/src/target_defs.rs @@ -0,0 +1,110 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +pub use impls::*; + +#[cfg(not(feature = "solana"))] +mod impls { + // Move addresses are 16 bytes by default, but can be made 20 or 32 at compile time. + pub const ACCOUNT_ADDRESS_LENGTH: usize = 16; + + pub fn print_string(s: &str) { + todo!() + } + + pub fn print_stack_trace() { + todo!() + } + + pub fn abort(code: u64) -> ! { + todo!() + } +} + +#[cfg(feature = "solana")] +mod impls { + // Solana pubkeys are 32 bytes. + // Move addresses are 16 bytes by default, but can be made 20 or 32 at compile time. + pub const ACCOUNT_ADDRESS_LENGTH: usize = 32; + + pub fn print_string(s: &str) { + unsafe { + syscalls::sol_log_(s.as_ptr(), s.len() as u64); + } + } + + pub fn print_stack_trace() { + todo!() + } + + pub fn abort(code: u64) -> ! { + unsafe { + syscalls::sol_log_64_( + code, code, code, code, code, + ); + syscalls::abort() + } + } + + // NB: not using the "static-syscalls" sbf feature + mod syscalls { + extern "C" { + pub fn abort() -> !; + pub fn sol_log_(msg: *const u8, len: u64); + pub fn sol_log_64_(_: u64, _: u64, _: u64, _: u64, _: u64); + } + } + + mod globals { + use alloc::alloc::{GlobalAlloc, Layout}; + use alloc::format; + use core::mem::size_of; + use core::ptr::null_mut; + + const PANIC_ABORT_CODE: u64 = 101; + + #[panic_handler] + fn panic(info: &core::panic::PanicInfo) -> ! { + super::print_string(&format!("{}", info)); + super::abort(PANIC_ABORT_CODE); + } + + #[global_allocator] + static A: BumpAllocator = BumpAllocator { + start: HEAP_START_ADDRESS as usize, + len: HEAP_LENGTH, + }; + + pub struct BumpAllocator { + pub start: usize, + pub len: usize, + } + + unsafe impl GlobalAlloc for BumpAllocator { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let pos_ptr = self.start as *mut usize; + + let mut pos = *pos_ptr; + if pos == 0 { + // First time, set starting position + pos = self.start + self.len; + } + pos = pos.saturating_sub(layout.size()); + pos &= !(layout.align().wrapping_sub(1)); + if pos < self.start + size_of::<*mut u8>() { + return null_mut(); + } + *pos_ptr = pos; + pos as *mut u8 + } + #[inline] + unsafe fn dealloc(&self, _: *mut u8, _: Layout) { + // I'm a bump allocator, I don't free + } + } + pub const HEAP_START_ADDRESS: u64 = 0x300000000; + pub const HEAP_LENGTH: usize = 32 * 1024; + } +}