Skip to content

Commit

Permalink
Rebase to master
Browse files Browse the repository at this point in the history
  • Loading branch information
xgreenx committed Jan 26, 2022
1 parent 2785a90 commit 55bbdae
Show file tree
Hide file tree
Showing 26 changed files with 562 additions and 792 deletions.
26 changes: 8 additions & 18 deletions crates/env/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ where
})
}

/// Returns the execution input to the executed contract and decodes it as `T`.
/// Returns the execution input to the executed contract as a byte slice.
///
/// # Note
///
Expand All @@ -349,24 +349,14 @@ where
///
/// # Usage
///
/// Normally contracts define their own `enum` dispatch types respective
/// to their exported constructors and messages that implement `scale::Decode`
/// according to the constructors or messages selectors and their arguments.
/// These `enum` dispatch types are then given to this procedure as the `T`.
/// Normally contracts decoding(`scale::Decode`) their arguments according to the
/// constructors or messages.
///
/// When using ink! users do not have to construct those enum dispatch types
/// themselves as they are normally generated by the ink! code generation
/// automatically.
///
/// # Errors
///
/// If the given `T` cannot be properly decoded from the expected input.
pub fn decode_input<T>() -> Result<T>
where
T: scale::Decode,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
EnvBackend::decode_input::<T>(instance)
/// When using ink! users do not have to decode input themselves as they are
/// normally decoded by the ink! code generation automatically.
pub fn input_bytes() -> &'static [u8] {
<EnvInstance as OnInstance<'static>>::on_instance(|instance| {
EnvBackend::input_bytes(instance)
})
}

Expand Down
22 changes: 6 additions & 16 deletions crates/env/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ impl ReturnFlags {
}

/// Returns the underlying `u32` representation.
#[cfg(not(feature = "ink-experimental-engine"))]
pub(crate) fn into_u32(self) -> u32 {
self.value
}
Expand Down Expand Up @@ -178,7 +177,7 @@ pub trait EnvBackend {
/// Clears the contract's storage key entry.
fn clear_contract_storage(&mut self, key: &Key);

/// Returns the execution input to the executed contract and decodes it as `T`.
/// Returns the execution input to the executed contract as a byte slice.
///
/// # Note
///
Expand All @@ -189,21 +188,12 @@ pub trait EnvBackend {
///
/// # Usage
///
/// Normally contracts define their own `enum` dispatch types respective
/// to their exported constructors and messages that implement `scale::Decode`
/// according to the constructors or messages selectors and their arguments.
/// These `enum` dispatch types are then given to this procedure as the `T`.
/// Normally contracts decoding(`scale::Decode`) their arguments according to the
/// constructors or messages.
///
/// When using ink! users do not have to construct those enum dispatch types
/// themselves as they are normally generated by the ink! code generation
/// automatically.
///
/// # Errors
///
/// If the given `T` cannot be properly decoded from the expected input.
fn decode_input<T>(&mut self) -> Result<T>
where
T: scale::Decode;
/// When using ink! users do not have to decode input themselves as they are
/// normally decoded by the ink! code generation automatically.
fn input_bytes(&mut self) -> &[u8];

/// Returns the value back to the caller of the executed contract.
///
Expand Down
11 changes: 3 additions & 8 deletions crates/env/src/engine/experimental_off_chain/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,20 +209,15 @@ impl EnvBackend for EnvInstance {
self.engine.clear_storage(key.as_ref())
}

fn decode_input<T>(&mut self) -> Result<T>
where
T: scale::Decode,
{
fn input_bytes(&mut self) -> &[u8] {
unimplemented!("the experimental off chain env does not implement `seal_input`")
}

fn return_value<R>(&mut self, _flags: ReturnFlags, _return_value: &R) -> !
fn return_value<R>(&mut self, flags: ReturnFlags, _return_value: &R) -> !
where
R: scale::Encode,
{
unimplemented!(
"the experimental off chain env does not implement `seal_return_value`"
)
std::process::exit(flags.into_u32() as i32)
}

fn debug_message(&mut self, message: &str) {
Expand Down
12 changes: 9 additions & 3 deletions crates/env/src/engine/experimental_off_chain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ pub struct EnvInstance {
engine: Engine,
}

impl OnInstance for EnvInstance {
impl<'a> OnInstance<'a> for EnvInstance {
fn on_instance<F, R>(f: F) -> R
where
F: FnOnce(&mut Self) -> R,
F: FnOnce(&'a mut Self) -> R,
{
use core::cell::RefCell;
thread_local!(
Expand All @@ -43,7 +43,13 @@ impl OnInstance for EnvInstance {
}
)
);
INSTANCE.with(|instance| f(&mut instance.borrow_mut()))
INSTANCE.with(|instance| {
// SAFETY: The value of `RefCell` will be no change,
// so the lifetime can be extended to `a`(it can be `static`).
let env: &'a mut EnvInstance =
unsafe { core::mem::transmute(&mut *instance.borrow_mut()) };
f(env)
})
}
}

Expand Down
4 changes: 2 additions & 2 deletions crates/env/src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ use crate::backend::{
};
use cfg_if::cfg_if;

pub trait OnInstance: EnvBackend + TypedEnvBackend {
pub trait OnInstance<'a>: 'a + EnvBackend + TypedEnvBackend {
fn on_instance<F, R>(f: F) -> R
where
F: FnOnce(&mut Self) -> R;
F: FnOnce(&'a mut Self) -> R;
}

cfg_if! {
Expand Down
26 changes: 11 additions & 15 deletions crates/env/src/engine/off_chain/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,27 +147,23 @@ impl EnvBackend for EnvInstance {
}
}

fn decode_input<T>(&mut self) -> Result<T>
where
T: scale::Decode,
{
self.exec_context()
.map(|exec_ctx| &exec_ctx.call_data)
.map(scale::Encode::encode)
.map_err(Into::into)
.and_then(|encoded| {
<T as scale::Decode>::decode(&mut &encoded[..])
.map_err(|_| scale::Error::from("could not decode input call data"))
.map_err(Into::into)
})
fn input_bytes(&mut self) -> &[u8] {
let encoded = self
.exec_context()
.map(|exec_ctx| exec_ctx.call_data.to_bytes())
.unwrap();
encoded
}

fn return_value<R>(&mut self, flags: ReturnFlags, return_value: &R) -> !
where
R: scale::Encode,
{
let ctx = self.exec_context_mut().expect(UNINITIALIZED_EXEC_CONTEXT);
ctx.output = Some(return_value.encode());
// Some UI tests use `return_value`, but they don't use `#[ink::test]`
// In that case only exit from the process to avoid panic that context is uninitialized
if let Ok(ctx) = self.exec_context_mut() {
ctx.output = Some(return_value.encode());
}
std::process::exit(flags.into_u32() as i32)
}

Expand Down
12 changes: 9 additions & 3 deletions crates/env/src/engine/off_chain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,16 +256,22 @@ impl EnvInstance {
}
}

impl OnInstance for EnvInstance {
impl<'a> OnInstance<'a> for EnvInstance {
fn on_instance<F, R>(f: F) -> R
where
F: FnOnce(&mut Self) -> R,
F: FnOnce(&'a mut Self) -> R,
{
thread_local!(
static INSTANCE: RefCell<EnvInstance> = RefCell::new(
EnvInstance::uninitialized()
)
);
INSTANCE.with(|instance| f(&mut instance.borrow_mut()))
INSTANCE.with(|instance| {
// SAFETY: The value of `RefCell` will be no change,
// so the lifetime can be extended to `a`(it can be `static`).
let env: &'a mut EnvInstance =
unsafe { core::mem::transmute(&mut *instance.borrow_mut()) };
f(env)
})
}
}
20 changes: 17 additions & 3 deletions crates/env/src/engine/on_chain/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,24 @@ impl core::ops::IndexMut<core::ops::RangeFull> for StaticBuffer {
}
}

impl core::ops::Index<core::ops::RangeTo<usize>> for StaticBuffer {
type Output = [u8];

fn index(&self, index: core::ops::RangeTo<usize>) -> &Self::Output {
core::ops::Index::index(&self.buffer[..], index)
}
}

impl core::ops::IndexMut<core::ops::RangeTo<usize>> for StaticBuffer {
fn index_mut(&mut self, index: core::ops::RangeTo<usize>) -> &mut Self::Output {
core::ops::IndexMut::index_mut(&mut self.buffer[..], index)
}
}

/// Utility to allow for non-heap allocating encoding into a static buffer.
///
/// Required by `ScopedBuffer` internals.
struct EncodeScope<'a> {
pub struct EncodeScope<'a> {
buffer: &'a mut [u8],
len: usize,
}
Expand Down Expand Up @@ -170,7 +184,7 @@ impl<'a> ScopedBuffer<'a> {
/// Appends the encoding of `value` to the scoped buffer.
///
/// Does not return the buffer immediately so that other values can be appended
/// afterwards. The [`take_appended`] method shall be used to return the buffer
/// afterwards. The `take_appended` method shall be used to return the buffer
/// that includes all appended encodings as a single buffer.
pub fn append_encoded<T>(&mut self, value: &T)
where
Expand All @@ -185,7 +199,7 @@ impl<'a> ScopedBuffer<'a> {
let _ = core::mem::replace(&mut self.buffer, buffer);
}

/// Returns the buffer containing all encodings appended via [`append_encoded`]
/// Returns the buffer containing all encodings appended via `append_encoded`
/// in a single byte buffer.
pub fn take_appended(&mut self) -> &'a mut [u8] {
debug_assert_ne!(self.offset, 0);
Expand Down
29 changes: 11 additions & 18 deletions crates/env/src/engine/on_chain/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

use super::{
ext,
EncodeScope,
EnvInstance,
Error as ExtError,
ScopedBuffer,
Expand Down Expand Up @@ -206,16 +207,6 @@ impl EnvInstance {
<T as FromLittleEndian>::from_le_bytes(result)
}

/// Returns the contract property value.
fn get_property<T>(&mut self, ext_fn: fn(output: &mut &mut [u8])) -> Result<T>
where
T: scale::Decode,
{
let full_scope = &mut self.scoped_buffer().take_rest();
ext_fn(full_scope);
scale::Decode::decode(&mut &full_scope[..]).map_err(Into::into)
}

/// Reusable implementation for invoking another contract message.
fn invoke_contract_impl<T, Args, RetType, R>(
&mut self,
Expand Down Expand Up @@ -283,20 +274,22 @@ impl EnvBackend for EnvInstance {
ext::clear_storage(key.as_ref())
}

fn decode_input<T>(&mut self) -> Result<T>
where
T: scale::Decode,
{
self.get_property::<T>(ext::input)
fn input_bytes(&mut self) -> &[u8] {
if !self.input_buffer.initialized {
ext::input(&mut &mut self.input_buffer.buffer[..]);
self.input_buffer.initialized = true;
}
&self.input_buffer.buffer[..]
}

fn return_value<R>(&mut self, flags: ReturnFlags, return_value: &R) -> !
where
R: scale::Encode,
{
let mut scope = self.scoped_buffer();
let enc_return_value = scope.take_encoded(return_value);
ext::return_value(flags, enc_return_value);
let mut scope = EncodeScope::from(&mut self.buffer[..]);
scale::Encode::encode_to(&return_value, &mut scope);
let size = scope.len();
ext::return_value(flags, &self.buffer[..size]);
}

fn debug_message(&mut self, content: &str) {
Expand Down
25 changes: 23 additions & 2 deletions crates/env/src/engine/on_chain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,26 @@ mod impls;

use self::{
buffer::{
EncodeScope,
ScopedBuffer,
StaticBuffer,
},
ext::Error,
};
use super::OnInstance;

/// The buffer with static size of 16 kB for the input of the contract.
///
/// If input requires more than that they will fail.
///
/// Please note that this is still an implementation detail and
/// might change. Users should generally avoid storing too big values
/// into single storage entries.
struct InputBuffer {
initialized: bool,
buffer: StaticBuffer,
}

/// The on-chain environment.
pub struct EnvInstance {
/// Encode & decode buffer with static size of 16 kB.
Expand All @@ -37,15 +50,23 @@ pub struct EnvInstance {
/// might change. Users should generally avoid storing too big values
/// into single storage entries.
buffer: StaticBuffer,

/// Please note that this variable should be initialized only one time.
/// After it should be used only for read only.
input_buffer: InputBuffer,
}

impl OnInstance for EnvInstance {
impl<'a> OnInstance<'a> for EnvInstance {
fn on_instance<F, R>(f: F) -> R
where
F: FnOnce(&mut Self) -> R,
F: FnOnce(&'a mut Self) -> R,
{
static mut INSTANCE: EnvInstance = EnvInstance {
buffer: StaticBuffer::new(),
input_buffer: InputBuffer {
initialized: false,
buffer: StaticBuffer::new(),
},
};
f(unsafe { &mut INSTANCE })
}
Expand Down
3 changes: 3 additions & 0 deletions crates/lang/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ std = [
]
show-codegen-docs = []

# Enable debugging during decoding and execution of messages or constructors.
ink-debug = []

# Due to /~https://github.com/rust-lang/cargo/issues/6915 features that affect a dev-dependency
# currently can't be enabled by a parent crate, hence `["ink_env/ink-experimental-engine"]` does
# not work.
Expand Down
10 changes: 0 additions & 10 deletions crates/lang/codegen/src/generator/arg_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,6 @@ pub fn input_types_tuple(inputs: ir::InputsIter) -> TokenStream2 {
}
}

/// Returns a tuple expression representing the bindings yielded by the inputs.
pub fn input_bindings_tuple(inputs: ir::InputsIter) -> TokenStream2 {
let input_bindings = input_bindings(inputs);
match input_bindings.len() {
0 => quote! { _ },
1 => quote! { #( #input_bindings ),* },
_ => quote! { ( #( #input_bindings ),* ) },
}
}

/// Builds up the `ink_env::call::utils::ArgumentList` type structure for the given types.
pub fn generate_argument_list<'b, Args>(args: Args) -> TokenStream2
where
Expand Down
Loading

0 comments on commit 55bbdae

Please sign in to comment.