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:
+269
@@ -0,0 +1,269 @@
|
||||
// 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.
|
||||
|
||||
//! Generates the bare function interface for a given trait definition.
|
||||
//!
|
||||
//! The bare functions are the ones that will be called by the user. On the native/host side, these
|
||||
//! functions directly execute the provided implementation. On the wasm side, these
|
||||
//! functions will prepare the parameters for the FFI boundary, call the external host function
|
||||
//! exported into wasm and convert back the result.
|
||||
//!
|
||||
//! [`generate`] is the entry point for generating for each
|
||||
//! trait method one bare function.
|
||||
//!
|
||||
//! [`function_for_method`] generates the bare
|
||||
//! function per trait method. Each bare function contains both implementations. The implementations
|
||||
//! are feature-gated, so that one is compiled for the native and the other for the wasm side.
|
||||
|
||||
use crate::utils::{
|
||||
create_exchangeable_host_function_ident, create_function_ident_with_version,
|
||||
generate_crate_access, get_function_argument_names, get_function_arguments,
|
||||
get_runtime_interface, host_inner_return_ty, pat_ty_to_host_inner, RuntimeInterfaceFunction,
|
||||
};
|
||||
|
||||
use syn::{
|
||||
parse_quote, spanned::Spanned, FnArg, Ident, ItemTrait, Result, Signature, Token, TraitItemFn,
|
||||
};
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
|
||||
use quote::{quote, quote_spanned};
|
||||
|
||||
use std::iter;
|
||||
|
||||
/// Generate one bare function per trait method. The name of the bare function is equal to the name
|
||||
/// of the trait method.
|
||||
pub fn generate(trait_def: &ItemTrait, is_wasm_only: bool, tracing: bool) -> Result<TokenStream> {
|
||||
let trait_name = &trait_def.ident;
|
||||
let runtime_interface = get_runtime_interface(trait_def)?;
|
||||
|
||||
// latest version dispatch
|
||||
let token_stream: Result<TokenStream> = runtime_interface.latest_versions_to_call().try_fold(
|
||||
TokenStream::new(),
|
||||
|mut t, (latest_version, method)| {
|
||||
t.extend(function_for_method(method, latest_version, is_wasm_only)?);
|
||||
Ok(t)
|
||||
},
|
||||
);
|
||||
|
||||
// earlier versions compatibility dispatch (only std variant)
|
||||
let result: Result<TokenStream> =
|
||||
runtime_interface
|
||||
.all_versions()
|
||||
.try_fold(token_stream?, |mut t, (version, method)| {
|
||||
t.extend(function_std_impl(trait_name, method, version, is_wasm_only, tracing)?);
|
||||
Ok(t)
|
||||
});
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Generates the bare function implementation for the given method for the host and wasm side.
|
||||
fn function_for_method(
|
||||
method: &RuntimeInterfaceFunction,
|
||||
latest_version: u32,
|
||||
is_wasm_only: bool,
|
||||
) -> Result<TokenStream> {
|
||||
let std_impl =
|
||||
if !is_wasm_only { function_std_latest_impl(method, latest_version)? } else { quote!() };
|
||||
|
||||
let no_std_impl = function_no_std_impl(method, is_wasm_only)?;
|
||||
|
||||
Ok(quote! {
|
||||
#std_impl
|
||||
|
||||
#no_std_impl
|
||||
})
|
||||
}
|
||||
|
||||
/// Generates the bare function implementation for `cfg(bizinikiwi_runtime)`.
|
||||
fn function_no_std_impl(
|
||||
method: &RuntimeInterfaceFunction,
|
||||
is_wasm_only: bool,
|
||||
) -> Result<TokenStream> {
|
||||
let should_trap_on_return = method.should_trap_on_return();
|
||||
let mut method = (*method).clone();
|
||||
crate::utils::unpack_inner_types_in_signature(&mut method.sig);
|
||||
|
||||
let function_name = &method.sig.ident;
|
||||
let host_function_name = create_exchangeable_host_function_ident(&method.sig.ident);
|
||||
let args = get_function_arguments(&method.sig);
|
||||
let arg_names = get_function_argument_names(&method.sig);
|
||||
let return_value = if should_trap_on_return {
|
||||
syn::ReturnType::Type(
|
||||
<Token![->]>::default(),
|
||||
Box::new(syn::TypeNever { bang_token: <Token![!]>::default() }.into()),
|
||||
)
|
||||
} else {
|
||||
method.sig.output.clone()
|
||||
};
|
||||
let maybe_unreachable = if should_trap_on_return {
|
||||
quote! {
|
||||
;
|
||||
#[cfg(target_family = "wasm")]
|
||||
{ core::arch::wasm32::unreachable(); }
|
||||
|
||||
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
|
||||
unsafe { core::arch::asm!("unimp", options(noreturn)); }
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
let attrs = method.attrs.iter().filter(|a| !a.path().is_ident("version"));
|
||||
|
||||
let cfg_wasm_only = if is_wasm_only {
|
||||
quote! { #[cfg(bizinikiwi_runtime)] }
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
Ok(quote! {
|
||||
#cfg_wasm_only
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
#( #attrs )*
|
||||
pub fn #function_name( #( #args, )* ) #return_value {
|
||||
// Call the host function
|
||||
#host_function_name.get()( #( #arg_names, )* )
|
||||
#maybe_unreachable
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate call to latest function version for `cfg(not(bizinikiwi_runtime))`
|
||||
///
|
||||
/// This should generate simple `fn func(..) { func_version_<latest_version>(..) }`.
|
||||
fn function_std_latest_impl(method: &TraitItemFn, latest_version: u32) -> Result<TokenStream> {
|
||||
let function_name = &method.sig.ident;
|
||||
let args = get_function_arguments(&method.sig).map(pat_ty_to_host_inner).map(FnArg::Typed);
|
||||
let arg_names = get_function_argument_names(&method.sig).collect::<Vec<_>>();
|
||||
let return_value = host_inner_return_ty(&method.sig.output);
|
||||
let attrs = method.attrs.iter().filter(|a| !a.path().is_ident("version"));
|
||||
let latest_function_name =
|
||||
create_function_ident_with_version(&method.sig.ident, latest_version);
|
||||
|
||||
Ok(quote_spanned! { method.span() =>
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
#( #attrs )*
|
||||
pub fn #function_name( #( #args, )* ) #return_value {
|
||||
#latest_function_name(
|
||||
#( #arg_names, )*
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Generates the bare function implementation for `cfg(not(bizinikiwi_runtime))`.
|
||||
fn function_std_impl(
|
||||
trait_name: &Ident,
|
||||
method: &TraitItemFn,
|
||||
version: u32,
|
||||
is_wasm_only: bool,
|
||||
tracing: bool,
|
||||
) -> Result<TokenStream> {
|
||||
let function_name = create_function_ident_with_version(&method.sig.ident, version);
|
||||
let function_name_str = function_name.to_string();
|
||||
|
||||
let crate_ = generate_crate_access();
|
||||
let args = get_function_arguments(&method.sig)
|
||||
.map(pat_ty_to_host_inner)
|
||||
.map(FnArg::Typed)
|
||||
.chain(
|
||||
// Add the function context as last parameter when this is a wasm only interface.
|
||||
iter::from_fn(|| {
|
||||
if is_wasm_only {
|
||||
Some(parse_quote!(
|
||||
mut __function_context__: &mut dyn #crate_::pezsp_wasm_interface::FunctionContext
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.take(1),
|
||||
);
|
||||
let return_value = host_inner_return_ty(&method.sig.output);
|
||||
let attrs = method.attrs.iter().filter(|a| !a.path().is_ident("version"));
|
||||
// Don't make the function public accessible when this is a wasm only interface.
|
||||
let call_to_trait = generate_call_to_trait(trait_name, method, version, is_wasm_only);
|
||||
let call_to_trait = if !tracing {
|
||||
call_to_trait
|
||||
} else {
|
||||
parse_quote!(
|
||||
#crate_::pezsp_tracing::within_span! { #crate_::pezsp_tracing::trace_span!(#function_name_str);
|
||||
#call_to_trait
|
||||
}
|
||||
)
|
||||
};
|
||||
|
||||
Ok(quote_spanned! { method.span() =>
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
#( #attrs )*
|
||||
fn #function_name( #( #args, )* ) #return_value {
|
||||
#call_to_trait
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate the call to the interface trait.
|
||||
fn generate_call_to_trait(
|
||||
trait_name: &Ident,
|
||||
method: &TraitItemFn,
|
||||
version: u32,
|
||||
is_wasm_only: bool,
|
||||
) -> TokenStream {
|
||||
let crate_ = generate_crate_access();
|
||||
let method_name = create_function_ident_with_version(&method.sig.ident, version);
|
||||
let expect_msg =
|
||||
format!("`{}` called outside of an Externalities-provided environment.", method_name);
|
||||
let arg_names = get_function_argument_names(&method.sig);
|
||||
|
||||
if takes_self_argument(&method.sig) {
|
||||
let instance = if is_wasm_only {
|
||||
Ident::new("__function_context__", Span::call_site())
|
||||
} else {
|
||||
Ident::new("__externalities__", Span::call_site())
|
||||
};
|
||||
|
||||
let impl_ = quote!( #trait_name::#method_name(&mut #instance, #( #arg_names, )*) );
|
||||
|
||||
if is_wasm_only {
|
||||
quote_spanned! { method.span() => #impl_ }
|
||||
} else {
|
||||
quote_spanned! { method.span() =>
|
||||
#crate_::with_externalities(|mut #instance| #impl_).expect(#expect_msg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The name of the trait the interface trait is implemented for
|
||||
let impl_trait_name = if is_wasm_only {
|
||||
quote!( #crate_::pezsp_wasm_interface::FunctionContext )
|
||||
} else {
|
||||
quote!( #crate_::Externalities )
|
||||
};
|
||||
|
||||
quote_spanned! { method.span() =>
|
||||
<&mut dyn #impl_trait_name as #trait_name>::#method_name(
|
||||
#( #arg_names, )*
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns if the given `Signature` takes a `self` argument.
|
||||
fn takes_self_argument(sig: &Signature) -> bool {
|
||||
matches!(sig.inputs.first(), Some(FnArg::Receiver(_)))
|
||||
}
|
||||
+480
@@ -0,0 +1,480 @@
|
||||
// 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.
|
||||
|
||||
//! Generates the extern host functions and the implementation for these host functions.
|
||||
//!
|
||||
//! The extern host functions will be called by the bare function interface from the Wasm side.
|
||||
//! The implementation of these host functions will be called on the host side from the Wasm
|
||||
//! executor. These implementations call the bare function interface.
|
||||
|
||||
use crate::utils::{
|
||||
create_exchangeable_host_function_ident, create_function_ident_with_version,
|
||||
create_host_function_ident, generate_crate_access, get_function_argument_names,
|
||||
get_function_argument_names_and_types, get_function_argument_types, get_function_arguments,
|
||||
get_runtime_interface, RuntimeInterfaceFunction,
|
||||
};
|
||||
|
||||
use syn::{
|
||||
spanned::Spanned, Error, Ident, ItemTrait, Pat, Result, ReturnType, Signature, TraitItemFn,
|
||||
};
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
|
||||
use quote::quote;
|
||||
|
||||
use inflector::Inflector;
|
||||
|
||||
use std::iter::Iterator;
|
||||
|
||||
/// Generate the extern host functions for wasm and the `HostFunctions` struct that provides the
|
||||
/// implementations for the host functions on the host.
|
||||
pub fn generate(trait_def: &ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
|
||||
let trait_name = &trait_def.ident;
|
||||
let extern_host_function_impls = get_runtime_interface(trait_def)?
|
||||
.latest_versions_to_call()
|
||||
.try_fold(TokenStream::new(), |mut t, (version, method)| {
|
||||
t.extend(generate_extern_host_function(method, version, trait_name)?);
|
||||
Ok::<_, Error>(t)
|
||||
})?;
|
||||
let exchangeable_host_functions = get_runtime_interface(trait_def)?
|
||||
.latest_versions_to_call()
|
||||
.try_fold(TokenStream::new(), |mut t, (_, m)| {
|
||||
t.extend(generate_exchangeable_host_function(m)?);
|
||||
Ok::<_, Error>(t)
|
||||
})?;
|
||||
let host_functions_struct = generate_host_functions_struct(trait_def, is_wasm_only)?;
|
||||
|
||||
Ok(quote! {
|
||||
/// The implementations of the extern host functions. This special implementation module
|
||||
/// is required to change the extern host functions signature to
|
||||
/// `unsafe fn name(args) -> ret` to make the function implementations exchangeable.
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
mod extern_host_function_impls {
|
||||
use super::*;
|
||||
|
||||
#extern_host_function_impls
|
||||
}
|
||||
|
||||
#exchangeable_host_functions
|
||||
|
||||
#host_functions_struct
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate the extern host function for the given method.
|
||||
fn generate_extern_host_function(
|
||||
method: &TraitItemFn,
|
||||
version: u32,
|
||||
trait_name: &Ident,
|
||||
) -> Result<TokenStream> {
|
||||
let crate_ = generate_crate_access();
|
||||
|
||||
let mut unpacked_sig = method.sig.clone();
|
||||
crate::utils::unpack_inner_types_in_signature(&mut unpacked_sig);
|
||||
let unpacked_args = get_function_arguments(&unpacked_sig);
|
||||
let unpacked_return_value = &unpacked_sig.output;
|
||||
|
||||
let arg_types = get_function_argument_types(&method.sig);
|
||||
let arg_names = get_function_argument_names(&method.sig);
|
||||
let function = &method.sig.ident;
|
||||
let ext_function = create_host_function_ident(&method.sig.ident, version, trait_name);
|
||||
let doc_string = format!(
|
||||
" Default extern host function implementation for [`super::{}`].",
|
||||
method.sig.ident,
|
||||
);
|
||||
let return_value = &method.sig.output;
|
||||
let cfg_attrs = method.attrs.iter().filter(|a| a.path().is_ident("cfg"));
|
||||
|
||||
let ffi_return_value = match method.sig.output {
|
||||
ReturnType::Default => quote!(),
|
||||
ReturnType::Type(_, ref ty) => quote! {
|
||||
-> <#ty as #crate_::RIType>::FFIType
|
||||
},
|
||||
};
|
||||
|
||||
let convert_return_value = match return_value {
|
||||
ReturnType::Default => quote! { __runtime_interface_result_ },
|
||||
ReturnType::Type(_, ref ty) => quote! {
|
||||
<#ty as #crate_::wasm::FromFFIValue>::from_ffi_value(__runtime_interface_result_)
|
||||
},
|
||||
};
|
||||
|
||||
let mut call_into_ffi_value = Vec::new();
|
||||
let mut drop_args = Vec::new();
|
||||
let mut ffi_names = Vec::new();
|
||||
for (nth, arg) in get_function_arguments(&method.sig).enumerate() {
|
||||
let arg_name = &arg.pat;
|
||||
let arg_ty = &arg.ty;
|
||||
let ffi_name =
|
||||
Ident::new(&format!("__runtime_interface_ffi_value_{}_", nth), arg.pat.span());
|
||||
let destructor_name =
|
||||
Ident::new(&format!("__runtime_interface_arg_destructor_{}_", nth), arg.pat.span());
|
||||
|
||||
ffi_names.push(ffi_name.clone());
|
||||
|
||||
call_into_ffi_value.push(quote! {
|
||||
let mut #arg_name = #arg_name;
|
||||
let (#ffi_name, #destructor_name) = <#arg_ty as #crate_::wasm::IntoFFIValue>::into_ffi_value(&mut #arg_name);
|
||||
});
|
||||
|
||||
drop_args.push(quote! {
|
||||
#[allow(dropping_copy_types)]
|
||||
::core::mem::drop(#destructor_name);
|
||||
});
|
||||
}
|
||||
|
||||
// Drop in the reverse order to construction.
|
||||
drop_args.reverse();
|
||||
|
||||
Ok(quote! {
|
||||
#(#cfg_attrs)*
|
||||
#[doc = #doc_string]
|
||||
pub fn #function ( #( #unpacked_args ),* ) #unpacked_return_value {
|
||||
#[cfg_attr(any(target_arch = "riscv32", target_arch = "riscv64"), #crate_::polkavm::polkavm_import(abi = #crate_::polkavm::polkavm_abi))]
|
||||
extern "C" {
|
||||
pub fn #ext_function (
|
||||
#( #arg_names: <#arg_types as #crate_::RIType>::FFIType ),*
|
||||
) #ffi_return_value;
|
||||
}
|
||||
|
||||
#(#call_into_ffi_value)*
|
||||
let __runtime_interface_result_ = unsafe { #ext_function( #( #ffi_names ),* ) };
|
||||
#(#drop_args)*
|
||||
#convert_return_value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate the host exchangeable function for the given method.
|
||||
fn generate_exchangeable_host_function(method: &TraitItemFn) -> Result<TokenStream> {
|
||||
let mut method = method.clone();
|
||||
crate::utils::unpack_inner_types_in_signature(&mut method.sig);
|
||||
|
||||
let crate_ = generate_crate_access();
|
||||
let arg_types = get_function_argument_types(&method.sig);
|
||||
let function = &method.sig.ident;
|
||||
let exchangeable_function = create_exchangeable_host_function_ident(&method.sig.ident);
|
||||
let doc_string = format!(" Exchangeable host function used by [`{}`].", method.sig.ident);
|
||||
let output = &method.sig.output;
|
||||
let cfg_attrs = method.attrs.iter().filter(|a| a.path().is_ident("cfg"));
|
||||
|
||||
Ok(quote! {
|
||||
#(#cfg_attrs)*
|
||||
#[cfg(bizinikiwi_runtime)]
|
||||
#[allow(non_upper_case_globals)]
|
||||
#[doc = #doc_string]
|
||||
pub static #exchangeable_function : #crate_::wasm::ExchangeableFunction<
|
||||
fn ( #( #arg_types ),* ) #output
|
||||
> = #crate_::wasm::ExchangeableFunction::new(extern_host_function_impls::#function);
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate the `HostFunctions` struct that implements `wasm-interface::HostFunctions` to provide
|
||||
/// implementations for the extern host functions.
|
||||
fn generate_host_functions_struct(
|
||||
trait_def: &ItemTrait,
|
||||
is_wasm_only: bool,
|
||||
) -> Result<TokenStream> {
|
||||
let crate_ = generate_crate_access();
|
||||
|
||||
let mut host_function_impls = Vec::new();
|
||||
let mut register_bodies = Vec::new();
|
||||
let mut append_hf_bodies = Vec::new();
|
||||
|
||||
for (version, method) in get_runtime_interface(trait_def)?.all_versions() {
|
||||
let (implementation, register_body, append_hf_body) =
|
||||
generate_host_function_implementation(&trait_def.ident, method, version, is_wasm_only)?;
|
||||
host_function_impls.push(implementation);
|
||||
register_bodies.push(register_body);
|
||||
append_hf_bodies.push(append_hf_body);
|
||||
}
|
||||
|
||||
Ok(quote! {
|
||||
#(#host_function_impls)*
|
||||
|
||||
/// Provides implementations for the extern host functions.
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
pub struct HostFunctions;
|
||||
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl #crate_::pezsp_wasm_interface::HostFunctions for HostFunctions {
|
||||
fn host_functions() -> Vec<&'static dyn #crate_::pezsp_wasm_interface::Function> {
|
||||
let mut host_functions_list = Vec::new();
|
||||
#(#append_hf_bodies)*
|
||||
host_functions_list
|
||||
}
|
||||
|
||||
#crate_::pezsp_wasm_interface::if_wasmtime_is_enabled! {
|
||||
fn register_static<T>(registry: &mut T) -> core::result::Result<(), T::Error>
|
||||
where T: #crate_::pezsp_wasm_interface::HostFunctionRegistry
|
||||
{
|
||||
#(#register_bodies)*
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Generates the host function struct that implements `wasm_interface::Function` and returns a
|
||||
/// static reference to this struct.
|
||||
///
|
||||
/// When calling from wasm into the host, we will call the `execute` function that calls the native
|
||||
/// implementation of the function.
|
||||
fn generate_host_function_implementation(
|
||||
trait_name: &Ident,
|
||||
method: &RuntimeInterfaceFunction,
|
||||
version: u32,
|
||||
is_wasm_only: bool,
|
||||
) -> Result<(TokenStream, TokenStream, TokenStream)> {
|
||||
let name = create_host_function_ident(&method.sig.ident, version, trait_name).to_string();
|
||||
let struct_name = Ident::new(&name.to_pascal_case(), Span::call_site());
|
||||
let crate_ = generate_crate_access();
|
||||
let signature = generate_wasm_interface_signature_for_host_function(&method.sig)?;
|
||||
|
||||
let fn_name = create_function_ident_with_version(&method.sig.ident, version);
|
||||
|
||||
// List of variable names containing WASM FFI-compatible arguments.
|
||||
let mut ffi_names = Vec::new();
|
||||
|
||||
// List of `$name: $ty` tokens containing WASM FFI-compatible arguments.
|
||||
let mut ffi_args_prototype = Vec::new();
|
||||
|
||||
// List of variable names containing arguments already converted into native Rust types.
|
||||
// Also includes the preceding `&` or `&mut`. To be used to call the actual implementation of
|
||||
// the host function.
|
||||
let mut host_names_with_ref = Vec::new();
|
||||
|
||||
// List of code snippets to copy over the results returned from a host function through
|
||||
// any `&mut` arguments back into WASM's linear memory.
|
||||
let mut copy_data_into_ref_mut_args = Vec::new();
|
||||
|
||||
// List of code snippets to convert dynamic FFI args (`Value` enum) into concrete static FFI
|
||||
// types (`u32`, etc.).
|
||||
let mut convert_args_dynamic_ffi_to_static_ffi = Vec::new();
|
||||
|
||||
// List of code snippets to convert static FFI args (`u32`, etc.) into native Rust types.
|
||||
let mut convert_args_static_ffi_to_host = Vec::new();
|
||||
|
||||
for (host_name, host_ty) in get_function_argument_names_and_types(&method.sig) {
|
||||
let ffi_name = generate_ffi_value_var_name(&host_name)?;
|
||||
let host_name_ident = match *host_name {
|
||||
Pat::Ident(ref pat_ident) => pat_ident.ident.clone(),
|
||||
_ => unreachable!("`generate_ffi_value_var_name` above would return an error on `Pat` != `Ident`; qed"),
|
||||
};
|
||||
|
||||
let ffi_ty = quote! { <#host_ty as #crate_::RIType>::FFIType };
|
||||
ffi_args_prototype.push(quote! { #ffi_name: #ffi_ty });
|
||||
ffi_names.push(quote! { #ffi_name });
|
||||
|
||||
let convert_arg_error = format!(
|
||||
"could not marshal the '{}' argument through the WASM FFI boundary while executing '{}' from interface '{}'",
|
||||
host_name_ident,
|
||||
method.sig.ident,
|
||||
trait_name
|
||||
);
|
||||
convert_args_static_ffi_to_host.push(quote! {
|
||||
let mut #host_name = <#host_ty as #crate_::host::FromFFIValue>::from_ffi_value(__function_context__, #ffi_name)
|
||||
.map_err(|err| #crate_::alloc::format!("{}: {}", err, #convert_arg_error))?;
|
||||
});
|
||||
|
||||
host_names_with_ref.push(
|
||||
quote! { <#host_ty as #crate_::host::FromFFIValue>::take_from_owned(&mut #host_name) },
|
||||
);
|
||||
copy_data_into_ref_mut_args.push(quote! {
|
||||
<#host_ty as #crate_::host::FromFFIValue>::write_back_into_runtime(#host_name, __function_context__, #ffi_name)?;
|
||||
});
|
||||
|
||||
let arg_count_mismatch_error = format!(
|
||||
"missing argument '{}': number of arguments given to '{}' from interface '{}' does not match the expected number of arguments",
|
||||
host_name_ident,
|
||||
method.sig.ident,
|
||||
trait_name
|
||||
);
|
||||
convert_args_dynamic_ffi_to_static_ffi.push(quote! {
|
||||
let #ffi_name = args.next().ok_or_else(|| #crate_::alloc::borrow::ToOwned::to_owned(#arg_count_mismatch_error))?;
|
||||
let #ffi_name: #ffi_ty = #crate_::pezsp_wasm_interface::TryFromValue::try_from_value(#ffi_name)
|
||||
.ok_or_else(|| #crate_::alloc::borrow::ToOwned::to_owned(#convert_arg_error))?;
|
||||
});
|
||||
}
|
||||
|
||||
let ffi_return_ty = match &method.sig.output {
|
||||
ReturnType::Type(_, ty) => quote! { <#ty as #crate_::RIType>::FFIType },
|
||||
ReturnType::Default => quote! { () },
|
||||
};
|
||||
|
||||
let convert_return_value_host_to_static_ffi = match &method.sig.output {
|
||||
ReturnType::Type(_, ty) => quote! {
|
||||
let __result__ = <#ty as #crate_::host::IntoFFIValue>::into_ffi_value(
|
||||
__result__,
|
||||
__function_context__
|
||||
);
|
||||
},
|
||||
ReturnType::Default => quote! {
|
||||
let __result__ = Ok(__result__);
|
||||
},
|
||||
};
|
||||
|
||||
let convert_return_value_static_ffi_to_dynamic_ffi = match &method.sig.output {
|
||||
ReturnType::Type(_, _) => quote! {
|
||||
let __result__ = Ok(Some(#crate_::pezsp_wasm_interface::IntoValue::into_value(__result__)));
|
||||
},
|
||||
ReturnType::Default => quote! {
|
||||
let __result__ = Ok(None);
|
||||
},
|
||||
};
|
||||
|
||||
if is_wasm_only {
|
||||
host_names_with_ref.push(quote! {
|
||||
__function_context__
|
||||
});
|
||||
}
|
||||
|
||||
let cfg_attrs: Vec<_> =
|
||||
method.attrs.iter().filter(|a| a.path().is_ident("cfg")).cloned().collect();
|
||||
if version > 1 && !cfg_attrs.is_empty() {
|
||||
return Err(Error::new(
|
||||
method.span(),
|
||||
"Conditional compilation is not supported for versioned functions",
|
||||
));
|
||||
}
|
||||
|
||||
let implementation = quote! {
|
||||
#(#cfg_attrs)*
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
struct #struct_name;
|
||||
|
||||
#(#cfg_attrs)*
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl #struct_name {
|
||||
fn call(
|
||||
__function_context__: &mut dyn #crate_::pezsp_wasm_interface::FunctionContext,
|
||||
#(#ffi_args_prototype),*
|
||||
) -> ::core::result::Result<#ffi_return_ty, #crate_::alloc::string::String> {
|
||||
#(#convert_args_static_ffi_to_host)*
|
||||
let __result__ = #fn_name(#(#host_names_with_ref),*);
|
||||
#(#copy_data_into_ref_mut_args)*
|
||||
#convert_return_value_host_to_static_ffi
|
||||
__result__
|
||||
}
|
||||
}
|
||||
|
||||
#(#cfg_attrs)*
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl #crate_::pezsp_wasm_interface::Function for #struct_name {
|
||||
fn name(&self) -> &str {
|
||||
#name
|
||||
}
|
||||
|
||||
fn signature(&self) -> #crate_::pezsp_wasm_interface::Signature {
|
||||
#signature
|
||||
}
|
||||
|
||||
fn execute(
|
||||
&self,
|
||||
__function_context__: &mut dyn #crate_::pezsp_wasm_interface::FunctionContext,
|
||||
args: &mut dyn Iterator<Item = #crate_::pezsp_wasm_interface::Value>,
|
||||
) -> ::core::result::Result<Option<#crate_::pezsp_wasm_interface::Value>, #crate_::alloc::string::String> {
|
||||
#(#convert_args_dynamic_ffi_to_static_ffi)*
|
||||
let __result__ = Self::call(
|
||||
__function_context__,
|
||||
#(#ffi_names),*
|
||||
)?;
|
||||
#convert_return_value_static_ffi_to_dynamic_ffi
|
||||
__result__
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let register_body = quote! {
|
||||
#(#cfg_attrs)*
|
||||
registry.register_static(
|
||||
#crate_::pezsp_wasm_interface::Function::name(&#struct_name),
|
||||
|mut caller: #crate_::pezsp_wasm_interface::wasmtime::Caller<T::State>, #(#ffi_args_prototype),*|
|
||||
-> ::core::result::Result<#ffi_return_ty, #crate_::pezsp_wasm_interface::anyhow::Error>
|
||||
{
|
||||
T::with_function_context(caller, move |__function_context__| {
|
||||
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
||||
#struct_name::call(
|
||||
__function_context__,
|
||||
#(#ffi_names,)*
|
||||
).map_err(#crate_::pezsp_wasm_interface::anyhow::Error::msg)
|
||||
}));
|
||||
match result {
|
||||
Ok(result) => result,
|
||||
Err(panic) => {
|
||||
let message =
|
||||
if let Some(message) = panic.downcast_ref::<#crate_::alloc::string::String>() {
|
||||
#crate_::alloc::format!("host code panicked while being called by the runtime: {}", message)
|
||||
} else if let Some(message) = panic.downcast_ref::<&'static str>() {
|
||||
#crate_::alloc::format!("host code panicked while being called by the runtime: {}", message)
|
||||
} else {
|
||||
#crate_::alloc::borrow::ToOwned::to_owned("host code panicked while being called by the runtime")
|
||||
};
|
||||
return Err(#crate_::pezsp_wasm_interface::anyhow::Error::msg(message));
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
)?;
|
||||
};
|
||||
|
||||
let append_hf_body = quote! {
|
||||
#(#cfg_attrs)*
|
||||
host_functions_list.push(&#struct_name as &dyn #crate_::pezsp_wasm_interface::Function);
|
||||
};
|
||||
|
||||
Ok((implementation, register_body, append_hf_body))
|
||||
}
|
||||
|
||||
/// Generate the `wasm_interface::Signature` for the given host function `sig`.
|
||||
fn generate_wasm_interface_signature_for_host_function(sig: &Signature) -> Result<TokenStream> {
|
||||
let crate_ = generate_crate_access();
|
||||
let return_value = match &sig.output {
|
||||
ReturnType::Type(_, ty) => quote! {
|
||||
Some( <<#ty as #crate_::RIType>::FFIType as #crate_::pezsp_wasm_interface::IntoValue>::VALUE_TYPE )
|
||||
},
|
||||
ReturnType::Default => quote!(None),
|
||||
};
|
||||
let arg_types = get_function_argument_types(sig).map(|ty| {
|
||||
quote! {
|
||||
<<#ty as #crate_::RIType>::FFIType as #crate_::pezsp_wasm_interface::IntoValue>::VALUE_TYPE
|
||||
}
|
||||
});
|
||||
|
||||
Ok(quote! {
|
||||
#crate_::pezsp_wasm_interface::Signature {
|
||||
args: #crate_::alloc::borrow::Cow::Borrowed(&[ #( #arg_types ),* ][..]),
|
||||
return_value: #return_value,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate the variable name that stores the FFI value.
|
||||
fn generate_ffi_value_var_name(pat: &Pat) -> Result<Ident> {
|
||||
match pat {
|
||||
Pat::Ident(pat_ident) =>
|
||||
if let Some(by_ref) = pat_ident.by_ref {
|
||||
Err(Error::new(by_ref.span(), "`ref` not supported!"))
|
||||
} else if let Some(sub_pattern) = &pat_ident.subpat {
|
||||
Err(Error::new(sub_pattern.0.span(), "Not supported!"))
|
||||
} else {
|
||||
Ok(Ident::new(&format!("{}_ffi_value", pat_ident.ident), Span::call_site()))
|
||||
},
|
||||
_ => Err(Error::new(pat.span(), "Not supported as variable name!")),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// 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.
|
||||
|
||||
use crate::utils::generate_runtime_interface_include;
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
|
||||
use syn::{Ident, ItemTrait, Result};
|
||||
|
||||
use inflector::Inflector;
|
||||
|
||||
use quote::quote;
|
||||
|
||||
mod bare_function_interface;
|
||||
mod host_function_interface;
|
||||
mod trait_decl_impl;
|
||||
|
||||
/// Custom keywords supported by the `runtime_interface` attribute.
|
||||
pub mod keywords {
|
||||
// Custom keyword `wasm_only` that can be given as attribute to [`runtime_interface`].
|
||||
syn::custom_keyword!(wasm_only);
|
||||
// Disable tracing-macros added to the [`runtime_interface`] by specifying this optional entry
|
||||
syn::custom_keyword!(no_tracing);
|
||||
}
|
||||
|
||||
/// Implementation of the `runtime_interface` attribute.
|
||||
///
|
||||
/// It expects the trait definition the attribute was put above and if this should be an wasm only
|
||||
/// interface.
|
||||
pub fn runtime_interface_impl(
|
||||
trait_def: ItemTrait,
|
||||
is_wasm_only: bool,
|
||||
tracing: bool,
|
||||
) -> Result<TokenStream> {
|
||||
let bare_functions = bare_function_interface::generate(&trait_def, is_wasm_only, tracing)?;
|
||||
let crate_include = generate_runtime_interface_include();
|
||||
let mod_name = Ident::new(&trait_def.ident.to_string().to_snake_case(), Span::call_site());
|
||||
let trait_decl_impl = trait_decl_impl::process(&trait_def, is_wasm_only)?;
|
||||
let host_functions = host_function_interface::generate(&trait_def, is_wasm_only)?;
|
||||
let vis = trait_def.vis;
|
||||
let attrs = &trait_def.attrs;
|
||||
|
||||
let res = quote! {
|
||||
#( #attrs )*
|
||||
#vis mod #mod_name {
|
||||
use super::*;
|
||||
#crate_include
|
||||
|
||||
#bare_functions
|
||||
|
||||
#trait_decl_impl
|
||||
|
||||
#host_functions
|
||||
}
|
||||
};
|
||||
|
||||
let res = expander::Expander::new("runtime_interface")
|
||||
.dry(std::env::var("EXPAND_MACROS").is_err())
|
||||
.verbose(true)
|
||||
.write_to_out_dir(res)
|
||||
.expect("Does not fail because of IO in OUT_DIR; qed");
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
// 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.
|
||||
|
||||
//! Checks the trait declaration, makes the trait declaration module local, removes all method
|
||||
//! default implementations and implements the trait for `&mut dyn Externalities`.
|
||||
|
||||
use crate::utils::{
|
||||
create_function_ident_with_version, generate_crate_access, get_function_argument_types,
|
||||
get_runtime_interface,
|
||||
};
|
||||
|
||||
use syn::{
|
||||
fold::{self, Fold},
|
||||
spanned::Spanned,
|
||||
Error, Generics, ItemTrait, Receiver, Result, TraitItemFn, Type, Visibility,
|
||||
};
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
use quote::quote;
|
||||
|
||||
/// Process the given trait definition, by checking that the definition is valid, fold it to the
|
||||
/// essential definition and implement this essential definition for `dyn Externalities`.
|
||||
pub fn process(trait_def: &ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
|
||||
let impl_trait = impl_trait_for_externalities(trait_def, is_wasm_only)?;
|
||||
let essential_trait_def = declare_essential_trait(trait_def)?;
|
||||
|
||||
Ok(quote! {
|
||||
#impl_trait
|
||||
|
||||
#essential_trait_def
|
||||
})
|
||||
}
|
||||
|
||||
/// Converts the given trait definition into the essential trait definition without method
|
||||
/// default implementations and visibility set to inherited.
|
||||
struct ToEssentialTraitDef {
|
||||
/// All errors found while doing the conversion.
|
||||
errors: Vec<Error>,
|
||||
methods: Vec<TraitItemFn>,
|
||||
}
|
||||
|
||||
impl ToEssentialTraitDef {
|
||||
fn new() -> Self {
|
||||
ToEssentialTraitDef { errors: vec![], methods: vec![] }
|
||||
}
|
||||
|
||||
fn into_methods(self) -> Result<Vec<TraitItemFn>> {
|
||||
let mut errors = self.errors;
|
||||
let methods = self.methods;
|
||||
if let Some(first_error) = errors.pop() {
|
||||
Err(errors.into_iter().fold(first_error, |mut o, n| {
|
||||
o.combine(n);
|
||||
o
|
||||
}))
|
||||
} else {
|
||||
Ok(methods)
|
||||
}
|
||||
}
|
||||
|
||||
fn process(&mut self, method: &TraitItemFn, version: u32) {
|
||||
let mut folded = self.fold_trait_item_fn(method.clone());
|
||||
folded.sig.ident = create_function_ident_with_version(&folded.sig.ident, version);
|
||||
crate::utils::unpack_inner_types_in_signature(&mut folded.sig);
|
||||
self.methods.push(folded);
|
||||
}
|
||||
|
||||
fn push_error<S: Spanned>(&mut self, span: &S, msg: &str) {
|
||||
self.errors.push(Error::new(span.span(), msg));
|
||||
}
|
||||
|
||||
fn error_on_generic_parameters(&mut self, generics: &Generics) {
|
||||
if let Some(param) = generics.params.first() {
|
||||
self.push_error(param, "Generic parameters not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Fold for ToEssentialTraitDef {
|
||||
fn fold_trait_item_fn(&mut self, mut method: TraitItemFn) -> TraitItemFn {
|
||||
if method.default.take().is_none() {
|
||||
self.push_error(&method, "Methods need to have an implementation.");
|
||||
}
|
||||
|
||||
let arg_types = get_function_argument_types(&method.sig);
|
||||
arg_types
|
||||
.filter_map(|ty| match *ty {
|
||||
Type::ImplTrait(impl_trait) => Some(impl_trait),
|
||||
_ => None,
|
||||
})
|
||||
.for_each(|invalid| self.push_error(&invalid, "`impl Trait` syntax not supported."));
|
||||
|
||||
self.error_on_generic_parameters(&method.sig.generics);
|
||||
|
||||
method.attrs.retain(|a| !a.path().is_ident("version"));
|
||||
|
||||
fold::fold_trait_item_fn(self, method)
|
||||
}
|
||||
|
||||
fn fold_item_trait(&mut self, mut trait_def: ItemTrait) -> ItemTrait {
|
||||
self.error_on_generic_parameters(&trait_def.generics);
|
||||
|
||||
trait_def.vis = Visibility::Inherited;
|
||||
fold::fold_item_trait(self, trait_def)
|
||||
}
|
||||
|
||||
fn fold_receiver(&mut self, receiver: Receiver) -> Receiver {
|
||||
if receiver.reference.is_none() {
|
||||
self.push_error(&receiver, "Taking `Self` by value is not allowed.");
|
||||
}
|
||||
|
||||
fold::fold_receiver(self, receiver)
|
||||
}
|
||||
}
|
||||
|
||||
fn declare_essential_trait(trait_def: &ItemTrait) -> Result<TokenStream> {
|
||||
let trait_ = &trait_def.ident;
|
||||
|
||||
if let Some(param) = trait_def.generics.params.first() {
|
||||
return Err(Error::new(param.span(), "Generic parameters not supported."));
|
||||
}
|
||||
|
||||
let interface = get_runtime_interface(trait_def)?;
|
||||
let mut folder = ToEssentialTraitDef::new();
|
||||
for (version, interface_method) in interface.all_versions() {
|
||||
folder.process(interface_method, version);
|
||||
}
|
||||
let methods = folder.into_methods()?;
|
||||
|
||||
Ok(quote! {
|
||||
trait #trait_ {
|
||||
#( #methods )*
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Implements the given trait definition for `dyn Externalities`.
|
||||
fn impl_trait_for_externalities(trait_def: &ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
|
||||
let trait_ = &trait_def.ident;
|
||||
let crate_ = generate_crate_access();
|
||||
let interface = get_runtime_interface(trait_def)?;
|
||||
let methods = interface.all_versions().map(|(version, method)| {
|
||||
let mut cloned = (*method).clone();
|
||||
cloned.attrs.retain(|a| !a.path().is_ident("version"));
|
||||
cloned.sig.ident = create_function_ident_with_version(&cloned.sig.ident, version);
|
||||
crate::utils::unpack_inner_types_in_signature(&mut cloned.sig);
|
||||
cloned
|
||||
});
|
||||
|
||||
let impl_type = if is_wasm_only {
|
||||
quote!( &mut dyn #crate_::pezsp_wasm_interface::FunctionContext )
|
||||
} else {
|
||||
quote!( &mut dyn #crate_::Externalities )
|
||||
};
|
||||
|
||||
Ok(quote! {
|
||||
#[cfg(not(bizinikiwi_runtime))]
|
||||
impl #trait_ for #impl_type {
|
||||
#( #methods )*
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user