From 531ffa7a1faedfbc15ad2c6c987e362ae0bb6dac Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Wed, 27 Jul 2022 08:22:40 +0100 Subject: [PATCH 01/10] new package for template macros --- dan_layer/engine/tests/macros/Cargo.lock | 47 +++++++++++++++ dan_layer/engine/tests/macros/Cargo.toml | 18 ++++++ dan_layer/engine/tests/macros/src/lib.rs | 76 ++++++++++++++++++++++++ 3 files changed, 141 insertions(+) create mode 100644 dan_layer/engine/tests/macros/Cargo.lock create mode 100644 dan_layer/engine/tests/macros/Cargo.toml create mode 100644 dan_layer/engine/tests/macros/src/lib.rs diff --git a/dan_layer/engine/tests/macros/Cargo.lock b/dan_layer/engine/tests/macros/Cargo.lock new file mode 100644 index 0000000000..5ebb46ed1f --- /dev/null +++ b/dan_layer/engine/tests/macros/Cargo.lock @@ -0,0 +1,47 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" diff --git a/dan_layer/engine/tests/macros/Cargo.toml b/dan_layer/engine/tests/macros/Cargo.toml new file mode 100644 index 0000000000..39c1490a75 --- /dev/null +++ b/dan_layer/engine/tests/macros/Cargo.toml @@ -0,0 +1,18 @@ +[workspace] +[package] +name = "macros" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +# [lib] +# proc-macro = true + +[lib] +crate-type = ["cdylib", "lib"] + +[dependencies] +syn = { version = "1.0.98", features = ["full"] } +proc-macro2 = "1.0.42" +quote = "1.0.20" \ No newline at end of file diff --git a/dan_layer/engine/tests/macros/src/lib.rs b/dan_layer/engine/tests/macros/src/lib.rs new file mode 100644 index 0000000000..0490a325b9 --- /dev/null +++ b/dan_layer/engine/tests/macros/src/lib.rs @@ -0,0 +1,76 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// 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. + +use proc_macro2::TokenStream; +use quote::quote; +use syn::{ + parse::{Parse, ParseStream}, + parse2, + ItemImpl, + ItemStruct, + Result, +}; + +#[allow(dead_code)] +struct TemplateAst { + pub struct_section: ItemStruct, + pub impl_section: ItemImpl, +} + +impl Parse for TemplateAst { + fn parse(input: ParseStream) -> Result { + Ok(Self { + struct_section: input.parse()?, + impl_section: input.parse()?, + }) + } +} + +pub fn generate_template(input: TokenStream) -> Result { + let _template_ast = parse2::(input).unwrap(); + + let output = quote! { + pub mod template {} + }; + + Ok(output) +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use proc_macro2::TokenStream; + + use crate::generate_template; + + #[test] + fn test_hello() { + let input = TokenStream::from_str( + "struct MockTemplate { value: u32 } impl MockTemplate { pub fn foo() -> u32 { 1_u32 } }", + ) + .unwrap(); + + let output = generate_template(input).unwrap(); + println!("{}", output); + } +} From dfd43d6ca2dca43aab140d6d84c5a9ce6ccf801b Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Wed, 27 Jul 2022 08:32:52 +0100 Subject: [PATCH 02/10] export template macro --- dan_layer/engine/tests/macros/Cargo.toml | 5 +- dan_layer/engine/tests/macros/src/lib.rs | 80 ++----------------- dan_layer/engine/tests/macros/src/template.rs | 76 ++++++++++++++++++ 3 files changed, 84 insertions(+), 77 deletions(-) create mode 100644 dan_layer/engine/tests/macros/src/template.rs diff --git a/dan_layer/engine/tests/macros/Cargo.toml b/dan_layer/engine/tests/macros/Cargo.toml index 39c1490a75..ca9cd43ae2 100644 --- a/dan_layer/engine/tests/macros/Cargo.toml +++ b/dan_layer/engine/tests/macros/Cargo.toml @@ -6,11 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -# [lib] -# proc-macro = true - [lib] -crate-type = ["cdylib", "lib"] +proc-macro = true [dependencies] syn = { version = "1.0.98", features = ["full"] } diff --git a/dan_layer/engine/tests/macros/src/lib.rs b/dan_layer/engine/tests/macros/src/lib.rs index 0490a325b9..c0992830fb 100644 --- a/dan_layer/engine/tests/macros/src/lib.rs +++ b/dan_layer/engine/tests/macros/src/lib.rs @@ -1,76 +1,10 @@ -// Copyright 2022. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// 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. +mod template; -use proc_macro2::TokenStream; -use quote::quote; -use syn::{ - parse::{Parse, ParseStream}, - parse2, - ItemImpl, - ItemStruct, - Result, -}; +use proc_macro::TokenStream; -#[allow(dead_code)] -struct TemplateAst { - pub struct_section: ItemStruct, - pub impl_section: ItemImpl, -} - -impl Parse for TemplateAst { - fn parse(input: ParseStream) -> Result { - Ok(Self { - struct_section: input.parse()?, - impl_section: input.parse()?, - }) - } -} - -pub fn generate_template(input: TokenStream) -> Result { - let _template_ast = parse2::(input).unwrap(); - - let output = quote! { - pub mod template {} - }; - - Ok(output) -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use proc_macro2::TokenStream; - - use crate::generate_template; - - #[test] - fn test_hello() { - let input = TokenStream::from_str( - "struct MockTemplate { value: u32 } impl MockTemplate { pub fn foo() -> u32 { 1_u32 } }", - ) - .unwrap(); - - let output = generate_template(input).unwrap(); - println!("{}", output); - } +#[proc_macro] +pub fn template(input: TokenStream) -> TokenStream { + template::generate_template(proc_macro2::TokenStream::from(input)) + .unwrap_or_else(|err| err.to_compile_error()) + .into() } diff --git a/dan_layer/engine/tests/macros/src/template.rs b/dan_layer/engine/tests/macros/src/template.rs new file mode 100644 index 0000000000..761c1e37d2 --- /dev/null +++ b/dan_layer/engine/tests/macros/src/template.rs @@ -0,0 +1,76 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// 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. + +use proc_macro2::TokenStream; +use quote::quote; +use syn::{ + parse::{Parse, ParseStream}, + parse2, + ItemImpl, + ItemStruct, + Result, +}; + +#[allow(dead_code)] +struct TemplateAst { + pub struct_section: ItemStruct, + pub impl_section: ItemImpl, +} + +impl Parse for TemplateAst { + fn parse(input: ParseStream) -> Result { + Ok(Self { + struct_section: input.parse()?, + impl_section: input.parse()?, + }) + } +} + +pub fn generate_template(input: TokenStream) -> Result { + let _template_ast = parse2::(input).unwrap(); + + let output = quote! { + pub mod template {} + }; + + Ok(output) +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use proc_macro2::TokenStream; + + use crate::template::generate_template; + + #[test] + fn test_hello() { + let input = TokenStream::from_str( + "struct MockTemplate { value: u32 } impl MockTemplate { pub fn foo() -> u32 { 1_u32 } }", + ) + .unwrap(); + + let output = generate_template(input).unwrap(); + println!("{}", output); + } +} From d6bf8a57a095e4784319cdac35bc73d1a91b1789 Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Wed, 27 Jul 2022 15:32:31 +0100 Subject: [PATCH 03/10] use the template macro in hello world example --- dan_layer/engine/tests/hello_world/Cargo.lock | 14 ++- dan_layer/engine/tests/hello_world/Cargo.toml | 1 + dan_layer/engine/tests/hello_world/src/lib.rs | 37 ++---- dan_layer/engine/tests/macros/Cargo.lock | 18 +-- dan_layer/engine/tests/macros/Cargo.toml | 2 +- dan_layer/engine/tests/macros/src/ast.rs | 21 ++++ dan_layer/engine/tests/macros/src/lib.rs | 3 +- dan_layer/engine/tests/macros/src/template.rs | 119 ++++++++++++++---- dan_layer/engine/tests/test.rs | 4 +- 9 files changed, 155 insertions(+), 64 deletions(-) create mode 100644 dan_layer/engine/tests/macros/src/ast.rs diff --git a/dan_layer/engine/tests/hello_world/Cargo.lock b/dan_layer/engine/tests/hello_world/Cargo.lock index 65f14a65b3..4570dcebad 100644 --- a/dan_layer/engine/tests/hello_world/Cargo.lock +++ b/dan_layer/engine/tests/hello_world/Cargo.lock @@ -97,6 +97,7 @@ version = "0.1.0" dependencies = [ "common", "tari_template_abi", + "tari_template_macros", ] [[package]] @@ -122,9 +123,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdcc2916cde080c1876ff40292a396541241fe0072ef928cd76582e9ea5d60d2" +checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b" dependencies = [ "unicode-ident", ] @@ -162,6 +163,15 @@ dependencies = [ "borsh", ] +[[package]] +name = "tari_template_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "toml" version = "0.5.9" diff --git a/dan_layer/engine/tests/hello_world/Cargo.toml b/dan_layer/engine/tests/hello_world/Cargo.toml index 110dc2d62b..ac1981236e 100644 --- a/dan_layer/engine/tests/hello_world/Cargo.toml +++ b/dan_layer/engine/tests/hello_world/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] tari_template_abi = { path = "../../../template_abi" } +tari_template_macros = { path = "../macros" } common = { path = "../common" } [profile.release] diff --git a/dan_layer/engine/tests/hello_world/src/lib.rs b/dan_layer/engine/tests/hello_world/src/lib.rs index c0f3abdf70..1c452dcf28 100644 --- a/dan_layer/engine/tests/hello_world/src/lib.rs +++ b/dan_layer/engine/tests/hello_world/src/lib.rs @@ -20,35 +20,14 @@ // 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 tari_template_macros::template; -use common::{generate_abi, generate_main, TemplateImpl}; -use tari_template_abi::{encode_with_len, FunctionDef, Type}; +template! { + struct HelloWorld {} -// TODO: Macro generated code -#[no_mangle] -extern "C" fn HelloWorld_abi() -> *mut u8 { - let template_name = "HelloWorld".to_string(); - - let functions = vec![FunctionDef { - name: "greet".to_string(), - arguments: vec![], - output: Type::String, - }]; - - generate_abi(template_name, functions) -} - -#[no_mangle] -extern "C" fn HelloWorld_main(call_info: *mut u8, call_info_len: usize) -> *mut u8 { - let mut template_impl = TemplateImpl::new(); - - template_impl.add_function("greet".to_string(), Box::new(|_| encode_with_len(&"Hello World!"))); - - generate_main(call_info, call_info_len, template_impl) -} - -extern "C" { - pub fn tari_engine(op: u32, input_ptr: *const u8, input_len: usize) -> *mut u8; + impl HelloWorld { + pub fn greet() -> String { + "Hello World!".to_string() + } + } } diff --git a/dan_layer/engine/tests/macros/Cargo.lock b/dan_layer/engine/tests/macros/Cargo.lock index 5ebb46ed1f..dec296f36d 100644 --- a/dan_layer/engine/tests/macros/Cargo.lock +++ b/dan_layer/engine/tests/macros/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "macros" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "proc-macro2" version = "1.0.42" @@ -40,6 +31,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tari_template_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "unicode-ident" version = "1.0.2" diff --git a/dan_layer/engine/tests/macros/Cargo.toml b/dan_layer/engine/tests/macros/Cargo.toml index ca9cd43ae2..90e56493d4 100644 --- a/dan_layer/engine/tests/macros/Cargo.toml +++ b/dan_layer/engine/tests/macros/Cargo.toml @@ -1,6 +1,6 @@ [workspace] [package] -name = "macros" +name = "tari_template_macros" version = "0.1.0" edition = "2021" diff --git a/dan_layer/engine/tests/macros/src/ast.rs b/dan_layer/engine/tests/macros/src/ast.rs new file mode 100644 index 0000000000..7f18aab241 --- /dev/null +++ b/dan_layer/engine/tests/macros/src/ast.rs @@ -0,0 +1,21 @@ +use syn::{ + parse::{Parse, ParseStream}, + ItemImpl, + ItemStruct, + Result, +}; + +#[allow(dead_code)] +pub struct TemplateAst { + pub struct_section: ItemStruct, + pub impl_section: ItemImpl, +} + +impl Parse for TemplateAst { + fn parse(input: ParseStream) -> Result { + Ok(Self { + struct_section: input.parse()?, + impl_section: input.parse()?, + }) + } +} \ No newline at end of file diff --git a/dan_layer/engine/tests/macros/src/lib.rs b/dan_layer/engine/tests/macros/src/lib.rs index c0992830fb..af736cced7 100644 --- a/dan_layer/engine/tests/macros/src/lib.rs +++ b/dan_layer/engine/tests/macros/src/lib.rs @@ -1,10 +1,11 @@ +mod ast; mod template; use proc_macro::TokenStream; #[proc_macro] pub fn template(input: TokenStream) -> TokenStream { - template::generate_template(proc_macro2::TokenStream::from(input)) + template::generate_template_output(proc_macro2::TokenStream::from(input)) .unwrap_or_else(|err| err.to_compile_error()) .into() } diff --git a/dan_layer/engine/tests/macros/src/template.rs b/dan_layer/engine/tests/macros/src/template.rs index 761c1e37d2..7f9bfc66d2 100644 --- a/dan_layer/engine/tests/macros/src/template.rs +++ b/dan_layer/engine/tests/macros/src/template.rs @@ -21,56 +21,133 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use proc_macro2::TokenStream; -use quote::quote; +use quote::{quote, format_ident}; use syn::{ - parse::{Parse, ParseStream}, parse2, - ItemImpl, - ItemStruct, Result, }; -#[allow(dead_code)] -struct TemplateAst { - pub struct_section: ItemStruct, - pub impl_section: ItemImpl, +use crate::ast::TemplateAst; + +pub fn generate_template_output(input: TokenStream) -> Result { + let ast = parse2::(input).unwrap(); + + let mod_output = generate_mod_output(&ast); + let abi_output = generate_abi_output(&ast)?; + let main_output = generate_main_output(&ast)?; + let engine_output = generate_engine_output(); + + let output = quote! { + #mod_output + + #abi_output + + #main_output + + #engine_output + }; + + Ok(output) } -impl Parse for TemplateAst { - fn parse(input: ParseStream) -> Result { - Ok(Self { - struct_section: input.parse()?, - impl_section: input.parse()?, - }) +fn generate_mod_output(ast: &TemplateAst) ->TokenStream { + let template_name = format_ident!("{}", ast.struct_section.ident); + let functions = &ast.impl_section.items; + + quote! { + pub mod template { + use super::*; + + pub struct #template_name { + // TODO: fill template fields + } + + impl #template_name { + #(#functions)* + } + } } } -pub fn generate_template(input: TokenStream) -> Result { - let _template_ast = parse2::(input).unwrap(); +fn generate_abi_output(ast: &TemplateAst) -> Result { + let template_name_str = format!("{}", ast.struct_section.ident); + let function_name = format_ident!("{}_abi", ast.struct_section.ident); let output = quote! { - pub mod template {} + #[no_mangle] + pub extern "C" fn #function_name() -> *mut u8 { + use ::common::wrap_ptr; + use ::tari_template_abi::{encode_with_len, FunctionDef, TemplateDef, Type}; + + let template = TemplateDef { + template_name: #template_name_str.to_string(), + functions: vec![FunctionDef { + name: "greet".to_string(), + arguments: vec![], + output: Type::String, + }], + }; + + let buf = encode_with_len(&template); + wrap_ptr(buf) + } }; Ok(output) } +fn generate_main_output(ast: &TemplateAst) -> Result { + let function_name = format_ident!("{}_main", ast.struct_section.ident); + + let output = quote! { + #[no_mangle] + pub extern "C" fn #function_name(call_info: *mut u8, call_info_len: usize) -> *mut u8 { + use ::common::wrap_ptr; + use ::tari_template_abi::{decode, encode_with_len, CallInfo}; + + 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(); + + let result = match call_info.func_name.as_str() { + "greet" => "Hello World!".to_string(), + _ => panic!("invalid function name") + }; + + wrap_ptr(encode_with_len(&result)) + } + }; + + Ok(output) +} + +fn generate_engine_output() -> TokenStream { + quote! { + extern "C" { + pub fn tari_engine(op: u32, input_ptr: *const u8, input_len: usize) -> *mut u8; + } + } +} + #[cfg(test)] mod tests { use std::str::FromStr; use proc_macro2::TokenStream; - use crate::template::generate_template; + use crate::template::generate_template_output; #[test] - fn test_hello() { + fn test_hello_world() { let input = TokenStream::from_str( - "struct MockTemplate { value: u32 } impl MockTemplate { pub fn foo() -> u32 { 1_u32 } }", + "struct HelloWorld {} impl HelloWorld { pub fn greet() -> String { \"Hello World!\".to_string() } }", ) .unwrap(); - let output = generate_template(input).unwrap(); + let output = generate_template_output(input).unwrap(); println!("{}", output); } } diff --git a/dan_layer/engine/tests/test.rs b/dan_layer/engine/tests/test.rs index e3eb40ff12..cefcf564b6 100644 --- a/dan_layer/engine/tests/test.rs +++ b/dan_layer/engine/tests/test.rs @@ -35,7 +35,9 @@ use tari_template_abi::encode_with_len; fn test_hello_world() { let template_test = TemplateTest::new("HelloWorld".to_string(), "tests/hello_world".to_string()); let result: String = template_test.run_instruction("greet".to_string(), vec![]); - assert_eq!(result, "Hello World!"); + + // FIXME: without the "encode_with_len" calls, the strings are different because of added padding characters + assert_eq!(encode_with_len(&result), encode_with_len(&"Hello World!")); } #[test] From 2ec0493c525b1238209d2d5d0e6969fce3ed4480 Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Wed, 27 Jul 2022 20:09:22 +0100 Subject: [PATCH 04/10] output real abi function names and return types --- dan_layer/engine/tests/hello_world/Cargo.lock | 1 + dan_layer/engine/tests/macros/Cargo.lock | 138 ++++++++++++++++++ dan_layer/engine/tests/macros/Cargo.toml | 1 + dan_layer/engine/tests/macros/src/ast.rs | 90 +++++++++++- dan_layer/engine/tests/macros/src/template.rs | 58 +++++--- 5 files changed, 270 insertions(+), 18 deletions(-) diff --git a/dan_layer/engine/tests/hello_world/Cargo.lock b/dan_layer/engine/tests/hello_world/Cargo.lock index 4570dcebad..57f0926882 100644 --- a/dan_layer/engine/tests/hello_world/Cargo.lock +++ b/dan_layer/engine/tests/hello_world/Cargo.lock @@ -170,6 +170,7 @@ dependencies = [ "proc-macro2", "quote", "syn", + "tari_template_abi", ] [[package]] diff --git a/dan_layer/engine/tests/macros/Cargo.lock b/dan_layer/engine/tests/macros/Cargo.lock index dec296f36d..ef3fb33195 100644 --- a/dan_layer/engine/tests/macros/Cargo.lock +++ b/dan_layer/engine/tests/macros/Cargo.lock @@ -2,6 +2,109 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "borsh" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" +dependencies = [ + "borsh-derive", + "hashbrown", +] + +[[package]] +name = "borsh-derive" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate", + "proc-macro2", + "syn", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + [[package]] name = "proc-macro2" version = "1.0.42" @@ -20,6 +123,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "serde" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" + [[package]] name = "syn" version = "1.0.98" @@ -31,6 +140,13 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tari_template_abi" +version = "0.1.0" +dependencies = [ + "borsh", +] + [[package]] name = "tari_template_macros" version = "0.1.0" @@ -38,6 +154,16 @@ dependencies = [ "proc-macro2", "quote", "syn", + "tari_template_abi", +] + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", ] [[package]] @@ -45,3 +171,15 @@ name = "unicode-ident" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/dan_layer/engine/tests/macros/Cargo.toml b/dan_layer/engine/tests/macros/Cargo.toml index 90e56493d4..725b337894 100644 --- a/dan_layer/engine/tests/macros/Cargo.toml +++ b/dan_layer/engine/tests/macros/Cargo.toml @@ -10,6 +10,7 @@ edition = "2021" proc-macro = true [dependencies] +tari_template_abi = { path = "../../../template_abi" } syn = { version = "1.0.98", features = ["full"] } proc-macro2 = "1.0.42" quote = "1.0.20" \ No newline at end of file diff --git a/dan_layer/engine/tests/macros/src/ast.rs b/dan_layer/engine/tests/macros/src/ast.rs index 7f18aab241..cb252184b8 100644 --- a/dan_layer/engine/tests/macros/src/ast.rs +++ b/dan_layer/engine/tests/macros/src/ast.rs @@ -1,9 +1,12 @@ use syn::{ parse::{Parse, ParseStream}, + parse_quote, + Expr, ItemImpl, ItemStruct, Result, }; +use tari_template_abi::{FunctionDef, Type}; #[allow(dead_code)] pub struct TemplateAst { @@ -18,4 +21,89 @@ impl Parse for TemplateAst { impl_section: input.parse()?, }) } -} \ No newline at end of file +} + +impl TemplateAst { + pub fn get_function_definitions(&self) -> Vec { + self.impl_section + .items + .iter() + .map(|item| match item { + syn::ImplItem::Method(m) => FunctionDef { + name: m.sig.ident.to_string(), + arguments: Self::map_abi_input_types(&m.sig.inputs), + output: Self::map_abi_return_type(&m.sig.output), + }, + _ => todo!(), + }) + .collect() + } + + fn map_abi_input_types(inputs: &syn::punctuated::Punctuated) -> Vec { + inputs + .iter() + .map(|input| { + match input { + // TODO: handle the "self" case + syn::FnArg::Receiver(_) => todo!(), + syn::FnArg::Typed(t) => Self::map_to_abi_type(&t.ty), + } + }) + .collect() + } + + fn map_abi_return_type(ast_return_type: &syn::ReturnType) -> Type { + match ast_return_type { + syn::ReturnType::Default => Type::Unit, + syn::ReturnType::Type(_, t) => Self::map_to_abi_type(t), + } + } + + fn map_to_abi_type(ast_type: &syn::Type) -> Type { + match ast_type { + syn::Type::Path(type_path) => { + // TODO: handle "Self" + // TODO: detect more complex types + let ident = type_path.path.segments[0].ident.to_string(); + // TODO: refactor to avoid these hardcoded string values + match ident.as_str() { + "()" => Type::Unit, + "bool" => Type::Bool, + "i8" => Type::I8, + "i16" => Type::I16, + "i32" => Type::I32, + "i64" => Type::I64, + "i128" => Type::I128, + "u8" => Type::U8, + "u16" => Type::U16, + "u32" => Type::U32, + "u64" => Type::U64, + "u128" => Type::U128, + "String" => Type::String, + _ => todo!(), + } + }, + _ => todo!(), + } + } + + // TODO: this function probably should not be here + pub fn get_abi_type_expr(abi_type: &Type) -> Expr { + // TODO: there must be a better way of doing this... + match *abi_type { + Type::Unit => parse_quote!(Type::Unit), + Type::Bool => parse_quote!(Type::Bool), + Type::I8 => parse_quote!(Type::I8), + Type::I16 => parse_quote!(Type::I16), + Type::I32 => parse_quote!(Type::I32), + Type::I64 => parse_quote!(Type::I64), + Type::I128 => parse_quote!(Type::I128), + Type::U8 => parse_quote!(Type::U8), + Type::U16 => parse_quote!(Type::U16), + Type::U32 => parse_quote!(Type::U32), + Type::U64 => parse_quote!(Type::U64), + Type::U128 => parse_quote!(Type::U128), + Type::String => parse_quote!(Type::String), + } + } +} diff --git a/dan_layer/engine/tests/macros/src/template.rs b/dan_layer/engine/tests/macros/src/template.rs index 7f9bfc66d2..736d8c69e0 100644 --- a/dan_layer/engine/tests/macros/src/template.rs +++ b/dan_layer/engine/tests/macros/src/template.rs @@ -21,11 +21,9 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use proc_macro2::TokenStream; -use quote::{quote, format_ident}; -use syn::{ - parse2, - Result, -}; +use quote::{format_ident, quote}; +use syn::{parse2, parse_quote, Expr, Result}; +use tari_template_abi::FunctionDef; use crate::ast::TemplateAst; @@ -50,7 +48,7 @@ pub fn generate_template_output(input: TokenStream) -> Result { Ok(output) } -fn generate_mod_output(ast: &TemplateAst) ->TokenStream { +fn generate_mod_output(ast: &TemplateAst) -> TokenStream { let template_name = format_ident!("{}", ast.struct_section.ident); let functions = &ast.impl_section.items; @@ -69,9 +67,12 @@ fn generate_mod_output(ast: &TemplateAst) ->TokenStream { } } -fn generate_abi_output(ast: &TemplateAst) -> Result { +fn generate_abi_output(ast: &TemplateAst) -> Result { let template_name_str = format!("{}", ast.struct_section.ident); - let function_name = format_ident!("{}_abi", ast.struct_section.ident); + let function_name = format_ident!("{}_abi", ast.struct_section.ident); + + let function_defs = ast.get_function_definitions(); + let function_defs_output: Vec = function_defs.iter().map(generate_function_def_output).collect(); let output = quote! { #[no_mangle] @@ -81,11 +82,7 @@ fn generate_abi_output(ast: &TemplateAst) -> Result { let template = TemplateDef { template_name: #template_name_str.to_string(), - functions: vec![FunctionDef { - name: "greet".to_string(), - arguments: vec![], - output: Type::String, - }], + functions: vec![ #(#function_defs_output),* ], }; let buf = encode_with_len(&template); @@ -96,7 +93,20 @@ fn generate_abi_output(ast: &TemplateAst) -> Result { Ok(output) } -fn generate_main_output(ast: &TemplateAst) -> Result { +fn generate_function_def_output(fd: &FunctionDef) -> Expr { + let name = fd.name.clone(); + let output = TemplateAst::get_abi_type_expr(&fd.output); + + parse_quote!( + FunctionDef { + name: #name.to_string(), + arguments: vec![], + output: #output, + } + ) +} + +fn generate_main_output(ast: &TemplateAst) -> Result { let function_name = format_ident!("{}_main", ast.struct_section.ident); let output = quote! { @@ -124,11 +134,11 @@ fn generate_main_output(ast: &TemplateAst) -> Result { Ok(output) } -fn generate_engine_output() -> TokenStream { +fn generate_engine_output() -> TokenStream { quote! { extern "C" { pub fn tari_engine(op: u32, input_ptr: *const u8, input_len: usize) -> *mut u8; - } + } } } @@ -137,8 +147,9 @@ mod tests { use std::str::FromStr; use proc_macro2::TokenStream; + use syn::parse2; - use crate::template::generate_template_output; + use crate::{ast::TemplateAst, template::generate_template_output}; #[test] fn test_hello_world() { @@ -150,4 +161,17 @@ mod tests { let output = generate_template_output(input).unwrap(); println!("{}", output); } + + #[test] + fn playground() { + let input = TokenStream::from_str( + "struct HelloWorld {} impl HelloWorld { pub fn greet() -> String { \"Hello World!\".to_string() } }", + ) + .unwrap(); + + let ast = parse2::(input).unwrap(); + let function_defs = ast.get_function_definitions(); + + println!("{:?}", function_defs); + } } From ca389013435a05a6c23d0bf4466e5d4e52ea458b Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Thu, 28 Jul 2022 08:38:07 +0100 Subject: [PATCH 05/10] output the argument types in abi --- dan_layer/engine/tests/macros/src/template.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dan_layer/engine/tests/macros/src/template.rs b/dan_layer/engine/tests/macros/src/template.rs index 736d8c69e0..9821100f0a 100644 --- a/dan_layer/engine/tests/macros/src/template.rs +++ b/dan_layer/engine/tests/macros/src/template.rs @@ -95,12 +95,17 @@ fn generate_abi_output(ast: &TemplateAst) -> Result { fn generate_function_def_output(fd: &FunctionDef) -> Expr { let name = fd.name.clone(); + let arguments: Vec = fd + .arguments + .iter() + .map(TemplateAst::get_abi_type_expr) + .collect(); let output = TemplateAst::get_abi_type_expr(&fd.output); parse_quote!( FunctionDef { name: #name.to_string(), - arguments: vec![], + arguments: vec![ #(#arguments),* ], output: #output, } ) From b9191a9a50556ad6c7a05eecf27ab41b3a4da610 Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Thu, 28 Jul 2022 08:58:11 +0100 Subject: [PATCH 06/10] remove references to the "common" project --- dan_layer/engine/tests/macros/src/template.rs | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/dan_layer/engine/tests/macros/src/template.rs b/dan_layer/engine/tests/macros/src/template.rs index 9821100f0a..184c192d29 100644 --- a/dan_layer/engine/tests/macros/src/template.rs +++ b/dan_layer/engine/tests/macros/src/template.rs @@ -33,7 +33,7 @@ pub fn generate_template_output(input: TokenStream) -> Result { let mod_output = generate_mod_output(&ast); let abi_output = generate_abi_output(&ast)?; let main_output = generate_main_output(&ast)?; - let engine_output = generate_engine_output(); + let dependencies_output = generate_dependencies_output(); let output = quote! { #mod_output @@ -42,7 +42,7 @@ pub fn generate_template_output(input: TokenStream) -> Result { #main_output - #engine_output + #dependencies_output }; Ok(output) @@ -77,7 +77,6 @@ fn generate_abi_output(ast: &TemplateAst) -> Result { let output = quote! { #[no_mangle] pub extern "C" fn #function_name() -> *mut u8 { - use ::common::wrap_ptr; use ::tari_template_abi::{encode_with_len, FunctionDef, TemplateDef, Type}; let template = TemplateDef { @@ -95,11 +94,7 @@ fn generate_abi_output(ast: &TemplateAst) -> Result { fn generate_function_def_output(fd: &FunctionDef) -> Expr { let name = fd.name.clone(); - let arguments: Vec = fd - .arguments - .iter() - .map(TemplateAst::get_abi_type_expr) - .collect(); + let arguments: Vec = fd.arguments.iter().map(TemplateAst::get_abi_type_expr).collect(); let output = TemplateAst::get_abi_type_expr(&fd.output); parse_quote!( @@ -117,7 +112,6 @@ fn generate_main_output(ast: &TemplateAst) -> Result { let output = quote! { #[no_mangle] pub extern "C" fn #function_name(call_info: *mut u8, call_info_len: usize) -> *mut u8 { - use ::common::wrap_ptr; use ::tari_template_abi::{decode, encode_with_len, CallInfo}; if call_info.is_null() { @@ -139,11 +133,42 @@ fn generate_main_output(ast: &TemplateAst) -> Result { Ok(output) } -fn generate_engine_output() -> TokenStream { +fn generate_dependencies_output() -> TokenStream { quote! { extern "C" { pub fn tari_engine(op: u32, input_ptr: *const u8, input_len: usize) -> *mut u8; } + + pub fn wrap_ptr(mut v: Vec) -> *mut u8 { + use std::mem; + + let ptr = v.as_mut_ptr(); + mem::forget(v); + ptr + } + + #[no_mangle] + pub unsafe extern "C" fn tari_alloc(len: u32) -> *mut u8 { + use std::{mem, intrinsics::copy}; + + let cap = (len + 4) as usize; + let mut buf = Vec::::with_capacity(cap); + let ptr = buf.as_mut_ptr(); + mem::forget(buf); + copy(len.to_le_bytes().as_ptr(), ptr, 4); + ptr + } + + #[no_mangle] + pub unsafe extern "C" fn tari_free(ptr: *mut u8) { + use std::intrinsics::copy; + + let mut len = [0u8; 4]; + copy(ptr, len.as_mut_ptr(), 4); + + let cap = (u32::from_le_bytes(len) + 4) as usize; + let _ = Vec::::from_raw_parts(ptr, cap, cap); + } } } From 8a41f44fbb174e0fdf0bd9a90ea977a020bc49fd Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Thu, 28 Jul 2022 09:41:25 +0100 Subject: [PATCH 07/10] split code generation into separated files --- dan_layer/engine/tests/macros/src/lib.rs | 4 +- .../engine/tests/macros/src/output/abi.rs | 45 ++++ .../tests/macros/src/output/definition.rs | 23 ++ .../tests/macros/src/output/dependencies.rs | 41 ++++ .../tests/macros/src/output/dispatcher.rs | 32 +++ .../engine/tests/macros/src/output/mod.rs | 6 + .../tests/macros/src/output/template.rs | 54 +++++ dan_layer/engine/tests/macros/src/template.rs | 207 ------------------ 8 files changed, 203 insertions(+), 209 deletions(-) create mode 100644 dan_layer/engine/tests/macros/src/output/abi.rs create mode 100644 dan_layer/engine/tests/macros/src/output/definition.rs create mode 100644 dan_layer/engine/tests/macros/src/output/dependencies.rs create mode 100644 dan_layer/engine/tests/macros/src/output/dispatcher.rs create mode 100644 dan_layer/engine/tests/macros/src/output/mod.rs create mode 100644 dan_layer/engine/tests/macros/src/output/template.rs delete mode 100644 dan_layer/engine/tests/macros/src/template.rs diff --git a/dan_layer/engine/tests/macros/src/lib.rs b/dan_layer/engine/tests/macros/src/lib.rs index af736cced7..2a29ca714f 100644 --- a/dan_layer/engine/tests/macros/src/lib.rs +++ b/dan_layer/engine/tests/macros/src/lib.rs @@ -1,11 +1,11 @@ mod ast; -mod template; +mod output; use proc_macro::TokenStream; #[proc_macro] pub fn template(input: TokenStream) -> TokenStream { - template::generate_template_output(proc_macro2::TokenStream::from(input)) + output::template::generate_template(proc_macro2::TokenStream::from(input)) .unwrap_or_else(|err| err.to_compile_error()) .into() } diff --git a/dan_layer/engine/tests/macros/src/output/abi.rs b/dan_layer/engine/tests/macros/src/output/abi.rs new file mode 100644 index 0000000000..c895e59706 --- /dev/null +++ b/dan_layer/engine/tests/macros/src/output/abi.rs @@ -0,0 +1,45 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use syn::{parse_quote, Expr, Result}; +use tari_template_abi::FunctionDef; + +use crate::ast::TemplateAst; + +pub fn generate_abi(ast: &TemplateAst) -> Result { + let template_name_str = format!("{}", ast.struct_section.ident); + let function_name = format_ident!("{}_abi", ast.struct_section.ident); + + let function_defs = ast.get_function_definitions(); + let function_defs_output: Vec = function_defs.iter().map(generate_function_def).collect(); + + let output = quote! { + #[no_mangle] + pub extern "C" fn #function_name() -> *mut u8 { + use ::tari_template_abi::{encode_with_len, FunctionDef, TemplateDef, Type}; + + let template = TemplateDef { + template_name: #template_name_str.to_string(), + functions: vec![ #(#function_defs_output),* ], + }; + + let buf = encode_with_len(&template); + wrap_ptr(buf) + } + }; + + Ok(output) +} + +fn generate_function_def(fd: &FunctionDef) -> Expr { + let name = fd.name.clone(); + let arguments: Vec = fd.arguments.iter().map(TemplateAst::get_abi_type_expr).collect(); + let output = TemplateAst::get_abi_type_expr(&fd.output); + + parse_quote!( + FunctionDef { + name: #name.to_string(), + arguments: vec![ #(#arguments),* ], + output: #output, + } + ) +} diff --git a/dan_layer/engine/tests/macros/src/output/definition.rs b/dan_layer/engine/tests/macros/src/output/definition.rs new file mode 100644 index 0000000000..69c9bc227e --- /dev/null +++ b/dan_layer/engine/tests/macros/src/output/definition.rs @@ -0,0 +1,23 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; + +use crate::ast::TemplateAst; + +pub fn generate_definition(ast: &TemplateAst) -> TokenStream { + let template_name = format_ident!("{}", ast.struct_section.ident); + let functions = &ast.impl_section.items; + + quote! { + pub mod template { + use super::*; + + pub struct #template_name { + // TODO: fill template fields + } + + impl #template_name { + #(#functions)* + } + } + } +} diff --git a/dan_layer/engine/tests/macros/src/output/dependencies.rs b/dan_layer/engine/tests/macros/src/output/dependencies.rs new file mode 100644 index 0000000000..e9e6305d68 --- /dev/null +++ b/dan_layer/engine/tests/macros/src/output/dependencies.rs @@ -0,0 +1,41 @@ +use proc_macro2::TokenStream; +use quote::quote; + +pub fn generate_dependencies() -> TokenStream { + quote! { + extern "C" { + pub fn tari_engine(op: u32, input_ptr: *const u8, input_len: usize) -> *mut u8; + } + + pub fn wrap_ptr(mut v: Vec) -> *mut u8 { + use std::mem; + + let ptr = v.as_mut_ptr(); + mem::forget(v); + ptr + } + + #[no_mangle] + pub unsafe extern "C" fn tari_alloc(len: u32) -> *mut u8 { + use std::{mem, intrinsics::copy}; + + let cap = (len + 4) as usize; + let mut buf = Vec::::with_capacity(cap); + let ptr = buf.as_mut_ptr(); + mem::forget(buf); + copy(len.to_le_bytes().as_ptr(), ptr, 4); + ptr + } + + #[no_mangle] + pub unsafe extern "C" fn tari_free(ptr: *mut u8) { + use std::intrinsics::copy; + + let mut len = [0u8; 4]; + copy(ptr, len.as_mut_ptr(), 4); + + let cap = (u32::from_le_bytes(len) + 4) as usize; + let _ = Vec::::from_raw_parts(ptr, cap, cap); + } + } +} diff --git a/dan_layer/engine/tests/macros/src/output/dispatcher.rs b/dan_layer/engine/tests/macros/src/output/dispatcher.rs new file mode 100644 index 0000000000..53c8e557a3 --- /dev/null +++ b/dan_layer/engine/tests/macros/src/output/dispatcher.rs @@ -0,0 +1,32 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use syn::Result; + +use crate::ast::TemplateAst; + +pub fn generate_dispatcher(ast: &TemplateAst) -> Result { + let function_name = format_ident!("{}_main", ast.struct_section.ident); + + let output = quote! { + #[no_mangle] + pub extern "C" fn #function_name(call_info: *mut u8, call_info_len: usize) -> *mut u8 { + use ::tari_template_abi::{decode, encode_with_len, CallInfo}; + + 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(); + + let result = match call_info.func_name.as_str() { + "greet" => "Hello World!".to_string(), + _ => panic!("invalid function name") + }; + + wrap_ptr(encode_with_len(&result)) + } + }; + + Ok(output) +} diff --git a/dan_layer/engine/tests/macros/src/output/mod.rs b/dan_layer/engine/tests/macros/src/output/mod.rs new file mode 100644 index 0000000000..6affaf9fc3 --- /dev/null +++ b/dan_layer/engine/tests/macros/src/output/mod.rs @@ -0,0 +1,6 @@ +pub mod template; + +mod abi; +mod definition; +mod dependencies; +mod dispatcher; diff --git a/dan_layer/engine/tests/macros/src/output/template.rs b/dan_layer/engine/tests/macros/src/output/template.rs new file mode 100644 index 0000000000..3c3ba922f2 --- /dev/null +++ b/dan_layer/engine/tests/macros/src/output/template.rs @@ -0,0 +1,54 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// 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. + +use proc_macro2::TokenStream; +use quote::quote; +use syn::{parse2, Result}; + +use super::{ + abi::generate_abi, + definition::generate_definition, + dependencies::generate_dependencies, + dispatcher::generate_dispatcher, +}; +use crate::ast::TemplateAst; + +pub fn generate_template(input: TokenStream) -> Result { + let ast = parse2::(input).unwrap(); + + let definition = generate_definition(&ast); + let abi = generate_abi(&ast)?; + let dispatcher = generate_dispatcher(&ast)?; + let dependencies = generate_dependencies(); + + let output = quote! { + #definition + + #abi + + #dispatcher + + #dependencies + }; + + Ok(output) +} diff --git a/dan_layer/engine/tests/macros/src/template.rs b/dan_layer/engine/tests/macros/src/template.rs deleted file mode 100644 index 184c192d29..0000000000 --- a/dan_layer/engine/tests/macros/src/template.rs +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2022. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// 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. - -use proc_macro2::TokenStream; -use quote::{format_ident, quote}; -use syn::{parse2, parse_quote, Expr, Result}; -use tari_template_abi::FunctionDef; - -use crate::ast::TemplateAst; - -pub fn generate_template_output(input: TokenStream) -> Result { - let ast = parse2::(input).unwrap(); - - let mod_output = generate_mod_output(&ast); - let abi_output = generate_abi_output(&ast)?; - let main_output = generate_main_output(&ast)?; - let dependencies_output = generate_dependencies_output(); - - let output = quote! { - #mod_output - - #abi_output - - #main_output - - #dependencies_output - }; - - Ok(output) -} - -fn generate_mod_output(ast: &TemplateAst) -> TokenStream { - let template_name = format_ident!("{}", ast.struct_section.ident); - let functions = &ast.impl_section.items; - - quote! { - pub mod template { - use super::*; - - pub struct #template_name { - // TODO: fill template fields - } - - impl #template_name { - #(#functions)* - } - } - } -} - -fn generate_abi_output(ast: &TemplateAst) -> Result { - let template_name_str = format!("{}", ast.struct_section.ident); - let function_name = format_ident!("{}_abi", ast.struct_section.ident); - - let function_defs = ast.get_function_definitions(); - let function_defs_output: Vec = function_defs.iter().map(generate_function_def_output).collect(); - - let output = quote! { - #[no_mangle] - pub extern "C" fn #function_name() -> *mut u8 { - use ::tari_template_abi::{encode_with_len, FunctionDef, TemplateDef, Type}; - - let template = TemplateDef { - template_name: #template_name_str.to_string(), - functions: vec![ #(#function_defs_output),* ], - }; - - let buf = encode_with_len(&template); - wrap_ptr(buf) - } - }; - - Ok(output) -} - -fn generate_function_def_output(fd: &FunctionDef) -> Expr { - let name = fd.name.clone(); - let arguments: Vec = fd.arguments.iter().map(TemplateAst::get_abi_type_expr).collect(); - let output = TemplateAst::get_abi_type_expr(&fd.output); - - parse_quote!( - FunctionDef { - name: #name.to_string(), - arguments: vec![ #(#arguments),* ], - output: #output, - } - ) -} - -fn generate_main_output(ast: &TemplateAst) -> Result { - let function_name = format_ident!("{}_main", ast.struct_section.ident); - - let output = quote! { - #[no_mangle] - pub extern "C" fn #function_name(call_info: *mut u8, call_info_len: usize) -> *mut u8 { - use ::tari_template_abi::{decode, encode_with_len, CallInfo}; - - 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(); - - let result = match call_info.func_name.as_str() { - "greet" => "Hello World!".to_string(), - _ => panic!("invalid function name") - }; - - wrap_ptr(encode_with_len(&result)) - } - }; - - Ok(output) -} - -fn generate_dependencies_output() -> TokenStream { - quote! { - extern "C" { - pub fn tari_engine(op: u32, input_ptr: *const u8, input_len: usize) -> *mut u8; - } - - pub fn wrap_ptr(mut v: Vec) -> *mut u8 { - use std::mem; - - let ptr = v.as_mut_ptr(); - mem::forget(v); - ptr - } - - #[no_mangle] - pub unsafe extern "C" fn tari_alloc(len: u32) -> *mut u8 { - use std::{mem, intrinsics::copy}; - - let cap = (len + 4) as usize; - let mut buf = Vec::::with_capacity(cap); - let ptr = buf.as_mut_ptr(); - mem::forget(buf); - copy(len.to_le_bytes().as_ptr(), ptr, 4); - ptr - } - - #[no_mangle] - pub unsafe extern "C" fn tari_free(ptr: *mut u8) { - use std::intrinsics::copy; - - let mut len = [0u8; 4]; - copy(ptr, len.as_mut_ptr(), 4); - - let cap = (u32::from_le_bytes(len) + 4) as usize; - let _ = Vec::::from_raw_parts(ptr, cap, cap); - } - } -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use proc_macro2::TokenStream; - use syn::parse2; - - use crate::{ast::TemplateAst, template::generate_template_output}; - - #[test] - fn test_hello_world() { - let input = TokenStream::from_str( - "struct HelloWorld {} impl HelloWorld { pub fn greet() -> String { \"Hello World!\".to_string() } }", - ) - .unwrap(); - - let output = generate_template_output(input).unwrap(); - println!("{}", output); - } - - #[test] - fn playground() { - let input = TokenStream::from_str( - "struct HelloWorld {} impl HelloWorld { pub fn greet() -> String { \"Hello World!\".to_string() } }", - ) - .unwrap(); - - let ast = parse2::(input).unwrap(); - let function_defs = ast.get_function_definitions(); - - println!("{:?}", function_defs); - } -} From ee2d9173c94962670c3c26e54579c40b427e35ed Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Thu, 28 Jul 2022 11:09:17 +0100 Subject: [PATCH 08/10] refactor abi function type mapping --- dan_layer/engine/tests/macros/src/ast.rs | 108 +++++++----------- .../engine/tests/macros/src/output/abi.rs | 50 +++++--- 2 files changed, 80 insertions(+), 78 deletions(-) diff --git a/dan_layer/engine/tests/macros/src/ast.rs b/dan_layer/engine/tests/macros/src/ast.rs index cb252184b8..064810cb06 100644 --- a/dan_layer/engine/tests/macros/src/ast.rs +++ b/dan_layer/engine/tests/macros/src/ast.rs @@ -1,109 +1,89 @@ use syn::{ parse::{Parse, ParseStream}, - parse_quote, - Expr, + punctuated::Punctuated, + token::Comma, + FnArg, + Ident, + ImplItem, ItemImpl, ItemStruct, Result, + ReturnType, }; -use tari_template_abi::{FunctionDef, Type}; #[allow(dead_code)] pub struct TemplateAst { + pub template_name: Ident, pub struct_section: ItemStruct, pub impl_section: ItemImpl, } impl Parse for TemplateAst { fn parse(input: ParseStream) -> Result { + let struct_section: ItemStruct = input.parse()?; + let impl_section = input.parse()?; + let template_name = struct_section.ident.clone(); + Ok(Self { - struct_section: input.parse()?, - impl_section: input.parse()?, + template_name, + struct_section, + impl_section, }) } } impl TemplateAst { - pub fn get_function_definitions(&self) -> Vec { + pub fn get_functions(&self) -> Vec { self.impl_section .items .iter() - .map(|item| match item { - syn::ImplItem::Method(m) => FunctionDef { - name: m.sig.ident.to_string(), - arguments: Self::map_abi_input_types(&m.sig.inputs), - output: Self::map_abi_return_type(&m.sig.output), - }, - _ => todo!(), - }) + .map(Self::get_function_from_item) .collect() } - fn map_abi_input_types(inputs: &syn::punctuated::Punctuated) -> Vec { + fn get_function_from_item(item: &ImplItem) -> FunctionAst { + match item { + ImplItem::Method(m) => FunctionAst { + name: m.sig.ident.to_string(), + input_types: Self::get_input_type_tokens(&m.sig.inputs), + output_type: Self::get_output_type_token(&m.sig.output), + }, + _ => todo!(), + } + } + + fn get_input_type_tokens(inputs: &Punctuated) -> Vec { inputs .iter() - .map(|input| { - match input { - // TODO: handle the "self" case - syn::FnArg::Receiver(_) => todo!(), - syn::FnArg::Typed(t) => Self::map_to_abi_type(&t.ty), - } + .map(|arg| match arg { + // TODO: handle the "self" case + syn::FnArg::Receiver(_) => todo!(), + syn::FnArg::Typed(t) => Self::get_type_token(&t.ty), }) .collect() } - fn map_abi_return_type(ast_return_type: &syn::ReturnType) -> Type { - match ast_return_type { - syn::ReturnType::Default => Type::Unit, - syn::ReturnType::Type(_, t) => Self::map_to_abi_type(t), + fn get_output_type_token(ast_type: &ReturnType) -> String { + match ast_type { + syn::ReturnType::Default => String::new(), // the function does not return anything + syn::ReturnType::Type(_, t) => Self::get_type_token(t), } } - fn map_to_abi_type(ast_type: &syn::Type) -> Type { - match ast_type { + fn get_type_token(syn_type: &syn::Type) -> String { + match syn_type { syn::Type::Path(type_path) => { // TODO: handle "Self" // TODO: detect more complex types - let ident = type_path.path.segments[0].ident.to_string(); - // TODO: refactor to avoid these hardcoded string values - match ident.as_str() { - "()" => Type::Unit, - "bool" => Type::Bool, - "i8" => Type::I8, - "i16" => Type::I16, - "i32" => Type::I32, - "i64" => Type::I64, - "i128" => Type::I128, - "u8" => Type::U8, - "u16" => Type::U16, - "u32" => Type::U32, - "u64" => Type::U64, - "u128" => Type::U128, - "String" => Type::String, - _ => todo!(), - } + type_path.path.segments[0].ident.to_string() }, _ => todo!(), } } +} - // TODO: this function probably should not be here - pub fn get_abi_type_expr(abi_type: &Type) -> Expr { - // TODO: there must be a better way of doing this... - match *abi_type { - Type::Unit => parse_quote!(Type::Unit), - Type::Bool => parse_quote!(Type::Bool), - Type::I8 => parse_quote!(Type::I8), - Type::I16 => parse_quote!(Type::I16), - Type::I32 => parse_quote!(Type::I32), - Type::I64 => parse_quote!(Type::I64), - Type::I128 => parse_quote!(Type::I128), - Type::U8 => parse_quote!(Type::U8), - Type::U16 => parse_quote!(Type::U16), - Type::U32 => parse_quote!(Type::U32), - Type::U64 => parse_quote!(Type::U64), - Type::U128 => parse_quote!(Type::U128), - Type::String => parse_quote!(Type::String), - } - } +pub struct FunctionAst { + pub name: String, + pub input_types: Vec, + pub output_type: String, } diff --git a/dan_layer/engine/tests/macros/src/output/abi.rs b/dan_layer/engine/tests/macros/src/output/abi.rs index c895e59706..f26822cf28 100644 --- a/dan_layer/engine/tests/macros/src/output/abi.rs +++ b/dan_layer/engine/tests/macros/src/output/abi.rs @@ -1,25 +1,22 @@ use proc_macro2::TokenStream; use quote::{format_ident, quote}; use syn::{parse_quote, Expr, Result}; -use tari_template_abi::FunctionDef; -use crate::ast::TemplateAst; +use crate::ast::{FunctionAst, TemplateAst}; pub fn generate_abi(ast: &TemplateAst) -> Result { - let template_name_str = format!("{}", ast.struct_section.ident); - let function_name = format_ident!("{}_abi", ast.struct_section.ident); - - let function_defs = ast.get_function_definitions(); - let function_defs_output: Vec = function_defs.iter().map(generate_function_def).collect(); + let abi_function_name = format_ident!("{}_abi", ast.struct_section.ident); + let template_name_as_str = ast.template_name.to_string(); + let function_defs: Vec = ast.get_functions().iter().map(generate_function_def).collect(); let output = quote! { #[no_mangle] - pub extern "C" fn #function_name() -> *mut u8 { + pub extern "C" fn #abi_function_name() -> *mut u8 { use ::tari_template_abi::{encode_with_len, FunctionDef, TemplateDef, Type}; let template = TemplateDef { - template_name: #template_name_str.to_string(), - functions: vec![ #(#function_defs_output),* ], + template_name: #template_name_as_str.to_string(), + functions: vec![ #(#function_defs),* ], }; let buf = encode_with_len(&template); @@ -30,10 +27,15 @@ pub fn generate_abi(ast: &TemplateAst) -> Result { Ok(output) } -fn generate_function_def(fd: &FunctionDef) -> Expr { - let name = fd.name.clone(); - let arguments: Vec = fd.arguments.iter().map(TemplateAst::get_abi_type_expr).collect(); - let output = TemplateAst::get_abi_type_expr(&fd.output); +fn generate_function_def(f: &FunctionAst) -> Expr { + let name = f.name.clone(); + let arguments: Vec = f + .input_types + .iter() + .map(String::as_str) + .map(generate_abi_type) + .collect(); + let output = generate_abi_type(&f.output_type); parse_quote!( FunctionDef { @@ -43,3 +45,23 @@ fn generate_function_def(fd: &FunctionDef) -> Expr { } ) } + +fn generate_abi_type(rust_type: &str) -> Expr { + // TODO: there may be a better way of handling this + match rust_type { + "" => parse_quote!(Type::Unit), + "bool" => parse_quote!(Type::Bool), + "i8" => parse_quote!(Type::I8), + "i16" => parse_quote!(Type::I16), + "i32" => parse_quote!(Type::I32), + "i64" => parse_quote!(Type::I64), + "i128" => parse_quote!(Type::I128), + "u8" => parse_quote!(Type::U8), + "u16" => parse_quote!(Type::U16), + "u32" => parse_quote!(Type::U32), + "u64" => parse_quote!(Type::U64), + "u128" => parse_quote!(Type::U128), + "String" => parse_quote!(Type::String), + _ => todo!(), + } +} From 0ee0e4508f9ff505149b058a7b96dd59ef1d1759 Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Thu, 28 Jul 2022 14:51:50 +0100 Subject: [PATCH 09/10] output dispatcher function --- dan_layer/engine/tests/macros/src/ast.rs | 8 ++ .../tests/macros/src/output/dispatcher.rs | 84 +++++++++++++++++-- 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/dan_layer/engine/tests/macros/src/ast.rs b/dan_layer/engine/tests/macros/src/ast.rs index 064810cb06..8b81a7c2b8 100644 --- a/dan_layer/engine/tests/macros/src/ast.rs +++ b/dan_layer/engine/tests/macros/src/ast.rs @@ -5,10 +5,12 @@ use syn::{ FnArg, Ident, ImplItem, + ImplItemMethod, ItemImpl, ItemStruct, Result, ReturnType, + Stmt, }; #[allow(dead_code)] @@ -47,6 +49,7 @@ impl TemplateAst { name: m.sig.ident.to_string(), input_types: Self::get_input_type_tokens(&m.sig.inputs), output_type: Self::get_output_type_token(&m.sig.output), + statements: Self::get_statements(m), }, _ => todo!(), } @@ -80,10 +83,15 @@ impl TemplateAst { _ => todo!(), } } + + fn get_statements(method: &ImplItemMethod) -> Vec { + method.block.stmts.clone() + } } pub struct FunctionAst { pub name: String, pub input_types: Vec, pub output_type: String, + pub statements: Vec, } diff --git a/dan_layer/engine/tests/macros/src/output/dispatcher.rs b/dan_layer/engine/tests/macros/src/output/dispatcher.rs index 53c8e557a3..36d46e03ee 100644 --- a/dan_layer/engine/tests/macros/src/output/dispatcher.rs +++ b/dan_layer/engine/tests/macros/src/output/dispatcher.rs @@ -1,15 +1,17 @@ -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; -use syn::Result; +use syn::{token::Brace, Block, Expr, ExprBlock, Result}; use crate::ast::TemplateAst; pub fn generate_dispatcher(ast: &TemplateAst) -> Result { - let function_name = format_ident!("{}_main", ast.struct_section.ident); + let dispatcher_function_name = format_ident!("{}_main", ast.struct_section.ident); + let function_names = get_function_names(ast); + let function_blocks = get_function_blocks(ast); let output = quote! { #[no_mangle] - pub extern "C" fn #function_name(call_info: *mut u8, call_info_len: usize) -> *mut u8 { + pub extern "C" fn #dispatcher_function_name(call_info: *mut u8, call_info_len: usize) -> *mut u8 { use ::tari_template_abi::{decode, encode_with_len, CallInfo}; if call_info.is_null() { @@ -20,7 +22,7 @@ pub fn generate_dispatcher(ast: &TemplateAst) -> Result { let call_info: CallInfo = decode(&call_data).unwrap(); let result = match call_info.func_name.as_str() { - "greet" => "Hello World!".to_string(), + #( #function_names => #function_blocks )*, _ => panic!("invalid function name") }; @@ -30,3 +32,75 @@ pub fn generate_dispatcher(ast: &TemplateAst) -> Result { Ok(output) } + +pub fn get_function_names(ast: &TemplateAst) -> Vec { + ast.get_functions().iter().map(|f| f.name.clone()).collect() +} + +pub fn get_function_blocks(ast: &TemplateAst) -> Vec { + let mut blocks = vec![]; + + for function in ast.get_functions() { + let statements = function.statements; + blocks.push(Expr::Block(ExprBlock { + attrs: vec![], + label: None, + block: Block { + brace_token: Brace { + span: Span::call_site(), + }, + stmts: statements, + }, + })); + } + + blocks +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use proc_macro2::TokenStream; + use quote::quote; + use syn::parse2; + + use crate::{ast::TemplateAst, output::dispatcher::generate_dispatcher}; + + #[test] + fn test_hello_world() { + let input = TokenStream::from_str( + "struct HelloWorld {} impl HelloWorld { pub fn greet() -> String { \"Hello World!\".to_string() } }", + ) + .unwrap(); + + let ast = parse2::(input).unwrap(); + + let output = generate_dispatcher(&ast).unwrap(); + + assert_code_eq(output, quote! { + #[no_mangle] + pub extern "C" fn HelloWorld_main(call_info: *mut u8, call_info_len: usize) -> *mut u8 { + use ::tari_template_abi::{decode, encode_with_len, CallInfo}; + + 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(); + + let result = match call_info.func_name.as_str() { + "greet" => { "Hello World!".to_string() }, + _ => panic!("invalid function name") + }; + + wrap_ptr(encode_with_len(&result)) + } + }); + } + + fn assert_code_eq(a: TokenStream, b: TokenStream) { + assert_eq!(a.to_string(), b.to_string()); + } +} From 8b5d7cf2992f27dbcec3638f353e9f9b0606c7be Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Thu, 28 Jul 2022 15:02:47 +0100 Subject: [PATCH 10/10] refactor template module and add unit tests --- dan_layer/engine/tests/macros/src/ast.rs | 22 +++ dan_layer/engine/tests/macros/src/lib.rs | 26 +++- .../engine/tests/macros/src/output/abi.rs | 67 --------- .../tests/macros/src/output/definition.rs | 23 --- .../tests/macros/src/output/dependencies.rs | 41 ------ .../engine/tests/macros/src/output/mod.rs | 6 - .../engine/tests/macros/src/template/abi.rs | 136 ++++++++++++++++++ .../tests/macros/src/template/definition.rs | 45 ++++++ .../tests/macros/src/template/dependencies.rs | 63 ++++++++ .../src/{output => template}/dispatcher.rs | 24 +++- .../{output/template.rs => template/mod.rs} | 7 +- 11 files changed, 319 insertions(+), 141 deletions(-) delete mode 100644 dan_layer/engine/tests/macros/src/output/abi.rs delete mode 100644 dan_layer/engine/tests/macros/src/output/definition.rs delete mode 100644 dan_layer/engine/tests/macros/src/output/dependencies.rs delete mode 100644 dan_layer/engine/tests/macros/src/output/mod.rs create mode 100644 dan_layer/engine/tests/macros/src/template/abi.rs create mode 100644 dan_layer/engine/tests/macros/src/template/definition.rs create mode 100644 dan_layer/engine/tests/macros/src/template/dependencies.rs rename dan_layer/engine/tests/macros/src/{output => template}/dispatcher.rs (66%) rename dan_layer/engine/tests/macros/src/{output/template.rs => template/mod.rs} (96%) diff --git a/dan_layer/engine/tests/macros/src/ast.rs b/dan_layer/engine/tests/macros/src/ast.rs index 8b81a7c2b8..bb64a9d30d 100644 --- a/dan_layer/engine/tests/macros/src/ast.rs +++ b/dan_layer/engine/tests/macros/src/ast.rs @@ -1,3 +1,25 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// 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. + use syn::{ parse::{Parse, ParseStream}, punctuated::Punctuated, diff --git a/dan_layer/engine/tests/macros/src/lib.rs b/dan_layer/engine/tests/macros/src/lib.rs index 2a29ca714f..82fe705cec 100644 --- a/dan_layer/engine/tests/macros/src/lib.rs +++ b/dan_layer/engine/tests/macros/src/lib.rs @@ -1,11 +1,33 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// 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. + mod ast; -mod output; +mod template; use proc_macro::TokenStream; #[proc_macro] pub fn template(input: TokenStream) -> TokenStream { - output::template::generate_template(proc_macro2::TokenStream::from(input)) + template::generate_template(proc_macro2::TokenStream::from(input)) .unwrap_or_else(|err| err.to_compile_error()) .into() } diff --git a/dan_layer/engine/tests/macros/src/output/abi.rs b/dan_layer/engine/tests/macros/src/output/abi.rs deleted file mode 100644 index f26822cf28..0000000000 --- a/dan_layer/engine/tests/macros/src/output/abi.rs +++ /dev/null @@ -1,67 +0,0 @@ -use proc_macro2::TokenStream; -use quote::{format_ident, quote}; -use syn::{parse_quote, Expr, Result}; - -use crate::ast::{FunctionAst, TemplateAst}; - -pub fn generate_abi(ast: &TemplateAst) -> Result { - let abi_function_name = format_ident!("{}_abi", ast.struct_section.ident); - let template_name_as_str = ast.template_name.to_string(); - let function_defs: Vec = ast.get_functions().iter().map(generate_function_def).collect(); - - let output = quote! { - #[no_mangle] - pub extern "C" fn #abi_function_name() -> *mut u8 { - use ::tari_template_abi::{encode_with_len, FunctionDef, TemplateDef, Type}; - - let template = TemplateDef { - template_name: #template_name_as_str.to_string(), - functions: vec![ #(#function_defs),* ], - }; - - let buf = encode_with_len(&template); - wrap_ptr(buf) - } - }; - - Ok(output) -} - -fn generate_function_def(f: &FunctionAst) -> Expr { - let name = f.name.clone(); - let arguments: Vec = f - .input_types - .iter() - .map(String::as_str) - .map(generate_abi_type) - .collect(); - let output = generate_abi_type(&f.output_type); - - parse_quote!( - FunctionDef { - name: #name.to_string(), - arguments: vec![ #(#arguments),* ], - output: #output, - } - ) -} - -fn generate_abi_type(rust_type: &str) -> Expr { - // TODO: there may be a better way of handling this - match rust_type { - "" => parse_quote!(Type::Unit), - "bool" => parse_quote!(Type::Bool), - "i8" => parse_quote!(Type::I8), - "i16" => parse_quote!(Type::I16), - "i32" => parse_quote!(Type::I32), - "i64" => parse_quote!(Type::I64), - "i128" => parse_quote!(Type::I128), - "u8" => parse_quote!(Type::U8), - "u16" => parse_quote!(Type::U16), - "u32" => parse_quote!(Type::U32), - "u64" => parse_quote!(Type::U64), - "u128" => parse_quote!(Type::U128), - "String" => parse_quote!(Type::String), - _ => todo!(), - } -} diff --git a/dan_layer/engine/tests/macros/src/output/definition.rs b/dan_layer/engine/tests/macros/src/output/definition.rs deleted file mode 100644 index 69c9bc227e..0000000000 --- a/dan_layer/engine/tests/macros/src/output/definition.rs +++ /dev/null @@ -1,23 +0,0 @@ -use proc_macro2::TokenStream; -use quote::{format_ident, quote}; - -use crate::ast::TemplateAst; - -pub fn generate_definition(ast: &TemplateAst) -> TokenStream { - let template_name = format_ident!("{}", ast.struct_section.ident); - let functions = &ast.impl_section.items; - - quote! { - pub mod template { - use super::*; - - pub struct #template_name { - // TODO: fill template fields - } - - impl #template_name { - #(#functions)* - } - } - } -} diff --git a/dan_layer/engine/tests/macros/src/output/dependencies.rs b/dan_layer/engine/tests/macros/src/output/dependencies.rs deleted file mode 100644 index e9e6305d68..0000000000 --- a/dan_layer/engine/tests/macros/src/output/dependencies.rs +++ /dev/null @@ -1,41 +0,0 @@ -use proc_macro2::TokenStream; -use quote::quote; - -pub fn generate_dependencies() -> TokenStream { - quote! { - extern "C" { - pub fn tari_engine(op: u32, input_ptr: *const u8, input_len: usize) -> *mut u8; - } - - pub fn wrap_ptr(mut v: Vec) -> *mut u8 { - use std::mem; - - let ptr = v.as_mut_ptr(); - mem::forget(v); - ptr - } - - #[no_mangle] - pub unsafe extern "C" fn tari_alloc(len: u32) -> *mut u8 { - use std::{mem, intrinsics::copy}; - - let cap = (len + 4) as usize; - let mut buf = Vec::::with_capacity(cap); - let ptr = buf.as_mut_ptr(); - mem::forget(buf); - copy(len.to_le_bytes().as_ptr(), ptr, 4); - ptr - } - - #[no_mangle] - pub unsafe extern "C" fn tari_free(ptr: *mut u8) { - use std::intrinsics::copy; - - let mut len = [0u8; 4]; - copy(ptr, len.as_mut_ptr(), 4); - - let cap = (u32::from_le_bytes(len) + 4) as usize; - let _ = Vec::::from_raw_parts(ptr, cap, cap); - } - } -} diff --git a/dan_layer/engine/tests/macros/src/output/mod.rs b/dan_layer/engine/tests/macros/src/output/mod.rs deleted file mode 100644 index 6affaf9fc3..0000000000 --- a/dan_layer/engine/tests/macros/src/output/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod template; - -mod abi; -mod definition; -mod dependencies; -mod dispatcher; diff --git a/dan_layer/engine/tests/macros/src/template/abi.rs b/dan_layer/engine/tests/macros/src/template/abi.rs new file mode 100644 index 0000000000..e1d3fc204f --- /dev/null +++ b/dan_layer/engine/tests/macros/src/template/abi.rs @@ -0,0 +1,136 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// 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. + +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use syn::{parse_quote, Expr, Result}; + +use crate::ast::{FunctionAst, TemplateAst}; + +pub fn generate_abi(ast: &TemplateAst) -> Result { + let abi_function_name = format_ident!("{}_abi", ast.struct_section.ident); + let template_name_as_str = ast.template_name.to_string(); + let function_defs: Vec = ast.get_functions().iter().map(generate_function_def).collect(); + + let output = quote! { + #[no_mangle] + pub extern "C" fn #abi_function_name() -> *mut u8 { + use ::tari_template_abi::{encode_with_len, FunctionDef, TemplateDef, Type}; + + let template = TemplateDef { + template_name: #template_name_as_str.to_string(), + functions: vec![ #(#function_defs),* ], + }; + + let buf = encode_with_len(&template); + wrap_ptr(buf) + } + }; + + Ok(output) +} + +fn generate_function_def(f: &FunctionAst) -> Expr { + let name = f.name.clone(); + let arguments: Vec = f + .input_types + .iter() + .map(String::as_str) + .map(generate_abi_type) + .collect(); + let output = generate_abi_type(&f.output_type); + + parse_quote!( + FunctionDef { + name: #name.to_string(), + arguments: vec![ #(#arguments),* ], + output: #output, + } + ) +} + +fn generate_abi_type(rust_type: &str) -> Expr { + // TODO: there may be a better way of handling this + match rust_type { + "" => parse_quote!(Type::Unit), + "bool" => parse_quote!(Type::Bool), + "i8" => parse_quote!(Type::I8), + "i16" => parse_quote!(Type::I16), + "i32" => parse_quote!(Type::I32), + "i64" => parse_quote!(Type::I64), + "i128" => parse_quote!(Type::I128), + "u8" => parse_quote!(Type::U8), + "u16" => parse_quote!(Type::U16), + "u32" => parse_quote!(Type::U32), + "u64" => parse_quote!(Type::U64), + "u128" => parse_quote!(Type::U128), + "String" => parse_quote!(Type::String), + _ => todo!(), + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use proc_macro2::TokenStream; + use quote::quote; + use syn::parse2; + + use super::generate_abi; + use crate::ast::TemplateAst; + + #[test] + fn test_hello_world() { + let input = TokenStream::from_str( + "struct HelloWorld {} impl HelloWorld { pub fn greet() -> String { \"Hello World!\".to_string() } }", + ) + .unwrap(); + + let ast = parse2::(input).unwrap(); + + let output = generate_abi(&ast).unwrap(); + + assert_code_eq(output, quote! { + #[no_mangle] + pub extern "C" fn HelloWorld_abi() -> *mut u8 { + use ::tari_template_abi::{encode_with_len, FunctionDef, TemplateDef, Type}; + + let template = TemplateDef { + template_name: "HelloWorld".to_string(), + functions: vec![ FunctionDef { + name: "greet".to_string(), + arguments: vec![], + output: Type::String, + }], + }; + + let buf = encode_with_len(&template); + wrap_ptr(buf) + } + }); + } + + fn assert_code_eq(a: TokenStream, b: TokenStream) { + assert_eq!(a.to_string(), b.to_string()); + } +} diff --git a/dan_layer/engine/tests/macros/src/template/definition.rs b/dan_layer/engine/tests/macros/src/template/definition.rs new file mode 100644 index 0000000000..dbc330bdb1 --- /dev/null +++ b/dan_layer/engine/tests/macros/src/template/definition.rs @@ -0,0 +1,45 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// 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. + +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; + +use crate::ast::TemplateAst; + +pub fn generate_definition(ast: &TemplateAst) -> TokenStream { + let template_name = format_ident!("{}", ast.struct_section.ident); + let functions = &ast.impl_section.items; + + quote! { + pub mod template { + use super::*; + + pub struct #template_name { + // TODO: fill template fields + } + + impl #template_name { + #(#functions)* + } + } + } +} diff --git a/dan_layer/engine/tests/macros/src/template/dependencies.rs b/dan_layer/engine/tests/macros/src/template/dependencies.rs new file mode 100644 index 0000000000..e997c4e0bf --- /dev/null +++ b/dan_layer/engine/tests/macros/src/template/dependencies.rs @@ -0,0 +1,63 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// 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. + +use proc_macro2::TokenStream; +use quote::quote; + +pub fn generate_dependencies() -> TokenStream { + quote! { + extern "C" { + pub fn tari_engine(op: u32, input_ptr: *const u8, input_len: usize) -> *mut u8; + } + + pub fn wrap_ptr(mut v: Vec) -> *mut u8 { + use std::mem; + + let ptr = v.as_mut_ptr(); + mem::forget(v); + ptr + } + + #[no_mangle] + pub unsafe extern "C" fn tari_alloc(len: u32) -> *mut u8 { + use std::{mem, intrinsics::copy}; + + let cap = (len + 4) as usize; + let mut buf = Vec::::with_capacity(cap); + let ptr = buf.as_mut_ptr(); + mem::forget(buf); + copy(len.to_le_bytes().as_ptr(), ptr, 4); + ptr + } + + #[no_mangle] + pub unsafe extern "C" fn tari_free(ptr: *mut u8) { + use std::intrinsics::copy; + + let mut len = [0u8; 4]; + copy(ptr, len.as_mut_ptr(), 4); + + let cap = (u32::from_le_bytes(len) + 4) as usize; + let _ = Vec::::from_raw_parts(ptr, cap, cap); + } + } +} diff --git a/dan_layer/engine/tests/macros/src/output/dispatcher.rs b/dan_layer/engine/tests/macros/src/template/dispatcher.rs similarity index 66% rename from dan_layer/engine/tests/macros/src/output/dispatcher.rs rename to dan_layer/engine/tests/macros/src/template/dispatcher.rs index 36d46e03ee..53d42fcd27 100644 --- a/dan_layer/engine/tests/macros/src/output/dispatcher.rs +++ b/dan_layer/engine/tests/macros/src/template/dispatcher.rs @@ -1,3 +1,25 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// 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. + use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; use syn::{token::Brace, Block, Expr, ExprBlock, Result}; @@ -65,7 +87,7 @@ mod tests { use quote::quote; use syn::parse2; - use crate::{ast::TemplateAst, output::dispatcher::generate_dispatcher}; + use crate::{ast::TemplateAst, template::dispatcher::generate_dispatcher}; #[test] fn test_hello_world() { diff --git a/dan_layer/engine/tests/macros/src/output/template.rs b/dan_layer/engine/tests/macros/src/template/mod.rs similarity index 96% rename from dan_layer/engine/tests/macros/src/output/template.rs rename to dan_layer/engine/tests/macros/src/template/mod.rs index 3c3ba922f2..e717fd73db 100644 --- a/dan_layer/engine/tests/macros/src/output/template.rs +++ b/dan_layer/engine/tests/macros/src/template/mod.rs @@ -20,11 +20,16 @@ // 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. +mod abi; +mod definition; +mod dependencies; +mod dispatcher; + use proc_macro2::TokenStream; use quote::quote; use syn::{parse2, Result}; -use super::{ +use self::{ abi::generate_abi, definition::generate_definition, dependencies::generate_dependencies,