feat: Rebrand Polkadot/Substrate references to PezkuwiChain
This commit systematically rebrands various references from Parity Technologies' Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk. Key changes include: - Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks. - Modified internal documentation and code comments to reflect PezkuwiChain naming and structure. - Replaced direct references to with or specific paths within the for XCM, Pezkuwi, and other modules. - Cleaned up deprecated issue and PR references in various and files, particularly in and modules. - Adjusted image and logo URLs in documentation to point to PezkuwiChain assets. - Removed or rephrased comments related to external Polkadot/Substrate PRs and issues. This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Traits required by the runtime interface from the host side.
|
||||
|
||||
use crate::RIType;
|
||||
|
||||
use pezsp_wasm_interface::{FunctionContext, Result};
|
||||
|
||||
/// A type used as a return value in a host function. Can be turned into an FFI value.
|
||||
pub trait IntoFFIValue: RIType {
|
||||
/// Convert `Self::Inner` into an FFI value.
|
||||
fn into_ffi_value(
|
||||
value: Self::Inner,
|
||||
context: &mut dyn FunctionContext,
|
||||
) -> Result<Self::FFIType>;
|
||||
}
|
||||
|
||||
/// A type used as a parameter in a host function. Can be created from an FFI value.
|
||||
///
|
||||
/// Implementations are safe to assume that the `arg` given to `from_ffi_value`
|
||||
/// is only generated by the corresponding [`wasm::IntoFFIValue`](crate::wasm::IntoFFIValue)
|
||||
/// implementation.
|
||||
pub trait FromFFIValue<'a>: RIType {
|
||||
/// The owned inner type.
|
||||
type Owned;
|
||||
|
||||
/// Creates `Self::Owned` from the given `arg` received through the FFI boundary from the
|
||||
/// runtime.
|
||||
fn from_ffi_value(context: &mut dyn FunctionContext, arg: Self::FFIType)
|
||||
-> Result<Self::Owned>;
|
||||
|
||||
/// Creates `Self::Inner` from an owned value.
|
||||
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner;
|
||||
|
||||
/// Write back a modified `value` back into the runtime's memory.
|
||||
///
|
||||
/// Only makes sense for parameters like e.g. `&mut [u8]`.
|
||||
#[inline]
|
||||
fn write_back_into_runtime(
|
||||
_value: Self::Owned,
|
||||
_context: &mut dyn FunctionContext,
|
||||
_arg: Self::FFIType,
|
||||
) -> Result<()> {
|
||||
// Default dummy implementation, because the vast majority of impls won't need this.
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Provides implementations for the runtime interface types which can be
|
||||
//! passed directly without any serialization strategy wrappers.
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
use crate::host::*;
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
use crate::wasm::*;
|
||||
use crate::{Pointer, RIType};
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
use pezsp_wasm_interface::{FunctionContext, Result};
|
||||
|
||||
// Make sure that our assumptions for storing a pointer + its size in `u64` is valid.
|
||||
#[cfg(all(bizinikiwi_runtime, not(feature = "disable_target_static_assertions")))]
|
||||
const _: () = {
|
||||
assert!(core::mem::size_of::<usize>() == core::mem::size_of::<u32>());
|
||||
assert!(core::mem::size_of::<*const u8>() == core::mem::size_of::<u32>());
|
||||
};
|
||||
|
||||
/// Implement the traits for the given primitive traits.
|
||||
macro_rules! impl_traits_for_primitives {
|
||||
(
|
||||
$(
|
||||
$rty:ty, $fty:ty,
|
||||
)*
|
||||
) => {
|
||||
$(
|
||||
/// The type is passed directly.
|
||||
impl RIType for $rty {
|
||||
type FFIType = $fty;
|
||||
type Inner = Self;
|
||||
}
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl IntoFFIValue for $rty {
|
||||
type Destructor = ();
|
||||
|
||||
fn into_ffi_value(value: &mut $rty) -> (Self::FFIType, Self::Destructor) {
|
||||
(*value as $fty, ())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl FromFFIValue for $rty {
|
||||
fn from_ffi_value(arg: $fty) -> $rty {
|
||||
arg as $rty
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<'a> FromFFIValue<'a> for $rty {
|
||||
type Owned = Self;
|
||||
|
||||
fn from_ffi_value(_: &mut dyn FunctionContext, arg: $fty) -> Result<$rty> {
|
||||
Ok(arg as $rty)
|
||||
}
|
||||
|
||||
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
|
||||
*owned
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl IntoFFIValue for $rty {
|
||||
fn into_ffi_value(value: Self::Inner, _: &mut dyn FunctionContext) -> Result<$fty> {
|
||||
Ok(value as $fty)
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
impl_traits_for_primitives! {
|
||||
u8, u32,
|
||||
u16, u32,
|
||||
u32, u32,
|
||||
u64, u64,
|
||||
i8, i32,
|
||||
i16, i32,
|
||||
i32, i32,
|
||||
i64, i64,
|
||||
}
|
||||
|
||||
/// `bool` is passed as `u32`.
|
||||
///
|
||||
/// - `1`: true
|
||||
/// - `0`: false
|
||||
impl RIType for bool {
|
||||
type FFIType = u32;
|
||||
type Inner = Self;
|
||||
}
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl IntoFFIValue for bool {
|
||||
type Destructor = ();
|
||||
|
||||
fn into_ffi_value(value: &mut bool) -> (Self::FFIType, Self::Destructor) {
|
||||
(if *value { 1 } else { 0 }, ())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl FromFFIValue for bool {
|
||||
fn from_ffi_value(arg: u32) -> bool {
|
||||
arg == 1
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<'a> FromFFIValue<'a> for bool {
|
||||
type Owned = Self;
|
||||
|
||||
fn from_ffi_value(_: &mut dyn FunctionContext, arg: u32) -> Result<bool> {
|
||||
Ok(arg == 1)
|
||||
}
|
||||
|
||||
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
|
||||
*owned
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl IntoFFIValue for bool {
|
||||
fn into_ffi_value(value: Self, _: &mut dyn FunctionContext) -> Result<u32> {
|
||||
Ok(if value { 1 } else { 0 })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<T: pezsp_wasm_interface::PointerType> RIType for Pointer<T> {
|
||||
type FFIType = u32;
|
||||
type Inner = Self;
|
||||
}
|
||||
|
||||
/// The type is passed as `u32`.
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl<T> RIType for Pointer<T> {
|
||||
type FFIType = u32;
|
||||
type Inner = Self;
|
||||
}
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl<T> IntoFFIValue for Pointer<T> {
|
||||
type Destructor = ();
|
||||
|
||||
fn into_ffi_value(value: &mut Pointer<T>) -> (Self::FFIType, Self::Destructor) {
|
||||
(*value as u32, ())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl<T> FromFFIValue for Pointer<T> {
|
||||
fn from_ffi_value(arg: u32) -> Self {
|
||||
arg as _
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<'a, T: pezsp_wasm_interface::PointerType> FromFFIValue<'a> for Pointer<T> {
|
||||
type Owned = Self;
|
||||
|
||||
fn from_ffi_value(_: &mut dyn FunctionContext, arg: u32) -> Result<Self> {
|
||||
Ok(Pointer::new(arg))
|
||||
}
|
||||
|
||||
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
|
||||
*owned
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<T: pezsp_wasm_interface::PointerType> IntoFFIValue for Pointer<T> {
|
||||
fn into_ffi_value(value: Self, _: &mut dyn FunctionContext) -> Result<u32> {
|
||||
Ok(value.into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Bizinikiwi runtime interface
|
||||
//!
|
||||
//! This crate provides types, traits and macros around runtime interfaces. A runtime interface is
|
||||
//! a fixed interface between a Bizinikiwi runtime (also called the "guest") and a Bizinikiwi node
|
||||
//! (also called the "host"). For a native runtime the interface maps to direct function calls of
|
||||
//! the implementation. For a non-native runtime the interface maps to an external function call.
|
||||
//! These external functions are exported by the runtime and they map to the same implementation
|
||||
//! as the native calls, just with some extra code to marshal them through the FFI boundary.
|
||||
//!
|
||||
//! # Using a type in a runtime interface
|
||||
//!
|
||||
//! Every argument type and return type must be wrapped in a marker newtype specifying the
|
||||
//! marshalling strategy used to pass the value through the FFI boundary between the host
|
||||
//! and the runtime. The only exceptions to this rule are a couple of basic, primitive types
|
||||
//! which can be passed directly through the FFI boundary and which don't require any special
|
||||
//! handling besides a straightforward, direct conversion.
|
||||
//!
|
||||
//! You can find the strategy wrapper types in the [`crate::pass_by`] module.
|
||||
//!
|
||||
//! The newtype wrappers are automatically stripped away when the function is called
|
||||
//! and applied when the function returns by the `runtime_interface` macro.
|
||||
//!
|
||||
//! # Declaring a runtime interface
|
||||
//!
|
||||
//! Declaring a runtime interface is similar to declaring a trait in Rust:
|
||||
//!
|
||||
//! ```
|
||||
//! # mod wrapper {
|
||||
//! # use pezsp_runtime_interface::pass_by::PassFatPointerAndRead;
|
||||
//!
|
||||
//! #[pezsp_runtime_interface::runtime_interface]
|
||||
//! trait RuntimeInterface {
|
||||
//! fn some_function(value: PassFatPointerAndRead<&[u8]>) -> bool {
|
||||
//! value.iter().all(|v| *v > 125)
|
||||
//! }
|
||||
//! }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! For more information on declaring a runtime interface, see
|
||||
//! [`#[runtime_interface]`](./attr.runtime_interface.html).
|
||||
|
||||
#![no_std]
|
||||
|
||||
pub extern crate alloc;
|
||||
extern crate self as pezsp_runtime_interface;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
pub use pezsp_wasm_interface;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use pezsp_tracing;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use pezsp_std;
|
||||
|
||||
/// Attribute macro for transforming a trait declaration into a runtime interface.
|
||||
///
|
||||
/// A runtime interface is a fixed interface between a Bizinikiwi compatible runtime and the
|
||||
/// native node. This interface is callable from a native and a wasm runtime. The macro will
|
||||
/// generate the corresponding code for the native implementation and the code for calling from
|
||||
/// the wasm side to the native implementation.
|
||||
///
|
||||
/// The macro expects the runtime interface declaration as trait declaration:
|
||||
///
|
||||
/// ```
|
||||
/// # mod wrapper {
|
||||
/// # use pezsp_runtime_interface::runtime_interface;
|
||||
/// # use pezsp_runtime_interface::pass_by::{PassFatPointerAndDecode, PassFatPointerAndRead, AllocateAndReturnFatPointer};
|
||||
///
|
||||
/// #[runtime_interface]
|
||||
/// trait Interface {
|
||||
/// /// A function that can be called from native/wasm.
|
||||
/// ///
|
||||
/// /// The implementation given to this function is only compiled on native.
|
||||
/// fn call(data: PassFatPointerAndRead<&[u8]>) -> AllocateAndReturnFatPointer<Vec<u8>> {
|
||||
/// // Here you could call some rather complex code that only compiles on native or
|
||||
/// // is way faster in native than executing it in wasm.
|
||||
/// Vec::new()
|
||||
/// }
|
||||
/// /// Call function, but different version.
|
||||
/// ///
|
||||
/// /// For new runtimes, only function with latest version is reachable.
|
||||
/// /// But old version (above) is still accessible for old runtimes.
|
||||
/// /// Default version is 1.
|
||||
/// #[version(2)]
|
||||
/// fn call(data: PassFatPointerAndRead<&[u8]>) -> AllocateAndReturnFatPointer<Vec<u8>> {
|
||||
/// // Here you could call some rather complex code that only compiles on native or
|
||||
/// // is way faster in native than executing it in wasm.
|
||||
/// [17].to_vec()
|
||||
/// }
|
||||
///
|
||||
/// /// Call function, different version and only being registered.
|
||||
/// ///
|
||||
/// /// This `register_only` version is only being registered, aka exposed to the runtime,
|
||||
/// /// but the runtime will still use the version 2 of this function. This is useful for when
|
||||
/// /// new host functions should be introduced. Adding new host functions requires that all
|
||||
/// /// nodes have the host functions available, because otherwise they fail at instantiation
|
||||
/// /// of the runtime. With `register_only` the function will not be used when compiling the
|
||||
/// /// runtime, but it will already be there for a future version of the runtime that will
|
||||
/// /// switch to using these host function.
|
||||
/// #[version(3, register_only)]
|
||||
/// fn call(data: PassFatPointerAndRead<&[u8]>) -> AllocateAndReturnFatPointer<Vec<u8>> {
|
||||
/// // Here you could call some rather complex code that only compiles on native or
|
||||
/// // is way faster in native than executing it in wasm.
|
||||
/// [18].to_vec()
|
||||
/// }
|
||||
///
|
||||
/// /// A function can take a `&self` or `&mut self` argument to get access to the
|
||||
/// /// `Externalities`. (The generated method does not require
|
||||
/// /// this argument, so the function can be called just with the `optional` argument)
|
||||
/// fn set_or_clear(&mut self, optional: PassFatPointerAndDecode<Option<Vec<u8>>>) {
|
||||
/// match optional {
|
||||
/// Some(value) => self.set_storage([1, 2, 3, 4].to_vec(), value),
|
||||
/// None => self.clear_storage(&[1, 2, 3, 4]),
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// /// A function can be gated behind a configuration (`cfg`) attribute.
|
||||
/// /// To prevent ambiguity and confusion about what will be the final exposed host
|
||||
/// /// functions list, conditionally compiled functions can't be versioned.
|
||||
/// /// That is, conditionally compiled functions with `version`s greater than 1
|
||||
/// /// are not allowed.
|
||||
/// #[cfg(feature = "experimental-function")]
|
||||
/// fn gated_call(data: PassFatPointerAndRead<&[u8]>) -> AllocateAndReturnFatPointer<Vec<u8>> {
|
||||
/// [42].to_vec()
|
||||
/// }
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// The given example will generate roughly the following code for native:
|
||||
///
|
||||
/// ```
|
||||
/// // The name of the trait is converted to snake case and used as mod name.
|
||||
/// //
|
||||
/// // Be aware that this module is not `public`, the visibility of the module is determined based
|
||||
/// // on the visibility of the trait declaration.
|
||||
/// mod interface {
|
||||
/// trait Interface {
|
||||
/// fn call_version_1(data: &[u8]) -> Vec<u8>;
|
||||
/// fn call_version_2(data: &[u8]) -> Vec<u8>;
|
||||
/// fn call_version_3(data: &[u8]) -> Vec<u8>;
|
||||
/// fn set_or_clear_version_1(&mut self, optional: Option<Vec<u8>>);
|
||||
/// #[cfg(feature = "experimental-function")]
|
||||
/// fn gated_call_version_1(data: &[u8]) -> Vec<u8>;
|
||||
/// }
|
||||
///
|
||||
/// impl Interface for &mut dyn pezsp_externalities::Externalities {
|
||||
/// fn call_version_1(data: &[u8]) -> Vec<u8> { Vec::new() }
|
||||
/// fn call_version_2(data: &[u8]) -> Vec<u8> { [17].to_vec() }
|
||||
/// fn call_version_3(data: &[u8]) -> Vec<u8> { [18].to_vec() }
|
||||
/// fn set_or_clear_version_1(&mut self, optional: Option<Vec<u8>>) {
|
||||
/// match optional {
|
||||
/// Some(value) => self.set_storage([1, 2, 3, 4].to_vec(), value),
|
||||
/// None => self.clear_storage(&[1, 2, 3, 4]),
|
||||
/// }
|
||||
/// }
|
||||
/// #[cfg(feature = "experimental-function")]
|
||||
/// fn gated_call_version_1(data: &[u8]) -> Vec<u8> { [42].to_vec() }
|
||||
/// }
|
||||
///
|
||||
/// pub fn call(data: &[u8]) -> Vec<u8> {
|
||||
/// // only latest version is exposed
|
||||
/// call_version_2(data)
|
||||
/// }
|
||||
///
|
||||
/// fn call_version_1(data: &[u8]) -> Vec<u8> {
|
||||
/// <&mut dyn pezsp_externalities::Externalities as Interface>::call_version_1(data)
|
||||
/// }
|
||||
///
|
||||
/// fn call_version_2(data: &[u8]) -> Vec<u8> {
|
||||
/// <&mut dyn pezsp_externalities::Externalities as Interface>::call_version_2(data)
|
||||
/// }
|
||||
///
|
||||
/// fn call_version_3(data: &[u8]) -> Vec<u8> {
|
||||
/// <&mut dyn pezsp_externalities::Externalities as Interface>::call_version_3(data)
|
||||
/// }
|
||||
///
|
||||
/// pub fn set_or_clear(optional: Option<Vec<u8>>) {
|
||||
/// set_or_clear_version_1(optional)
|
||||
/// }
|
||||
///
|
||||
/// fn set_or_clear_version_1(optional: Option<Vec<u8>>) {
|
||||
/// pezsp_externalities::with_externalities(|mut ext| Interface::set_or_clear_version_1(&mut ext, optional))
|
||||
/// .expect("`set_or_clear` called outside of an Externalities-provided environment.")
|
||||
/// }
|
||||
///
|
||||
/// #[cfg(feature = "experimental-function")]
|
||||
/// pub fn gated_call(data: &[u8]) -> Vec<u8> {
|
||||
/// gated_call_version_1(data)
|
||||
/// }
|
||||
///
|
||||
/// #[cfg(feature = "experimental-function")]
|
||||
/// fn gated_call_version_1(data: &[u8]) -> Vec<u8> {
|
||||
/// <&mut dyn pezsp_externalities::Externalities as Interface>::gated_call_version_1(data)
|
||||
/// }
|
||||
///
|
||||
/// /// This type implements the `HostFunctions` trait (from `sp-wasm-interface`) and
|
||||
/// /// provides the host implementation for the wasm side. The host implementation converts the
|
||||
/// /// arguments from wasm to native and calls the corresponding native function.
|
||||
/// ///
|
||||
/// /// This type needs to be passed to the wasm executor, so that the host functions will be
|
||||
/// /// registered in the executor.
|
||||
/// pub struct HostFunctions;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The given example will generate roughly the following code for wasm:
|
||||
///
|
||||
/// ```
|
||||
/// mod interface {
|
||||
/// mod extern_host_functions_impls {
|
||||
/// /// Every function is exported by the native code as `ext_FUNCTION_NAME_version_VERSION`.
|
||||
/// ///
|
||||
/// /// The type for each argument of the exported function depends on
|
||||
/// /// `<ARGUMENT_TYPE as RIType>::FFIType`.
|
||||
/// ///
|
||||
/// /// `key` holds the pointer and the length to the `data` slice.
|
||||
/// pub fn call(data: &[u8]) -> Vec<u8> {
|
||||
/// extern "C" { pub fn ext_call_version_2(key: u64); }
|
||||
/// // Should call into external `ext_call_version_2(<[u8] as IntoFFIValue>::into_ffi_value(key))`
|
||||
/// // But this is too much to replicate in a doc test so here we just return a dummy vector.
|
||||
/// // Note that we jump into the latest version not marked as `register_only` (i.e. version 2).
|
||||
/// Vec::new()
|
||||
/// }
|
||||
///
|
||||
/// /// `key` holds the pointer and the length of the `option` value.
|
||||
/// pub fn set_or_clear(option: Option<Vec<u8>>) {
|
||||
/// extern "C" { pub fn ext_set_or_clear_version_1(key: u64); }
|
||||
/// // Same as above
|
||||
/// }
|
||||
///
|
||||
/// /// `key` holds the pointer and the length to the `data` slice.
|
||||
/// #[cfg(feature = "experimental-function")]
|
||||
/// pub fn gated_call(data: &[u8]) -> Vec<u8> {
|
||||
/// extern "C" { pub fn ext_gated_call_version_1(key: u64); }
|
||||
/// /// Same as above
|
||||
/// Vec::new()
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// /// The type is actually `ExchangeableFunction` (from `sp-runtime-interface`) and
|
||||
/// /// by default this is initialized to jump into the corresponding function in
|
||||
/// /// `extern_host_functions_impls`.
|
||||
/// ///
|
||||
/// /// This can be used to replace the implementation of the `call` function.
|
||||
/// /// Instead of calling into the host, the callee will automatically call the other
|
||||
/// /// implementation.
|
||||
/// ///
|
||||
/// /// To replace the implementation:
|
||||
/// ///
|
||||
/// /// `host_call.replace_implementation(some_other_impl)`
|
||||
/// pub static host_call: () = ();
|
||||
/// pub static host_set_or_clear: () = ();
|
||||
/// #[cfg(feature = "experimental-feature")]
|
||||
/// pub static gated_call: () = ();
|
||||
///
|
||||
/// pub fn call(data: &[u8]) -> Vec<u8> {
|
||||
/// // This is the actual call: `host_call.get()(data)`
|
||||
/// //
|
||||
/// // But that does not work for several reasons in this example, so we just return an
|
||||
/// // empty vector.
|
||||
/// Vec::new()
|
||||
/// }
|
||||
///
|
||||
/// pub fn set_or_clear(optional: Option<Vec<u8>>) {
|
||||
/// // Same as above
|
||||
/// }
|
||||
///
|
||||
/// #[cfg(feature = "experimental-feature")]
|
||||
/// pub fn gated_call(data: &[u8]) -> Vec<u8> {
|
||||
/// // Same as above
|
||||
/// Vec::new()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Argument and return types
|
||||
///
|
||||
/// Every argument type and return type must be wrapped in a marker newtype specifying the
|
||||
/// marshalling strategy used to pass the value through the FFI boundary between the host
|
||||
/// and the runtime. The only exceptions to this rule are a couple of basic, primitive types
|
||||
/// which can be passed directly through the FFI boundary and which don't require any special
|
||||
/// handling besides a straightforward, direct conversion.
|
||||
///
|
||||
/// The following table documents those types which can be passed between the host and the
|
||||
/// runtime without a marshalling strategy wrapper:
|
||||
///
|
||||
/// | Type | FFI type | Conversion |
|
||||
/// |----|----|----|
|
||||
/// | `u8` | `u32` | zero-extended to 32-bits |
|
||||
/// | `u16` | `u32` | zero-extended to 32-bits |
|
||||
/// | `u32` | `u32` | `Identity` |
|
||||
/// | `u64` | `u64` | `Identity` |
|
||||
/// | `i8` | `i32` | sign-extended to 32-bits |
|
||||
/// | `i16` | `i32` | sign-extended to 32-bits |
|
||||
/// | `i32` | `i32` | `Identity` |
|
||||
/// | `i64` | `i64` | `Identity` |
|
||||
/// | `bool` | `u32` | `if v { 1 } else { 0 }` |
|
||||
/// | `*const T` | `u32` | `Identity` |
|
||||
///
|
||||
/// `Identity` means that the value is passed as-is directly in a bit-exact fashion.
|
||||
///
|
||||
/// You can find the strategy wrapper types in the [`crate::pass_by`] module.
|
||||
///
|
||||
/// The newtype wrappers are automatically stripped away when the function is called
|
||||
/// and applied when the function returns by the `runtime_interface` macro.
|
||||
///
|
||||
/// # Wasm only interfaces
|
||||
///
|
||||
/// Some interfaces are only required from within the wasm runtime e.g. the allocator
|
||||
/// interface. To support this, the macro can be called like `#[runtime_interface(wasm_only)]`.
|
||||
/// This instructs the macro to make two significant changes to the generated code:
|
||||
///
|
||||
/// 1. The generated functions are not callable from the native side.
|
||||
/// 2. The trait as shown above is not implemented for [`Externalities`] and is instead
|
||||
/// implemented for `FunctionContext` (from `sp-wasm-interface`).
|
||||
///
|
||||
/// # Disable tracing
|
||||
/// By adding `no_tracing` to the list of options you can prevent the wasm-side interface from
|
||||
/// generating the default `sp-tracing`-calls. Note that this is rarely needed but only meant
|
||||
/// for the case when that would create a circular dependency. You usually _do not_ want to add
|
||||
/// this flag, as tracing doesn't cost you anything by default anyways (it is added as a no-op)
|
||||
/// but is super useful for debugging later.
|
||||
pub use pezsp_runtime_interface_proc_macro::runtime_interface;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
pub use pezsp_externalities::{
|
||||
set_and_run_with_externalities, with_externalities, ExtensionStore, Externalities,
|
||||
ExternalitiesExt,
|
||||
};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use codec;
|
||||
|
||||
#[cfg(all(any(target_arch = "riscv32", target_arch = "riscv64"), bizinikiwi_runtime))]
|
||||
pub mod polkavm;
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
pub mod host;
|
||||
pub(crate) mod impls;
|
||||
pub mod pass_by;
|
||||
#[cfg(any(bizinikiwi_runtime, doc))]
|
||||
pub mod wasm;
|
||||
|
||||
mod util;
|
||||
|
||||
pub use util::{pack_ptr_and_len, unpack_ptr_and_len};
|
||||
|
||||
/// Something that can be used by the runtime interface as type to communicate between the runtime
|
||||
/// and the host.
|
||||
///
|
||||
/// Every type that should be used in a runtime interface function signature needs to implement
|
||||
/// this trait.
|
||||
pub trait RIType: Sized {
|
||||
/// The raw FFI type that is used to pass `Self` through the host <-> runtime boundary.
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
type FFIType: pezsp_wasm_interface::IntoValue
|
||||
+ pezsp_wasm_interface::TryFromValue
|
||||
+ pezsp_wasm_interface::WasmTy;
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
type FFIType;
|
||||
|
||||
/// The inner type without any serialization strategy wrapper.
|
||||
type Inner;
|
||||
}
|
||||
|
||||
/// A raw pointer that can be used in a runtime interface function signature.
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
pub type Pointer<T> = *mut T;
|
||||
|
||||
/// A raw pointer that can be used in a runtime interface function signature.
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
pub type Pointer<T> = pezsp_wasm_interface::Pointer<T>;
|
||||
@@ -0,0 +1,712 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Provides host <-> runtime FFI marshalling strategy newtype wrappers
|
||||
//! for defining runtime interfaces.
|
||||
|
||||
use crate::{
|
||||
util::{pack_ptr_and_len, unpack_ptr_and_len},
|
||||
RIType,
|
||||
};
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
use crate::host::*;
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
use crate::wasm::*;
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
use pezsp_wasm_interface::{FunctionContext, Pointer, Result};
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
use alloc::{format, string::String};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::{any::type_name, marker::PhantomData};
|
||||
|
||||
/// Pass a value into the host by a thin pointer.
|
||||
///
|
||||
/// This casts the value into a `&[u8]` using `AsRef<[u8]>` and passes a pointer to that byte blob
|
||||
/// to the host. Then the host reads `N` bytes from that address into an `[u8; N]`, converts it
|
||||
/// into target type using `From<[u8; N]>` and passes it into the host function by a copy.
|
||||
///
|
||||
/// Use [`PassPointerAndRead`] if you want to have the host function accept a reference type
|
||||
/// on the host side or if you'd like to avoid the extra copy.
|
||||
///
|
||||
/// Raw FFI type: `u32` (a pointer)
|
||||
pub struct PassPointerAndReadCopy<T, const N: usize>(PhantomData<(T, [u8; N])>);
|
||||
|
||||
impl<T, const N: usize> RIType for PassPointerAndReadCopy<T, N> {
|
||||
type FFIType = u32;
|
||||
type Inner = T;
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<'a, T, const N: usize> FromFFIValue<'a> for PassPointerAndReadCopy<T, N>
|
||||
where
|
||||
T: From<[u8; N]> + Copy,
|
||||
{
|
||||
type Owned = T;
|
||||
|
||||
fn from_ffi_value(
|
||||
context: &mut dyn FunctionContext,
|
||||
arg: Self::FFIType,
|
||||
) -> Result<Self::Owned> {
|
||||
let mut out = [0; N];
|
||||
context.read_memory_into(Pointer::new(arg), &mut out)?;
|
||||
Ok(T::from(out))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
|
||||
*owned
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl<T, const N: usize> IntoFFIValue for PassPointerAndReadCopy<T, N>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
type Destructor = ();
|
||||
|
||||
fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
|
||||
// Using an 'assert' instead of a 'T: AsRef<[u8; N]>` bound since a '[u8; N]' *doesn't*
|
||||
// implement it.
|
||||
assert_eq!(value.as_ref().len(), N);
|
||||
(value.as_ref().as_ptr() as u32, ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Pass a value into the host by a thin pointer.
|
||||
///
|
||||
/// This casts the value into a `&[u8]` using `AsRef<[u8]>` and passes a pointer to that byte blob
|
||||
/// to the host. Then the host reads `N` bytes from that address into an `[u8; N]`, converts it
|
||||
/// into target type using `From<[u8; N]>` and passes it into the host function by a reference.
|
||||
///
|
||||
/// This can only be used with reference types (e.g. `&[u8; 32]`). Use [`PassPointerAndReadCopy`]
|
||||
/// if you want to have the host function accept a non-reference type on the host side.
|
||||
///
|
||||
/// Raw FFI type: `u32` (a pointer)
|
||||
pub struct PassPointerAndRead<T, const N: usize>(PhantomData<(T, [u8; N])>);
|
||||
|
||||
impl<'a, T, const N: usize> RIType for PassPointerAndRead<&'a T, N> {
|
||||
type FFIType = u32;
|
||||
type Inner = &'a T;
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<'a, T, const N: usize> FromFFIValue<'a> for PassPointerAndRead<&'a T, N>
|
||||
where
|
||||
T: From<[u8; N]>,
|
||||
{
|
||||
type Owned = T;
|
||||
|
||||
fn from_ffi_value(
|
||||
context: &mut dyn FunctionContext,
|
||||
arg: Self::FFIType,
|
||||
) -> Result<Self::Owned> {
|
||||
let mut out = [0; N];
|
||||
context.read_memory_into(Pointer::new(arg), &mut out)?;
|
||||
Ok(T::from(out))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
|
||||
&*owned
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl<'a, T, const N: usize> IntoFFIValue for PassPointerAndRead<&'a T, N>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
type Destructor = ();
|
||||
|
||||
fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
|
||||
assert_eq!(value.as_ref().len(), N);
|
||||
(value.as_ref().as_ptr() as u32, ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Pass a value into the host by a fat pointer.
|
||||
///
|
||||
/// This casts the value into a `&[u8]` and passes a pointer to that byte blob and its length
|
||||
/// to the host. Then the host reads that blob and converts it into an owned type and passes it
|
||||
/// (either as an owned type or as a reference) to the host function.
|
||||
///
|
||||
/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
|
||||
pub struct PassFatPointerAndRead<T>(PhantomData<T>);
|
||||
|
||||
impl<T> RIType for PassFatPointerAndRead<T> {
|
||||
type FFIType = u64;
|
||||
type Inner = T;
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<'a> FromFFIValue<'a> for PassFatPointerAndRead<&'a [u8]> {
|
||||
type Owned = Vec<u8>;
|
||||
|
||||
fn from_ffi_value(
|
||||
context: &mut dyn FunctionContext,
|
||||
arg: Self::FFIType,
|
||||
) -> Result<Self::Owned> {
|
||||
let (ptr, len) = unpack_ptr_and_len(arg);
|
||||
context.read_memory(Pointer::new(ptr), len)
|
||||
}
|
||||
|
||||
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
|
||||
&*owned
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<'a> FromFFIValue<'a> for PassFatPointerAndRead<&'a str> {
|
||||
type Owned = String;
|
||||
|
||||
fn from_ffi_value(
|
||||
context: &mut dyn FunctionContext,
|
||||
arg: Self::FFIType,
|
||||
) -> Result<Self::Owned> {
|
||||
let (ptr, len) = unpack_ptr_and_len(arg);
|
||||
let vec = context.read_memory(Pointer::new(ptr), len)?;
|
||||
String::from_utf8(vec).map_err(|_| "could not parse '&str' when marshalling hostcall's arguments through the FFI boundary: the string is not valid UTF-8".into())
|
||||
}
|
||||
|
||||
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
|
||||
&*owned
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<'a> FromFFIValue<'a> for PassFatPointerAndRead<Vec<u8>> {
|
||||
type Owned = Vec<u8>;
|
||||
|
||||
fn from_ffi_value(
|
||||
context: &mut dyn FunctionContext,
|
||||
arg: Self::FFIType,
|
||||
) -> Result<Self::Owned> {
|
||||
<PassFatPointerAndRead<&[u8]> as FromFFIValue>::from_ffi_value(context, arg)
|
||||
}
|
||||
|
||||
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
|
||||
core::mem::take(owned)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl<T> IntoFFIValue for PassFatPointerAndRead<T>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
type Destructor = ();
|
||||
|
||||
fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
|
||||
let value = value.as_ref();
|
||||
(pack_ptr_and_len(value.as_ptr() as u32, value.len() as u32), ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Pass a value into the host by a fat pointer, writing it back after the host call ends.
|
||||
///
|
||||
/// This casts the value into a `&mut [u8]` and passes a pointer to that byte blob and its length
|
||||
/// to the host. Then the host reads that blob and converts it into an owned type and passes it
|
||||
/// as a mutable reference to the host function. After the host function finishes the byte blob
|
||||
/// is written back into the guest memory.
|
||||
///
|
||||
/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
|
||||
pub struct PassFatPointerAndReadWrite<T>(PhantomData<T>);
|
||||
|
||||
impl<T> RIType for PassFatPointerAndReadWrite<T> {
|
||||
type FFIType = u64;
|
||||
type Inner = T;
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<'a> FromFFIValue<'a> for PassFatPointerAndReadWrite<&'a mut [u8]> {
|
||||
type Owned = Vec<u8>;
|
||||
|
||||
fn from_ffi_value(
|
||||
context: &mut dyn FunctionContext,
|
||||
arg: Self::FFIType,
|
||||
) -> Result<Self::Owned> {
|
||||
let (ptr, len) = unpack_ptr_and_len(arg);
|
||||
context.read_memory(Pointer::new(ptr), len)
|
||||
}
|
||||
|
||||
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
|
||||
&mut *owned
|
||||
}
|
||||
|
||||
fn write_back_into_runtime(
|
||||
value: Self::Owned,
|
||||
context: &mut dyn FunctionContext,
|
||||
arg: Self::FFIType,
|
||||
) -> Result<()> {
|
||||
let (ptr, len) = unpack_ptr_and_len(arg);
|
||||
assert_eq!(len as usize, value.len());
|
||||
context.write_memory(Pointer::new(ptr), &value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl<'a> IntoFFIValue for PassFatPointerAndReadWrite<&'a mut [u8]> {
|
||||
type Destructor = ();
|
||||
|
||||
fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
|
||||
(pack_ptr_and_len(value.as_ptr() as u32, value.len() as u32), ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Pass a pointer into the host and write to it after the host call ends.
|
||||
///
|
||||
/// This casts a given type into `&mut [u8]` using `AsMut<[u8]>` and passes a pointer to
|
||||
/// that byte slice into the host. The host *doesn't* read from this and instead creates
|
||||
/// a default instance of type `T` and passes it as a `&mut T` into the host function
|
||||
/// implementation. After the host function finishes this value is then cast into a `&[u8]` using
|
||||
/// `AsRef<[u8]>` and written back into the guest memory.
|
||||
///
|
||||
/// Raw FFI type: `u32` (a pointer)
|
||||
pub struct PassPointerAndWrite<T, const N: usize>(PhantomData<(T, [u8; N])>);
|
||||
|
||||
impl<T, const N: usize> RIType for PassPointerAndWrite<T, N> {
|
||||
type FFIType = u32;
|
||||
type Inner = T;
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<'a, T, const N: usize> FromFFIValue<'a> for PassPointerAndWrite<&'a mut T, N>
|
||||
where
|
||||
T: Default + AsRef<[u8]>,
|
||||
{
|
||||
type Owned = T;
|
||||
|
||||
fn from_ffi_value(
|
||||
_context: &mut dyn FunctionContext,
|
||||
_arg: Self::FFIType,
|
||||
) -> Result<Self::Owned> {
|
||||
Ok(T::default())
|
||||
}
|
||||
|
||||
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
|
||||
&mut *owned
|
||||
}
|
||||
|
||||
fn write_back_into_runtime(
|
||||
value: Self::Owned,
|
||||
context: &mut dyn FunctionContext,
|
||||
arg: Self::FFIType,
|
||||
) -> Result<()> {
|
||||
let value = value.as_ref();
|
||||
assert_eq!(value.len(), N);
|
||||
context.write_memory(Pointer::new(arg), value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl<'a, T, const N: usize> IntoFFIValue for PassPointerAndWrite<&'a mut T, N>
|
||||
where
|
||||
T: AsMut<[u8]>,
|
||||
{
|
||||
type Destructor = ();
|
||||
|
||||
fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
|
||||
let value = value.as_mut();
|
||||
assert_eq!(value.len(), N);
|
||||
(value.as_ptr() as u32, ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Pass a `T` into the host using the SCALE codec.
|
||||
///
|
||||
/// This encodes a `T` into a `Vec<u8>` using the SCALE codec and then
|
||||
/// passes a pointer to that byte blob and its length to the host,
|
||||
/// which then reads it and decodes back into `T`.
|
||||
///
|
||||
/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
|
||||
pub struct PassFatPointerAndDecode<T>(PhantomData<T>);
|
||||
|
||||
impl<T> RIType for PassFatPointerAndDecode<T> {
|
||||
type FFIType = u64;
|
||||
type Inner = T;
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<'a, T: codec::Decode> FromFFIValue<'a> for PassFatPointerAndDecode<T> {
|
||||
type Owned = Option<T>;
|
||||
|
||||
fn from_ffi_value(
|
||||
context: &mut dyn FunctionContext,
|
||||
arg: Self::FFIType,
|
||||
) -> Result<Self::Owned> {
|
||||
let (ptr, len) = unpack_ptr_and_len(arg);
|
||||
let vec = context.read_memory(Pointer::new(ptr), len)?;
|
||||
T::decode(&mut &vec[..]).map_err(|error| format!(
|
||||
"could not SCALE-decode '{}' when marshalling hostcall's arguments through the FFI boundary: {error}",
|
||||
type_name::<T>())
|
||||
).map(Some)
|
||||
}
|
||||
|
||||
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
|
||||
owned.take().expect("this is called only once and is never 'None'")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl<T: codec::Encode> IntoFFIValue for PassFatPointerAndDecode<T> {
|
||||
type Destructor = Vec<u8>;
|
||||
|
||||
fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
|
||||
let data = value.encode();
|
||||
(pack_ptr_and_len(data.as_ptr() as u32, data.len() as u32), data)
|
||||
}
|
||||
}
|
||||
|
||||
/// Pass a `&[T]` into the host using the SCALE codec.
|
||||
///
|
||||
/// This encodes a `&[T]` into a `Vec<u8>` using the SCALE codec and then
|
||||
/// passes a pointer to that byte blob and its length to the host,
|
||||
/// which then reads it and decodes back into `Vec<T>` and passes
|
||||
/// a reference to that (as `&[T]`) into the host function.
|
||||
///
|
||||
/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
|
||||
pub struct PassFatPointerAndDecodeSlice<T>(PhantomData<T>);
|
||||
|
||||
impl<T> RIType for PassFatPointerAndDecodeSlice<T> {
|
||||
type FFIType = u64;
|
||||
type Inner = T;
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<'a, T: codec::Decode> FromFFIValue<'a> for PassFatPointerAndDecodeSlice<&'a [T]> {
|
||||
type Owned = Vec<T>;
|
||||
|
||||
fn from_ffi_value(
|
||||
context: &mut dyn FunctionContext,
|
||||
arg: Self::FFIType,
|
||||
) -> Result<Self::Owned> {
|
||||
let (ptr, len) = unpack_ptr_and_len(arg);
|
||||
let vec = context.read_memory(Pointer::new(ptr), len)?;
|
||||
<Vec::<T> as codec::Decode>::decode(&mut &vec[..]).map_err(|error| format!(
|
||||
"could not SCALE-decode '{}' when marshalling hostcall's arguments through the FFI boundary: {error}",
|
||||
type_name::<Vec<T>>()
|
||||
))
|
||||
}
|
||||
|
||||
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
|
||||
&*owned
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl<'a, T: codec::Encode> IntoFFIValue for PassFatPointerAndDecodeSlice<&'a [T]> {
|
||||
type Destructor = Vec<u8>;
|
||||
|
||||
fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
|
||||
let data = codec::Encode::encode(value);
|
||||
(pack_ptr_and_len(data.as_ptr() as u32, data.len() as u32), data)
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait signifying a primitive type.
|
||||
trait Primitive: Copy {}
|
||||
|
||||
impl Primitive for u8 {}
|
||||
impl Primitive for u16 {}
|
||||
impl Primitive for u32 {}
|
||||
impl Primitive for u64 {}
|
||||
|
||||
impl Primitive for i8 {}
|
||||
impl Primitive for i16 {}
|
||||
impl Primitive for i32 {}
|
||||
impl Primitive for i64 {}
|
||||
|
||||
/// Pass `T` through the FFI boundary by first converting it to `U` in the runtime, and then
|
||||
/// converting it back to `T` on the host's side.
|
||||
///
|
||||
/// Raw FFI type: same as `U`'s FFI type
|
||||
pub struct PassAs<T, U>(PhantomData<(T, U)>);
|
||||
|
||||
impl<T, U> RIType for PassAs<T, U>
|
||||
where
|
||||
U: RIType,
|
||||
{
|
||||
type FFIType = <U as RIType>::FFIType;
|
||||
type Inner = T;
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<'a, T, U> FromFFIValue<'a> for PassAs<T, U>
|
||||
where
|
||||
U: RIType + FromFFIValue<'a> + Primitive,
|
||||
T: TryFrom<<U as FromFFIValue<'a>>::Owned> + Copy,
|
||||
{
|
||||
type Owned = T;
|
||||
|
||||
fn from_ffi_value(
|
||||
context: &mut dyn FunctionContext,
|
||||
arg: Self::FFIType,
|
||||
) -> Result<Self::Owned> {
|
||||
<U as FromFFIValue>::from_ffi_value(context, arg).and_then(|value| value.try_into()
|
||||
.map_err(|_| format!(
|
||||
"failed to convert '{}' (passed as '{}') into '{}' when marshalling hostcall's arguments through the FFI boundary",
|
||||
type_name::<U>(),
|
||||
type_name::<Self::FFIType>(),
|
||||
type_name::<Self::Owned>()
|
||||
)))
|
||||
}
|
||||
|
||||
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
|
||||
*owned
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl<T, U> IntoFFIValue for PassAs<T, U>
|
||||
where
|
||||
U: RIType + IntoFFIValue + Primitive,
|
||||
U::Inner: From<T>,
|
||||
T: Copy,
|
||||
{
|
||||
type Destructor = <U as IntoFFIValue>::Destructor;
|
||||
|
||||
fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
|
||||
let mut value = U::Inner::from(*value);
|
||||
<U as IntoFFIValue>::into_ffi_value(&mut value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `T` through the FFI boundary by first converting it to `U` on the host's side, and then
|
||||
/// converting it back to `T` in the runtime.
|
||||
///
|
||||
/// Raw FFI type: same as `U`'s FFI type
|
||||
pub struct ReturnAs<T, U>(PhantomData<(T, U)>);
|
||||
|
||||
impl<T, U> RIType for ReturnAs<T, U>
|
||||
where
|
||||
U: RIType,
|
||||
{
|
||||
type FFIType = <U as RIType>::FFIType;
|
||||
type Inner = T;
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<T, U> IntoFFIValue for ReturnAs<T, U>
|
||||
where
|
||||
U: RIType + IntoFFIValue + Primitive,
|
||||
<U as RIType>::Inner: From<Self::Inner>,
|
||||
{
|
||||
fn into_ffi_value(
|
||||
value: Self::Inner,
|
||||
context: &mut dyn FunctionContext,
|
||||
) -> Result<Self::FFIType> {
|
||||
let value: <U as RIType>::Inner = value.into();
|
||||
<U as IntoFFIValue>::into_ffi_value(value, context)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl<T, U> FromFFIValue for ReturnAs<T, U>
|
||||
where
|
||||
U: RIType + FromFFIValue + Primitive,
|
||||
Self::Inner: TryFrom<U::Inner>,
|
||||
{
|
||||
fn from_ffi_value(arg: Self::FFIType) -> Self::Inner {
|
||||
let value = <U as FromFFIValue>::from_ffi_value(arg);
|
||||
match Self::Inner::try_from(value) {
|
||||
Ok(value) => value,
|
||||
Err(_) => {
|
||||
panic!(
|
||||
"failed to convert '{}' (passed as '{}') into a '{}' when marshalling a hostcall's return value through the FFI boundary",
|
||||
type_name::<U::Inner>(),
|
||||
type_name::<Self::FFIType>(),
|
||||
type_name::<Self::Inner>()
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// (DEPRECATED) Return `T` as a blob of bytes into the runtime.
|
||||
///
|
||||
/// Uses `T::AsRef<[u8]>` to cast `T` into a `&[u8]`, allocates runtime memory
|
||||
/// using the legacy allocator, copies the slice into the runtime memory, and
|
||||
/// returns a pointer to it.
|
||||
///
|
||||
/// THIS STRATEGY IS DEPRECATED; DO NOT USE FOR NEW HOST FUNCTIONS!
|
||||
///
|
||||
/// Ideally use a mutable slice to return data to the guest, for example using
|
||||
/// the [`PassPointerAndWrite`] strategy.
|
||||
///
|
||||
/// Raw FFI type: `u32` (a pointer to the byte blob)
|
||||
pub struct AllocateAndReturnPointer<T, const N: usize>(PhantomData<(T, [u8; N])>);
|
||||
|
||||
impl<T, const N: usize> RIType for AllocateAndReturnPointer<T, N> {
|
||||
type FFIType = u32;
|
||||
type Inner = T;
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<T, const N: usize> IntoFFIValue for AllocateAndReturnPointer<T, N>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn into_ffi_value(
|
||||
value: Self::Inner,
|
||||
context: &mut dyn FunctionContext,
|
||||
) -> Result<Self::FFIType> {
|
||||
let value = value.as_ref();
|
||||
assert_eq!(
|
||||
value.len(),
|
||||
N,
|
||||
"expected the byte blob to be {N} bytes long, is {} bytes when returning '{}' from a host function",
|
||||
value.len(),
|
||||
type_name::<T>()
|
||||
);
|
||||
|
||||
let addr = context.allocate_memory(value.len() as u32)?;
|
||||
context.write_memory(addr, value)?;
|
||||
Ok(addr.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl<T: codec::Decode, const N: usize> FromFFIValue for AllocateAndReturnPointer<T, N>
|
||||
where
|
||||
T: From<[u8; N]>,
|
||||
{
|
||||
fn from_ffi_value(arg: Self::FFIType) -> Self::Inner {
|
||||
// SAFETY: This memory was allocated by the host allocator with the exact
|
||||
// capacity needed, so it's safe to make a `Vec` out of it.
|
||||
let value = unsafe { Vec::from_raw_parts(arg as *mut u8, N, N) };
|
||||
|
||||
// SAFETY: Reading a `[u8; N]` from a `&[u8]` which is at least `N` elements long is safe.
|
||||
let array = unsafe { *(value.as_ptr() as *const [u8; N]) };
|
||||
T::from(array)
|
||||
}
|
||||
}
|
||||
|
||||
/// (DEPRECATED) Return `T` as a blob of bytes into the runtime.
|
||||
///
|
||||
/// Uses `T::AsRef<[u8]>` to cast `T` into a `&[u8]`, allocates runtime memory
|
||||
/// using the legacy allocator, copies the slice into the runtime memory, and
|
||||
/// returns a pointer to it.
|
||||
///
|
||||
/// THIS STRATEGY IS DEPRECATED; DO NOT USE FOR NEW HOST FUNCTIONS!
|
||||
///
|
||||
/// Ideally use a mutable slice to return data to the guest, for example using
|
||||
/// the [`PassPointerAndWrite`] strategy.
|
||||
///
|
||||
/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
|
||||
pub struct AllocateAndReturnFatPointer<T>(PhantomData<T>);
|
||||
|
||||
impl<T> RIType for AllocateAndReturnFatPointer<T> {
|
||||
type FFIType = u64;
|
||||
type Inner = T;
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<T> IntoFFIValue for AllocateAndReturnFatPointer<T>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn into_ffi_value(
|
||||
value: Self::Inner,
|
||||
context: &mut dyn FunctionContext,
|
||||
) -> Result<Self::FFIType> {
|
||||
let value = value.as_ref();
|
||||
let ptr = context.allocate_memory(value.len() as u32)?;
|
||||
context.write_memory(ptr, &value)?;
|
||||
Ok(pack_ptr_and_len(ptr.into(), value.len() as u32))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl<T> FromFFIValue for AllocateAndReturnFatPointer<T>
|
||||
where
|
||||
T: From<Vec<u8>>,
|
||||
{
|
||||
fn from_ffi_value(arg: Self::FFIType) -> Self::Inner {
|
||||
let (ptr, len) = unpack_ptr_and_len(arg);
|
||||
let len = len as usize;
|
||||
let vec = if len == 0 {
|
||||
Vec::new()
|
||||
} else {
|
||||
// SAFETY: This memory was allocated by the host allocator with the exact
|
||||
// capacity needed, so it's safe to make a `Vec` out of it.
|
||||
unsafe { Vec::from_raw_parts(ptr as *mut u8, len, len) }
|
||||
};
|
||||
|
||||
T::from(vec)
|
||||
}
|
||||
}
|
||||
|
||||
/// (DEPRECATED) Return `T` into the runtime using the SCALE codec.
|
||||
///
|
||||
/// Encodes `T` using the SCALE codec, allocates runtime memory using the legacy
|
||||
/// allocator, copies the encoded payload into the runtime memory, and returns
|
||||
/// a fat pointer to it.
|
||||
///
|
||||
/// THIS STRATEGY IS DEPRECATED; DO NOT USE FOR NEW HOST FUNCTIONS!
|
||||
///
|
||||
/// Ideally use a mutable slice to return data to the guest, for example using
|
||||
/// the [`PassPointerAndWrite`] strategy.
|
||||
///
|
||||
/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
|
||||
pub struct AllocateAndReturnByCodec<T>(PhantomData<T>);
|
||||
|
||||
impl<T> RIType for AllocateAndReturnByCodec<T> {
|
||||
type FFIType = u64;
|
||||
type Inner = T;
|
||||
}
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl<T: codec::Encode> IntoFFIValue for AllocateAndReturnByCodec<T> {
|
||||
fn into_ffi_value(value: T, context: &mut dyn FunctionContext) -> Result<Self::FFIType> {
|
||||
let vec = value.encode();
|
||||
let ptr = context.allocate_memory(vec.len() as u32)?;
|
||||
context.write_memory(ptr, &vec)?;
|
||||
Ok(pack_ptr_and_len(ptr.into(), vec.len() as u32))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
impl<T: codec::Decode> FromFFIValue for AllocateAndReturnByCodec<T> {
|
||||
fn from_ffi_value(arg: Self::FFIType) -> Self::Inner {
|
||||
let (ptr, len) = unpack_ptr_and_len(arg);
|
||||
let len = len as usize;
|
||||
|
||||
let encoded = if len == 0 {
|
||||
bytes::Bytes::new()
|
||||
} else {
|
||||
// SAFETY: This memory was allocated by the host allocator with the exact
|
||||
// capacity needed, so it's safe to make a `Vec` out of it.
|
||||
bytes::Bytes::from(unsafe { Vec::from_raw_parts(ptr as *mut u8, len, len) })
|
||||
};
|
||||
|
||||
match codec::decode_from_bytes(encoded) {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
panic!(
|
||||
"failed to decode '{}' when marshalling a hostcall's return value through the FFI boundary: {error}",
|
||||
type_name::<T>(),
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pub use polkavm_derive::{polkavm_export, polkavm_import};
|
||||
|
||||
#[polkavm_derive::polkavm_define_abi(allow_extra_input_registers)]
|
||||
pub mod polkavm_abi {}
|
||||
|
||||
impl self::polkavm_abi::FromHost for *mut u8 {
|
||||
type Regs = (u32,);
|
||||
|
||||
#[inline]
|
||||
fn from_host((value,): Self::Regs) -> Self {
|
||||
value as *mut u8
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Various utilities that help interfacing with wasm runtime code.
|
||||
|
||||
/// Pack a pointer and length into an `u64`.
|
||||
pub fn pack_ptr_and_len(ptr: u32, len: u32) -> u64 {
|
||||
// The static assertions from above are changed into a runtime check.
|
||||
#[cfg(all(bizinikiwi_runtime, feature = "disable_target_static_assertions"))]
|
||||
assert_eq!(4, core::mem::size_of::<usize>());
|
||||
|
||||
(u64::from(len) << 32) | u64::from(ptr)
|
||||
}
|
||||
|
||||
/// Unpacks an `u64` into the pointer and length.
|
||||
///
|
||||
/// Runtime API functions return a 64-bit value which encodes a pointer in the least-significant
|
||||
/// 32-bits and a length in the most-significant 32 bits. This interprets the returned value as a
|
||||
/// pointer, length tuple.
|
||||
pub fn unpack_ptr_and_len(val: u64) -> (u32, u32) {
|
||||
// The static assertions from above are changed into a runtime check.
|
||||
#[cfg(all(bizinikiwi_runtime, feature = "disable_target_static_assertions"))]
|
||||
assert_eq!(4, core::mem::size_of::<usize>());
|
||||
|
||||
let ptr = (val & (!0u32 as u64)) as u32;
|
||||
let len = (val >> 32) as u32;
|
||||
|
||||
(ptr, len)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{pack_ptr_and_len, unpack_ptr_and_len};
|
||||
|
||||
#[test]
|
||||
fn ptr_len_packing_unpacking() {
|
||||
const PTR: u32 = 0x1337;
|
||||
const LEN: u32 = 0x7f000000;
|
||||
|
||||
let packed = pack_ptr_and_len(PTR, LEN);
|
||||
let (ptr, len) = unpack_ptr_and_len(packed);
|
||||
|
||||
assert_eq!(PTR, ptr);
|
||||
assert_eq!(LEN, len);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Traits required by the runtime interface from the wasm side.
|
||||
|
||||
use crate::RIType;
|
||||
|
||||
use core::cell::Cell;
|
||||
|
||||
/// A type used as a return value in a host function. Can be created from an FFI value.
|
||||
///
|
||||
/// Implementations are safe to assume that the `arg` given to `from_ffi_value`
|
||||
/// is only generated by the corresponding [`host::IntoFFIValue`](crate::host::IntoFFIValue)
|
||||
/// implementation.
|
||||
pub trait FromFFIValue: Sized + RIType {
|
||||
/// Create `Self::Inner` from the given FFI value.
|
||||
fn from_ffi_value(arg: Self::FFIType) -> Self::Inner;
|
||||
}
|
||||
|
||||
/// A type used as a parameter in a host function. Can be turned into an FFI value.
|
||||
pub trait IntoFFIValue: RIType {
|
||||
/// Destructor for the value passed into `into_ffi_value`.
|
||||
type Destructor;
|
||||
|
||||
/// Convert `Self::Inner` into an FFI type, with an optional destructor.
|
||||
fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor);
|
||||
}
|
||||
|
||||
/// The state of an exchangeable function.
|
||||
#[derive(Clone, Copy)]
|
||||
enum ExchangeableFunctionState {
|
||||
/// Original function is present
|
||||
Original,
|
||||
/// The function has been replaced.
|
||||
Replaced,
|
||||
}
|
||||
|
||||
/// A function which implementation can be exchanged.
|
||||
///
|
||||
/// Internally this works by swapping function pointers.
|
||||
pub struct ExchangeableFunction<T>(Cell<(T, ExchangeableFunctionState)>);
|
||||
|
||||
impl<T> ExchangeableFunction<T> {
|
||||
/// Create a new instance of `ExchangeableFunction`.
|
||||
pub const fn new(impl_: T) -> Self {
|
||||
Self(Cell::new((impl_, ExchangeableFunctionState::Original)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> ExchangeableFunction<T> {
|
||||
/// Replace the implementation with `new_impl`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics when trying to replace an already replaced implementation.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns the original implementation wrapped in [`RestoreImplementation`].
|
||||
pub fn replace_implementation(&'static self, new_impl: T) -> RestoreImplementation<T> {
|
||||
if let ExchangeableFunctionState::Replaced = self.0.get().1 {
|
||||
panic!("Trying to replace an already replaced implementation!")
|
||||
}
|
||||
|
||||
let old = self.0.replace((new_impl, ExchangeableFunctionState::Replaced));
|
||||
|
||||
RestoreImplementation(self, Some(old.0))
|
||||
}
|
||||
|
||||
/// Restore the original implementation.
|
||||
fn restore_orig_implementation(&self, orig: T) {
|
||||
self.0.set((orig, ExchangeableFunctionState::Original));
|
||||
}
|
||||
|
||||
/// Returns the internal function pointer.
|
||||
pub fn get(&self) -> T {
|
||||
self.0.get().0
|
||||
}
|
||||
}
|
||||
|
||||
// Wasm does not support threads, so this is safe; qed.
|
||||
unsafe impl<T> Sync for ExchangeableFunction<T> {}
|
||||
|
||||
/// Restores a function implementation on drop.
|
||||
///
|
||||
/// Stores a static reference to the function object and the original implementation.
|
||||
pub struct RestoreImplementation<T: 'static + Copy>(&'static ExchangeableFunction<T>, Option<T>);
|
||||
|
||||
impl<T: Copy> Drop for RestoreImplementation<T> {
|
||||
fn drop(&mut self) {
|
||||
self.0
|
||||
.restore_orig_implementation(self.1.take().expect("Value is only taken on drop; qed"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user