Skip to content

Commit

Permalink
feat(dan): invocation of functions in templates (#4343)
Browse files Browse the repository at this point in the history
Description
---
* New template test that simulates a component with state, a constructor and get/set methods
    * For now we don't perform real calls to handle state and constructor calls as we don't have those implemented yet
* Renamed existing template test to a more proper "hello world" name
* Refactor of all template wasm compilation/execution/test into reusable functions, as much as possible without macros:
    * Template test initialization and invocation
    * Generation of template ABI code
    * Generation of template wasm code, allowing for multiple functions with different signatures

Motivation and Context
---
One of the next steps in template wasm compilation and execution is the implementation of a more complex example with multiple functions. We also want to refactor as much as possible all common code between examples

How Has This Been Tested?
---
The new units test for the templates pass
  • Loading branch information
mrnaveira committed Jul 26, 2022
1 parent 53397e0 commit 3d92eb0
Show file tree
Hide file tree
Showing 10 changed files with 710 additions and 62 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

@@ -1,6 +1,6 @@
[workspace]
[package]
name = "test_template"
name = "common"
version = "0.1.0"
edition = "2021"

Expand Down
Expand Up @@ -20,59 +20,63 @@
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// TODO: we should only use stdlib if the template dev needs to include it e.g. use core::mem when stdlib is not
// available
use std::{mem, ptr::copy, vec::Vec};
use std::{collections::HashMap, mem, intrinsics::copy};

// TODO: Macro generated code
#[no_mangle]
extern "C" fn TestTemplate_abi() -> *mut u8 {
use tari_template_abi::{encode_with_len, FunctionDef, TemplateDef, Type};
use tari_template_abi::{encode_with_len, FunctionDef, TemplateDef, CallInfo, decode};

pub fn generate_abi(template_name: String, functions: Vec<FunctionDef>) -> *mut u8 {
let template = TemplateDef {
template_name: "TestTemplate".to_string(),
functions: vec![FunctionDef {
name: "initialize".to_string(),
arguments: vec![],
output: Type::Unit,
}],
template_name,
functions,
};

let buf = encode_with_len(&template);
wrap_ptr(buf)
}

#[no_mangle]
extern "C" fn TestTemplate_main(call_info: *mut u8, call_info_len: usize) -> *mut u8 {
use tari_template_abi::{decode, encode_with_len, CallInfo};
type FunctionImpl = Box<dyn Fn(Vec<Vec<u8>>) -> Vec<u8>>;

pub struct TemplateImpl(HashMap<String, FunctionImpl>);

impl TemplateImpl {
pub fn new() -> Self {
Self(HashMap::new())
}

pub fn add_function(&mut self, name: String, implementation: FunctionImpl) {
self.0.insert(name.clone(), implementation);
}
}

pub fn generate_main(call_info: *mut u8, call_info_len: usize, template_impl: TemplateImpl) -> *mut u8 {
if call_info.is_null() {
panic!("call_info is null");
}

let call_data = unsafe { Vec::from_raw_parts(call_info, call_info_len, call_info_len) };
let call_info: CallInfo = decode(&call_data).unwrap();

// Call engine for fun
unsafe { tari_engine(123, std::ptr::null(), 0) };
// get the function
let function = match template_impl.0.get(&call_info.func_name) {
Some(f) => f.clone(),
None => panic!("invalid function name"),
};

// call the function
let result = function(call_info.args);

let msg = format!("'{}' was called", call_info.func_name);
let v = encode_with_len(&msg);
wrap_ptr(v)
// return the encoded results of the function call
wrap_ptr(result)
}

// TODO: ------ Everything below here should be in a common wasm lib ------
fn wrap_ptr(mut v: Vec<u8>) -> *mut u8 {
pub fn wrap_ptr(mut v: Vec<u8>) -> *mut u8 {
let ptr = v.as_mut_ptr();
mem::forget(v);
ptr
}

extern "C" {
pub fn tari_engine(op: u32, input_ptr: *const u8, input_len: usize) -> *mut u8;
}

#[no_mangle]
unsafe extern "C" fn tari_alloc(len: u32) -> *mut u8 {
pub unsafe extern "C" fn tari_alloc(len: u32) -> *mut u8 {
let cap = (len + 4) as usize;
let mut buf = Vec::<u8>::with_capacity(cap);
let ptr = buf.as_mut_ptr();
Expand All @@ -82,10 +86,10 @@ unsafe extern "C" fn tari_alloc(len: u32) -> *mut u8 {
}

#[no_mangle]
unsafe extern "C" fn tari_free(ptr: *mut u8) {
pub unsafe extern "C" fn tari_free(ptr: *mut u8) {
let mut len = [0u8; 4];
copy(ptr, len.as_mut_ptr(), 4);

let cap = (u32::from_le_bytes(len) + 4) as usize;
let _ = Vec::<u8>::from_raw_parts(ptr, cap, cap);
}
}
190 changes: 190 additions & 0 deletions dan_layer/engine/tests/hello_world/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions dan_layer/engine/tests/hello_world/Cargo.toml
@@ -0,0 +1,21 @@
[workspace]
[package]
name = "hello_world"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
tari_template_abi = { path = "../../../template_abi" }
common = { path = "../common" }

[profile.release]
opt-level = 's' # Optimize for size.
lto = true # Enable Link Time Optimization.
codegen-units = 1 # Reduce number of codegen units to increase optimizations.
panic = 'abort' # Abort on panic.
strip = "debuginfo" # Strip debug info.

[lib]
crate-type = ["cdylib", "lib"]

0 comments on commit 3d92eb0

Please sign in to comment.