sp_runtime_interface/wasm.rs
1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Traits required by the runtime interface from the wasm side.
19
20use crate::RIType;
21
22use core::cell::Cell;
23
24/// Something that can be created from a ffi value.
25///
26/// # Safety
27///
28/// It is unsafe behavior to call `Something::into_ffi_value().get()` and take this as input for
29/// `from_ffi_value`. Implementations are safe to assume that the `arg` given to `from_ffi_value`
30/// is only generated by the corresponding [`host::IntoFFIValue`](crate::host::IntoFFIValue)
31/// implementation.
32pub trait FromFFIValue: Sized + RIType {
33 /// Create `Self` from the given ffi value.
34 fn from_ffi_value(arg: Self::FFIType) -> Self;
35}
36
37/// Something that can be converted into a ffi value.
38pub trait IntoFFIValue: RIType {
39 /// The owned rust type that is stored with the ffi value in [`WrappedFFIValue`].
40 ///
41 /// If no owned value is required, `()` can be used as a type.
42 type Owned;
43
44 /// Convert `self` into a [`WrappedFFIValue`].
45 fn into_ffi_value(&self) -> WrappedFFIValue<Self::FFIType, Self::Owned>;
46}
47
48/// Represents a wrapped ffi value.
49///
50/// It is either the ffi value itself or the ffi value plus some other owned value. By providing
51/// support for storing another owned value besides the actual ffi value certain performance
52/// optimizations can be applied. For example using the pointer to a `Vec<u8>`, while using the
53/// pointer to a SCALE encoded `Vec<u8>` that is stored in this wrapper for any other `Vec<T>`.
54pub enum WrappedFFIValue<T, O = ()> {
55 Wrapped(T),
56 WrappedAndOwned(T, O),
57}
58
59impl<T: Copy, O> WrappedFFIValue<T, O> {
60 /// Returns the wrapped ffi value.
61 pub fn get(&self) -> T {
62 match self {
63 Self::Wrapped(data) | Self::WrappedAndOwned(data, _) => *data,
64 }
65 }
66}
67
68impl<T, O> From<T> for WrappedFFIValue<T, O> {
69 fn from(val: T) -> Self {
70 WrappedFFIValue::Wrapped(val)
71 }
72}
73
74impl<T, O> From<(T, O)> for WrappedFFIValue<T, O> {
75 fn from(val: (T, O)) -> Self {
76 WrappedFFIValue::WrappedAndOwned(val.0, val.1)
77 }
78}
79
80/// The state of an exchangeable function.
81#[derive(Clone, Copy)]
82enum ExchangeableFunctionState {
83 /// Original function is present
84 Original,
85 /// The function has been replaced.
86 Replaced,
87}
88
89/// A function which implementation can be exchanged.
90///
91/// Internally this works by swapping function pointers.
92pub struct ExchangeableFunction<T>(Cell<(T, ExchangeableFunctionState)>);
93
94impl<T> ExchangeableFunction<T> {
95 /// Create a new instance of `ExchangeableFunction`.
96 pub const fn new(impl_: T) -> Self {
97 Self(Cell::new((impl_, ExchangeableFunctionState::Original)))
98 }
99}
100
101impl<T: Copy> ExchangeableFunction<T> {
102 /// Replace the implementation with `new_impl`.
103 ///
104 /// # Panics
105 ///
106 /// Panics when trying to replace an already replaced implementation.
107 ///
108 /// # Returns
109 ///
110 /// Returns the original implementation wrapped in [`RestoreImplementation`].
111 pub fn replace_implementation(&'static self, new_impl: T) -> RestoreImplementation<T> {
112 if let ExchangeableFunctionState::Replaced = self.0.get().1 {
113 panic!("Trying to replace an already replaced implementation!")
114 }
115
116 let old = self.0.replace((new_impl, ExchangeableFunctionState::Replaced));
117
118 RestoreImplementation(self, Some(old.0))
119 }
120
121 /// Restore the original implementation.
122 fn restore_orig_implementation(&self, orig: T) {
123 self.0.set((orig, ExchangeableFunctionState::Original));
124 }
125
126 /// Returns the internal function pointer.
127 pub fn get(&self) -> T {
128 self.0.get().0
129 }
130}
131
132// Wasm does not support threads, so this is safe; qed.
133unsafe impl<T> Sync for ExchangeableFunction<T> {}
134
135/// Restores a function implementation on drop.
136///
137/// Stores a static reference to the function object and the original implementation.
138pub struct RestoreImplementation<T: 'static + Copy>(&'static ExchangeableFunction<T>, Option<T>);
139
140impl<T: Copy> Drop for RestoreImplementation<T> {
141 fn drop(&mut self) {
142 self.0
143 .restore_orig_implementation(self.1.take().expect("Value is only taken on drop; qed"));
144 }
145}