From 45fcebd36e95b048106f704010e7de953f649ed9 Mon Sep 17 00:00:00 2001 From: Alix ANNERAUD Date: Tue, 10 Feb 2026 23:36:38 +0100 Subject: [PATCH 01/16] Moved WASM bindings from core into wasm executable --- .../wasm/build/guest.rs | 117 ++++- executables/wasm/build/host.rs | 210 +++++++++ executables/wasm/build/main.rs | 18 + .../wasm/build/utilities/additional.rs | 33 ++ .../wasm/build/utilities}/context.rs | 27 +- .../wasm/build/utilities}/enumeration.rs | 12 +- .../wasm/build/utilities}/file.rs | 2 +- .../wasm/build/utilities}/format.rs | 14 - .../wasm/build/utilities}/function.rs | 0 .../wasm/build/utilities/mod.rs | 0 .../wasm/build/utilities}/type_tree.rs | 13 - executables/wasm/src/guest/bindings.rs | 1 + .../wasm/src/guest}/c_functions.rs | 0 .../wasm/src/guest}/enumeration.rs | 0 .../wasm/src/guest}/functions.rs | 0 executables/wasm/src/guest/mod.rs | 7 + .../wasm/src/guest}/prelude.rs | 3 +- .../src/host/bindings/graphics/additionnal.rs | 102 +++++ .../wasm/src/host/bindings/graphics/error.rs | 8 + .../wasm/src/host/bindings/graphics/mod.rs | 119 +++++ .../src/host/bindings/graphics/translate.rs | 405 ++++++++++++++++++ executables/wasm/src/host/bindings/mod.rs | 1 + modules/bindings/host/Cargo.toml | 43 -- modules/bindings/host/build/generator.rs | 326 -------------- modules/bindings/host/build/main.rs | 24 -- modules/bindings/host/src/graphics.rs | 198 --------- modules/bindings/host/src/lib.rs | 4 - modules/bindings/host/tests/graphics.rs | 44 -- .../host/tests/wasm_test/.cargo/config.toml | 16 - .../bindings/host/tests/wasm_test/.gitignore | 1 - .../bindings/host/tests/wasm_test/Cargo.toml | 7 - .../bindings/host/tests/wasm_test/src/main.rs | 48 --- modules/bindings/utilities/Cargo.toml | 9 - modules/bindings/utilities/src/additional.rs | 141 ------ modules/bindings/wasm/.gitignore | 1 - modules/bindings/wasm/Cargo.toml | 19 - modules/bindings/wasm/build/main.rs | 101 ----- modules/bindings/wasm/src/bindings.rs | 1 - modules/bindings/wasm/src/lib.rs | 22 - 39 files changed, 1023 insertions(+), 1074 deletions(-) rename modules/bindings/wasm/build/generator.rs => executables/wasm/build/guest.rs (66%) create mode 100644 executables/wasm/build/host.rs create mode 100644 executables/wasm/build/main.rs create mode 100644 executables/wasm/build/utilities/additional.rs rename {modules/bindings/utilities/src => executables/wasm/build/utilities}/context.rs (90%) rename {modules/bindings/utilities/src => executables/wasm/build/utilities}/enumeration.rs (70%) rename {modules/bindings/utilities/src => executables/wasm/build/utilities}/file.rs (92%) rename {modules/bindings/utilities/src => executables/wasm/build/utilities}/format.rs (74%) rename {modules/bindings/utilities/src => executables/wasm/build/utilities}/function.rs (100%) rename modules/bindings/utilities/src/lib.rs => executables/wasm/build/utilities/mod.rs (100%) rename {modules/bindings/utilities/src => executables/wasm/build/utilities}/type_tree.rs (55%) create mode 100644 executables/wasm/src/guest/bindings.rs rename {modules/bindings/wasm/src => executables/wasm/src/guest}/c_functions.rs (100%) rename {modules/bindings/wasm/src => executables/wasm/src/guest}/enumeration.rs (100%) rename {modules/bindings/wasm/src => executables/wasm/src/guest}/functions.rs (100%) create mode 100644 executables/wasm/src/guest/mod.rs rename {modules/bindings/wasm/src => executables/wasm/src/guest}/prelude.rs (99%) create mode 100644 executables/wasm/src/host/bindings/graphics/additionnal.rs create mode 100644 executables/wasm/src/host/bindings/graphics/error.rs create mode 100644 executables/wasm/src/host/bindings/graphics/mod.rs create mode 100644 executables/wasm/src/host/bindings/graphics/translate.rs create mode 100644 executables/wasm/src/host/bindings/mod.rs delete mode 100644 modules/bindings/host/Cargo.toml delete mode 100644 modules/bindings/host/build/generator.rs delete mode 100644 modules/bindings/host/build/main.rs delete mode 100644 modules/bindings/host/src/graphics.rs delete mode 100644 modules/bindings/host/src/lib.rs delete mode 100644 modules/bindings/host/tests/graphics.rs delete mode 100644 modules/bindings/host/tests/wasm_test/.cargo/config.toml delete mode 100644 modules/bindings/host/tests/wasm_test/.gitignore delete mode 100644 modules/bindings/host/tests/wasm_test/Cargo.toml delete mode 100644 modules/bindings/host/tests/wasm_test/src/main.rs delete mode 100644 modules/bindings/utilities/Cargo.toml delete mode 100644 modules/bindings/utilities/src/additional.rs delete mode 100644 modules/bindings/wasm/.gitignore delete mode 100644 modules/bindings/wasm/Cargo.toml delete mode 100644 modules/bindings/wasm/build/main.rs delete mode 100644 modules/bindings/wasm/src/bindings.rs delete mode 100644 modules/bindings/wasm/src/lib.rs diff --git a/modules/bindings/wasm/build/generator.rs b/executables/wasm/build/guest.rs similarity index 66% rename from modules/bindings/wasm/build/generator.rs rename to executables/wasm/build/guest.rs index 5ad89573..e3d8e9fb 100644 --- a/modules/bindings/wasm/build/generator.rs +++ b/executables/wasm/build/guest.rs @@ -1,15 +1,18 @@ use std::ops::Deref; -use std::path::Path; - -use bindings_utilities::enumeration; -use bindings_utilities::file::write_token_stream_to_file; -use bindings_utilities::format::snake_to_upper_camel_case; - -use bindings_utilities::context::LvglContext; -use bindings_utilities::function::{get_function_identifier, is_public_input}; +use std::path::{Path, PathBuf}; +use std::{env, fs}; + +use crate::utilities::context::LvglContext; +use crate::utilities::file::write_token_stream_to_file; +use crate::utilities::format::{format_rust, snake_to_upper_camel_case}; +use crate::utilities::function::{get_function_identifier, is_public_input}; +use crate::utilities::{self, enumeration}; +use cbindgen::{EnumConfig, ExportConfig, FunctionConfig, RenameRule}; use proc_macro2::TokenStream; use quote::{ToTokens, format_ident, quote}; +use syn::visit::Visit; use syn::{FnArg, Ident, ReturnType, Signature, Type}; +use target::Architecture; fn convert_type(mut ty: Type) -> Type { match &mut ty { @@ -21,6 +24,7 @@ fn convert_type(mut ty: Type) -> Type { "lv_obj_flag_t" => format_ident!("ObjectFlag"), "lv_obj_point_transform_flag_t" => format_ident!("ObjectPointTransformFlag"), "lv_result_t" => format_ident!("LvglResult"), + "WasmPointer" => format_ident!("c_void"), identifier => { let ident = if identifier.starts_with("lv_") { let ident = identifier.strip_prefix("lv_").unwrap_or(identifier); @@ -126,7 +130,7 @@ fn generate_xila_graphics_call(signature: &Signature) -> TokenStream { quote! { xila_graphics_call( - crate::FunctionCall::#enumeration_variant, + FunctionCall::#enumeration_variant, #( #passed_arguments ),*, #argument_count, #passed_result @@ -226,7 +230,7 @@ fn generate_function(signature: &Signature) -> TokenStream { } } -pub fn generate_enumeration( +fn generate_enumeration( path: impl AsRef, lvgl_functions: &LvglContext, ) -> Result<(), String> { @@ -237,10 +241,7 @@ pub fn generate_enumeration( Ok(()) } -pub fn generate_functions( - path: impl AsRef, - lvgl_functions: &LvglContext, -) -> Result<(), String> { +fn generate_functions(path: impl AsRef, lvgl_functions: &LvglContext) -> Result<(), String> { let generated_functions = lvgl_functions .get_signatures() .iter() @@ -258,7 +259,7 @@ pub fn generate_functions( Ok(()) } -pub fn generate_c_abi_functions( +fn generate_c_abi_functions( path: impl AsRef, lvgl_functions: &LvglContext, ) -> Result<(), String> { @@ -278,3 +279,89 @@ pub fn generate_c_abi_functions( Ok(()) } + +fn is_c_bindings_enabled() -> bool { + env::var_os("CARGO_FEATURE_C_BINDINGS").is_some() +} + +fn generate_c_functions_module_body(path: impl AsRef) -> Result<(), String> { + let token_stream = quote! { + include!(concat!(env!("OUT_DIR"), "/c_functions.generated.rs")); + }; + + fs::write(&path, token_stream.to_string()) + .map_err(|e| format!("Error writing to file: {}", e))?; + + format_rust(path)?; + + Ok(()) +} + +pub fn generate(output_path: &Path) { + // Build only for WASM32 architecture. + if Architecture::get() != Architecture::WASM32 { + return; + } + + let input = lvgl_rust_sys::_bindgen_raw_src(); + let parsed_input = syn::parse_file(input).expect("Error parsing input file"); + + let mut context = LvglContext::default(); + context.set_function_filtering(Some(LvglContext::filter_function)); + context.visit_file(&parsed_input); + context.set_function_filtering(None); + context.visit_file(&syn::parse2(utilities::additional::get()).unwrap()); + + let crate_directory = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + + let enumerations_generated_path = output_path.join("enumeration.generated.rs"); + let functions_generated_path = output_path.join("functions.generated.rs"); + let c_functions_generated_path = output_path.join("c_functions.generated.rs"); + let c_functions_module_path = crate_directory.join("src").join("c_functions.rs"); + let c_header_path = output_path.join("xila_graphics.h"); + + generate_enumeration(&enumerations_generated_path, &context).unwrap(); + + generate_functions(&functions_generated_path, &context).unwrap(); + + if is_c_bindings_enabled() { + // Overwrite c_functions.rs file with generated C ABI functions + // This is workaround for cbindgen macro expansion limitations + generate_c_abi_functions(&c_functions_module_path, &context).unwrap(); + + generate_c_abi_functions(&c_functions_generated_path, &context).unwrap(); + + let configuration: cbindgen::Config = cbindgen::Config { + language: cbindgen::Language::C, + include_guard: Some("__XILA_GRAPHICS_GENERATED_H_INCLUDED".to_string()), + sys_includes: vec![ + "stdarg.h".to_string(), + "stdbool.h".to_string(), + "stdint.h".to_string(), + ], + export: ExportConfig { + prefix: Some("XilaGraphics".to_string()), + ..Default::default() + }, + function: FunctionConfig { + ..Default::default() + }, + no_includes: true, + enumeration: EnumConfig { + rename_variants: RenameRule::QualifiedScreamingSnakeCase, + ..Default::default() + }, + ..Default::default() + }; + + cbindgen::Builder::new() + .with_crate(crate_directory) + .with_config(configuration) + .generate() + .expect("Unable to generate bindings") + .write_to_file(&c_header_path); + + // Restore c_functions.rs file + generate_c_functions_module_body(&c_functions_module_path).unwrap(); + } +} diff --git a/executables/wasm/build/host.rs b/executables/wasm/build/host.rs new file mode 100644 index 00000000..f8e0e37d --- /dev/null +++ b/executables/wasm/build/host.rs @@ -0,0 +1,210 @@ +use crate::utilities::{ + self, context::LvglContext, enumeration, file::write_token_stream_to_file, + function::split_inputs, +}; +use std::path::Path; + +use proc_macro2::{Literal, TokenStream}; +use quote::{ToTokens, format_ident, quote}; +use syn::{FnArg, ItemFn, ReturnType, Signature, visit::Visit}; + +fn generate_conversion_for_output(r#return: &ReturnType) -> Result, String> { + match r#return { + ReturnType::Type(_, _) => { + let conversion = quote! { + let __current_result = TranslateInto::translate_into(__current_result, __translator)?; + }; + + Ok(Some(quote! { + #conversion + + *__result = __current_result; + })) + } + // If the return type is not specified, we don't need to convert it + ReturnType::Default => Ok(None), + } +} + +fn generate_conversion_for_argument(index: usize, argument: &FnArg) -> Result { + let argument_identifier = format_ident!("__argument_{}", index); + + let identifier = match argument { + FnArg::Typed(pattern) => &*pattern.pat, + _ => return Err("Unsupported argument type".to_string()), + }; + + Ok(quote! { + let #identifier = TranslateFrom::translate_from(#argument_identifier, __translator)?; + }) +} + +fn generate_call_argument(argument: &FnArg) -> Result { + match argument { + FnArg::Typed(pattern) => { + let identifier = &*pattern.pat; + Ok(quote! { + #identifier + }) + } + _ => Err("Unsupported argument type".to_string()), + } +} + +fn generate_function_call(function: &Signature) -> Result { + // - Get the inputs + let inputs = function.inputs.iter().collect::>(); + + let (left_inputs, right_inputs) = split_inputs(&inputs)?; + + // - Generate the assignation of the arguments to variables (let name = __Argument_X;) + let assigns = right_inputs + .iter() + .enumerate() + .map(|(i, argument)| generate_conversion_for_argument(i, argument)) + .collect::, _>>()?; + + // - Generate the order of the arguments in the function call (name, name, ...) + let call_arguments = left_inputs + .iter() + .chain(right_inputs.iter()) + .map(|argument| generate_call_argument(argument)) + .collect::, _>>()?; + + // - Get the number of arguments + let arguments_count = Literal::usize_unsuffixed(right_inputs.len()); + + // - Get the function identifier + let function_identifier = &function.ident; + let variant_identifier = enumeration::get_variant_identifier(&function.ident); + + // - Generate the return conversion if needed (let __result = __current_result;) + let r#return = generate_conversion_for_output(&function.output)?; + + // - Generate the code for the function call (let __current_result = Function_identifier(arguments);) + let function_call = if let Some(r#return) = &r#return { + quote! { + let __current_result = #function_identifier(#( + #call_arguments, + )*); + + let __result = __translator.translate_to_host(__result, true).ok_or(Error::InvalidPointer)?; + + #r#return + } + } else { + quote! { + #function_identifier(#( + #call_arguments, + )*); + + } + }; + + Ok(quote! { + FunctionCall::#variant_identifier => { + // Check arguments count + if __arguments_count != #arguments_count { + return Err(Error::InvalidArgumentsCount); + } + // Assign arguments + #( + #assigns + )* + + // Call function + #function_call + } + }) +} + +pub fn generate_code( + signatures: Vec, + definitions: Vec, +) -> Result { + let functions_call = signatures + .iter() + .map(generate_function_call) + .collect::, _>>()?; + + Ok(quote! { + + #( #definitions )* + + #[allow(unused_variables)] + #[allow(clippy::too_many_arguments)] + pub unsafe fn call_function( + __translator: &mut Translator, + __function: FunctionCall, + __argument_0: WasmUsize, + __argument_1: WasmUsize, + __argument_2: WasmUsize, + __argument_3: WasmUsize, + __argument_4: WasmUsize, + __argument_5: WasmUsize, + __argument_6: WasmUsize, + __arguments_count: u8, + __result: WasmPointer, + ) -> Result<()> + { + use xila::graphics::lvgl::*; + use crate::host::bindings::graphics::{ + translate::{TranslateFrom, TranslateInto}, + error::{Error}, + additionnal::*, + }; + + unsafe { + let result = match __function { + #( + #functions_call + )* + + }; + } + + Ok(()) + } + } + .to_token_stream()) +} + +pub fn generate_inner(output_path: &Path, context: &LvglContext) -> Result<(), String> { + let output_file_path = output_path.join("bindings.rs"); + + let enumerations = enumeration::generate_code(context.get_signatures()); + + let functions = generate_code(context.get_signatures(), context.get_definitions())?; + + let token_stream = quote! { + use crate::host::{ + virtual_machine::{ + Environment, WasmPointer, WasmUsize, Translator + } + }; + use crate::host::bindings::graphics::error::Result; + + #enumerations + + #functions + }; + + write_token_stream_to_file(output_file_path, token_stream)?; + + Ok(()) +} + +pub fn generate(out_path: &Path) -> Result<(), ()> { + let input = lvgl_rust_sys::_bindgen_raw_src(); + let parsed_input = syn::parse_file(input).expect("Error parsing input file"); + + let mut context = LvglContext::default(); + context.set_function_filtering(Some(LvglContext::filter_function)); + context.visit_file(&parsed_input); + context.set_function_filtering(None); + context.visit_file(&syn::parse2(utilities::additional::get()).unwrap()); + + generate_inner(out_path, &context).expect("Error generating native bindings"); + + Ok(()) +} diff --git a/executables/wasm/build/main.rs b/executables/wasm/build/main.rs new file mode 100644 index 00000000..261d9acf --- /dev/null +++ b/executables/wasm/build/main.rs @@ -0,0 +1,18 @@ +use std::{env, path::Path}; + +mod guest; +mod host; +mod utilities; + +pub fn main() { + let out_path = env::var("OUT_DIR").unwrap(); + let out_path = Path::new(&out_path); + + if env::var("CARGO_FEATURE_GUEST").is_ok() { + guest::generate(out_path); + } + + if env::var("CARGO_FEATURE_HOST").is_ok() { + host::generate(out_path).expect("Error generating host bindings"); + } +} diff --git a/executables/wasm/build/utilities/additional.rs b/executables/wasm/build/utilities/additional.rs new file mode 100644 index 00000000..cf2fdb1d --- /dev/null +++ b/executables/wasm/build/utilities/additional.rs @@ -0,0 +1,33 @@ +use proc_macro2::TokenStream; +use quote::quote; + +pub fn get() -> TokenStream { + quote! { + extern "C" { + pub fn object_delete(__translator : &mut Translator, object: u16); + + pub fn window_create() -> *mut lv_obj_t; + + pub fn window_pop_event( + __translator: &mut Translator, + window: *mut lv_obj_t, + code: *mut u32, + target: *mut WasmPointer + ); + + pub fn window_get_event_code(window: *mut lv_obj_t) -> u32; + + pub fn window_get_event_target(__translator: &mut Translator, window: *mut lv_obj_t) -> u16; + + pub fn window_next_event(window: *mut lv_obj_t); + + pub fn window_set_icon( + window: *mut lv_obj_t, + icon_string: *const core::ffi::c_char, + icon_color: lv_color_t + ); + + pub fn percentage(value: i32) -> i32; + } + } +} diff --git a/modules/bindings/utilities/src/context.rs b/executables/wasm/build/utilities/context.rs similarity index 90% rename from modules/bindings/utilities/src/context.rs rename to executables/wasm/build/utilities/context.rs index 162c0901..53d69828 100644 --- a/modules/bindings/utilities/src/context.rs +++ b/executables/wasm/build/utilities/context.rs @@ -27,22 +27,6 @@ impl LvglContext { self.definitions.clone() } - pub fn get_type_tree(&self) -> &TypeTree { - &self.type_tree - } - - pub fn get_types(&self) -> &Vec { - &self.types - } - - pub fn get_structures(&self) -> &Vec { - &self.structures - } - - pub fn get_unions(&self) -> &Vec { - &self.unions - } - fn contains_excluded_type(signature: &Signature) -> bool { signature.inputs.iter().any(|input| match input { syn::FnArg::Typed(pat_type) => match &*pat_type.ty { @@ -89,14 +73,17 @@ impl LvglContext { pub fn filter_function(signature: &Signature) -> bool { let unauthorized_functions = [ - "lv_obj_get_display", "lv_obj_delete", + "lv_obj_get_display", "lv_obj_delete_delayed", "lv_obj_delete_async", - "lv_buttonmatrix_set_map", + "lv_obj_null_on_delete", + "lv_buttonmatrix_get_map", ]; - if unauthorized_functions.contains(&signature.ident.to_string().as_str()) { + let signature_ident_str = signature.ident.to_string(); + + if unauthorized_functions.contains(&signature_ident_str.as_str()) { return false; } @@ -135,7 +122,7 @@ impl LvglContext { if !authorized_prefixes .iter() - .any(|&prefix| signature.ident.to_string().starts_with(prefix)) + .any(|&prefix| signature_ident_str.starts_with(prefix)) { return false; } diff --git a/modules/bindings/utilities/src/enumeration.rs b/executables/wasm/build/utilities/enumeration.rs similarity index 70% rename from modules/bindings/utilities/src/enumeration.rs rename to executables/wasm/build/utilities/enumeration.rs index 92bef95e..f7787a0c 100644 --- a/modules/bindings/utilities/src/enumeration.rs +++ b/executables/wasm/build/utilities/enumeration.rs @@ -2,7 +2,7 @@ use proc_macro2::{Literal, TokenStream}; use quote::{ToTokens, quote}; use syn::{Ident, Signature}; -use crate::{format::snake_ident_to_upper_camel, function::get_function_identifier}; +use crate::utilities::{format::snake_ident_to_upper_camel, function::get_function_identifier}; pub fn get_variant_identifier(identifier: &Ident) -> Ident { let identifier = get_function_identifier("", identifier); @@ -16,16 +16,18 @@ pub fn generate_code(signatures: Vec) -> TokenStream { let variants = &signatures .into_iter() - .map(|signature| get_variant_identifier(&signature.ident)) .enumerate() - .map(|(i, identifier)| { - let i = Literal::usize_unsuffixed(i); - quote! { #identifier = #i } + .map(|(i, signature)| { + let identifier = get_variant_identifier(&signature.ident); + let value = Literal::usize_unsuffixed(i); + + quote! { #identifier = #value } }) .collect::>(); quote! { #[derive(Debug, Copy, Clone, PartialEq, Eq)] + #[allow(dead_code)] #[repr(u16)] pub enum FunctionCall { #( diff --git a/modules/bindings/utilities/src/file.rs b/executables/wasm/build/utilities/file.rs similarity index 92% rename from modules/bindings/utilities/src/file.rs rename to executables/wasm/build/utilities/file.rs index 3c525084..8a69cafa 100644 --- a/modules/bindings/utilities/src/file.rs +++ b/executables/wasm/build/utilities/file.rs @@ -3,7 +3,7 @@ use std::{fs, path::Path}; use proc_macro2::TokenStream; use quote::quote; -use crate::format::format_rust; +use crate::utilities::format::format_rust; pub fn write_token_stream_to_file( path: impl AsRef, diff --git a/modules/bindings/utilities/src/format.rs b/executables/wasm/build/utilities/format.rs similarity index 74% rename from modules/bindings/utilities/src/format.rs rename to executables/wasm/build/utilities/format.rs index 5bcd0ef6..d55ea5fb 100644 --- a/modules/bindings/utilities/src/format.rs +++ b/executables/wasm/build/utilities/format.rs @@ -15,20 +15,6 @@ pub fn format_rust(file_path: impl AsRef) -> Result<(), String> { Ok(()) } -pub fn format_c(file_path: &Path) -> Result<(), String> { - Command::new("clang-format") - .arg("-i") - .arg( - file_path - .to_str() - .ok_or("Error converting path to string")?, - ) - .status() - .map_err(|code| format!("Error running clang-format : {code}"))?; - - Ok(()) -} - pub fn snake_to_upper_camel_case(input: &str) -> String { input .split('_') diff --git a/modules/bindings/utilities/src/function.rs b/executables/wasm/build/utilities/function.rs similarity index 100% rename from modules/bindings/utilities/src/function.rs rename to executables/wasm/build/utilities/function.rs diff --git a/modules/bindings/utilities/src/lib.rs b/executables/wasm/build/utilities/mod.rs similarity index 100% rename from modules/bindings/utilities/src/lib.rs rename to executables/wasm/build/utilities/mod.rs diff --git a/modules/bindings/utilities/src/type_tree.rs b/executables/wasm/build/utilities/type_tree.rs similarity index 55% rename from modules/bindings/utilities/src/type_tree.rs rename to executables/wasm/build/utilities/type_tree.rs index 3b39edac..fc563928 100644 --- a/modules/bindings/utilities/src/type_tree.rs +++ b/executables/wasm/build/utilities/type_tree.rs @@ -1,8 +1,5 @@ use std::collections::HashMap; -use quote::ToTokens; -use syn::Path; - #[derive(Debug)] pub struct TypeTree { type_tree: HashMap, @@ -20,14 +17,4 @@ impl TypeTree { pub fn insert(&mut self, r#type: String, alias: String) { self.type_tree.insert(r#type, alias); } - - pub fn resolve(&self, path: &Path) -> String { - let path_string = path.to_token_stream().to_string(); - - if let Some(alias) = self.type_tree.get(&path_string) { - alias.clone() - } else { - path_string - } - } } diff --git a/executables/wasm/src/guest/bindings.rs b/executables/wasm/src/guest/bindings.rs new file mode 100644 index 00000000..44ae78fc --- /dev/null +++ b/executables/wasm/src/guest/bindings.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/modules/bindings/wasm/src/c_functions.rs b/executables/wasm/src/guest/c_functions.rs similarity index 100% rename from modules/bindings/wasm/src/c_functions.rs rename to executables/wasm/src/guest/c_functions.rs diff --git a/modules/bindings/wasm/src/enumeration.rs b/executables/wasm/src/guest/enumeration.rs similarity index 100% rename from modules/bindings/wasm/src/enumeration.rs rename to executables/wasm/src/guest/enumeration.rs diff --git a/modules/bindings/wasm/src/functions.rs b/executables/wasm/src/guest/functions.rs similarity index 100% rename from modules/bindings/wasm/src/functions.rs rename to executables/wasm/src/guest/functions.rs diff --git a/executables/wasm/src/guest/mod.rs b/executables/wasm/src/guest/mod.rs new file mode 100644 index 00000000..938621cb --- /dev/null +++ b/executables/wasm/src/guest/mod.rs @@ -0,0 +1,7 @@ +mod enumeration; +mod functions; + +pub use enumeration::*; +pub use functions::*; +pub use prelude::*; +pub mod prelude; diff --git a/modules/bindings/wasm/src/prelude.rs b/executables/wasm/src/guest/prelude.rs similarity index 99% rename from modules/bindings/wasm/src/prelude.rs rename to executables/wasm/src/guest/prelude.rs index a4a935a0..7f1d597f 100644 --- a/modules/bindings/wasm/src/prelude.rs +++ b/executables/wasm/src/guest/prelude.rs @@ -1,4 +1,5 @@ -use crate::FunctionCall; +pub use crate::FunctionCall; +pub use core::ffi::c_void; #[link(wasm_import_module = "host")] unsafe extern "C" { diff --git a/executables/wasm/src/host/bindings/graphics/additionnal.rs b/executables/wasm/src/host/bindings/graphics/additionnal.rs new file mode 100644 index 00000000..25e3ab80 --- /dev/null +++ b/executables/wasm/src/host/bindings/graphics/additionnal.rs @@ -0,0 +1,102 @@ +use core::ffi::CStr; + +use xila::{ + graphics::{self, Color, Window, lvgl}, + log, + task::{self}, +}; + +use crate::host::virtual_machine::{Translator, WasmPointer}; + +pub unsafe fn object_delete(__translator: &mut Translator, object: WasmPointer) { + let object = __translator.remove_host_translation(object).unwrap(); + unsafe { + lvgl::lv_obj_delete(object as *mut _); + } +} + +pub unsafe fn window_create() -> *mut lvgl::lv_obj_t { + task::block_on(graphics::get_instance().create_window()) + .unwrap() + .into_raw() +} + +pub unsafe fn window_pop_event( + __translator: &mut Translator, + window: *mut lvgl::lv_obj_t, + code: *mut u32, + target: *mut WasmPointer, +) { + let mut window = unsafe { graphics::Window::from_raw(window) }; + + if let Some(event) = window.pop_event() { + unsafe { + *code = event.code as u32; + + *target = __translator + .translate_to_guest(event.target, false) + .unwrap_or(0); + } + } + + core::mem::forget(window); +} + +pub unsafe fn window_get_event_code(window: *mut lvgl::lv_obj_t) -> u32 { + let window = unsafe { graphics::Window::from_raw(window) }; + + let code = if let Some(event) = window.peek_event() { + event.code as u32 + } else { + graphics::EventKind::All as u32 + }; + + core::mem::forget(window); + + code +} + +pub unsafe fn window_get_event_target( + __translator: &mut Translator, + window: *mut lvgl::lv_obj_t, +) -> WasmPointer { + let window = unsafe { graphics::Window::from_raw(window) }; + + let target = if let Some(event) = window.peek_event() { + event.target + } else { + log::warning!("No event available for the window"); + core::ptr::null_mut() + }; + + core::mem::forget(window); + + unsafe { __translator.translate_to_guest(target, false).unwrap() } +} + +pub unsafe fn window_next_event(window: *mut lvgl::lv_obj_t) { + let mut window = unsafe { Window::from_raw(window) }; + + window.pop_event(); + + core::mem::forget(window); +} + +pub unsafe fn window_set_icon( + window: *mut lvgl::lv_obj_t, + icon_string: *const core::ffi::c_char, + icon_color: lvgl::lv_color_t, +) { + let mut window = unsafe { Window::from_raw(window) }; + + let icon_string = unsafe { CStr::from_ptr(icon_string).to_str().unwrap() }; + + let icon_color = Color::from_lvgl_color(icon_color); + window.set_icon(icon_string, icon_color); + + core::mem::forget(window); +} + +pub unsafe fn percentage(value: i32) -> i32 { + unsafe { lvgl::lv_pct(value) } +} diff --git a/executables/wasm/src/host/bindings/graphics/error.rs b/executables/wasm/src/host/bindings/graphics/error.rs new file mode 100644 index 00000000..37aec4c9 --- /dev/null +++ b/executables/wasm/src/host/bindings/graphics/error.rs @@ -0,0 +1,8 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Error { + InvalidArgumentsCount, + InvalidPointer, + EnvironmentRetrievalFailed, +} + +pub type Result = core::result::Result; diff --git a/executables/wasm/src/host/bindings/graphics/mod.rs b/executables/wasm/src/host/bindings/graphics/mod.rs new file mode 100644 index 00000000..f3855e62 --- /dev/null +++ b/executables/wasm/src/host/bindings/graphics/mod.rs @@ -0,0 +1,119 @@ +mod additionnal; +mod error; +mod translate; + +use crate::host::virtual_machine::{ + Environment, EnvironmentPointer, FunctionDescriptor, Registrable, Translator, WasmPointer, + WasmUsize, +}; +use xila::{graphics, log, task::block_on}; + +pub use error::{Error, Result}; + +mod generated_bindings { + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +} + +pub struct GraphicsBindings; + +impl Registrable for GraphicsBindings { + fn get_functions(&self) -> &[FunctionDescriptor] { + &GRAPHICS_BINDINGS_FUNCTIONS + } +} + +/// Call to graphics API +/// +/// # Safety +/// +/// This function is unsafe because it may dereference raw pointers (e.g. `Environment`, `Result` or `Arguments`). +/// The pointer must be valid and properly aligned (ensured by the virtual machine). +#[allow(clippy::too_many_arguments)] +unsafe fn call_inner( + environment: EnvironmentPointer, + function: generated_bindings::FunctionCall, + argument_0: WasmUsize, + argument_1: WasmUsize, + argument_2: WasmUsize, + argument_3: WasmUsize, + argument_4: WasmUsize, + argument_5: WasmUsize, + argument_6: WasmUsize, + arguments_count: u8, + result_pointer: WasmPointer, +) -> Result<()> { + unsafe { + let instance = graphics::get_instance(); + + let _lock = block_on(instance.lock()); + + let environment = Environment::from_raw_pointer(environment); + + let mut translation_map = Translator::from_environment(environment) + .map_err(|_| Error::EnvironmentRetrievalFailed)?; + + generated_bindings::call_function( + &mut translation_map, + function, + argument_0, + argument_1, + argument_2, + argument_3, + argument_4, + argument_5, + argument_6, + arguments_count, + result_pointer, + )?; + + Ok(()) + + // Lock is automatically released here. + } +} + +#[allow(clippy::too_many_arguments)] +pub unsafe fn call( + environment: EnvironmentPointer, + function: generated_bindings::FunctionCall, + argument_0: WasmUsize, + argument_1: WasmUsize, + argument_2: WasmUsize, + argument_3: WasmUsize, + argument_4: WasmUsize, + argument_5: WasmUsize, + argument_6: WasmUsize, + arguments_count: u8, + result_pointer: WasmPointer, +) -> i32 { + let result = unsafe { + call_inner( + environment, + function, + argument_0, + argument_1, + argument_2, + argument_3, + argument_4, + argument_5, + argument_6, + arguments_count, + result_pointer, + ) + }; + + match result { + Ok(_) => 0, + Err(error) => { + log::error!( + "Error {error:?} durring graphics call: {function:?} with arguments: {argument_0:x}, {argument_1:x}, {argument_2:x}, {argument_3:x}, {argument_4:x}, {argument_5:x}, {argument_6:x}, count: {arguments_count}, result pointer: {result_pointer:x}" + ); + error as i32 + } + } +} + +const GRAPHICS_BINDINGS_FUNCTIONS: [FunctionDescriptor; 1] = [FunctionDescriptor { + name: "xila_graphics_call", + pointer: call as *mut _, +}]; diff --git a/executables/wasm/src/host/bindings/graphics/translate.rs b/executables/wasm/src/host/bindings/graphics/translate.rs new file mode 100644 index 00000000..334da640 --- /dev/null +++ b/executables/wasm/src/host/bindings/graphics/translate.rs @@ -0,0 +1,405 @@ +use core::ffi::{c_char, c_void}; +use core::mem::transmute; + +use crate::host::bindings::graphics::{Error, Result}; +use crate::host::virtual_machine::{Translator, WasmPointer, WasmUsize}; +use alloc::vec::Vec; +use xila::graphics::lvgl::{self, lv_style_value_t}; +use xila::log; + +pub trait TranslateFrom { + unsafe fn translate_from(wasm_usize: WasmUsize, translator: &mut Translator) -> Result + where + Self: Sized; +} + +pub trait TranslateInto: Sized { + type Output; + + unsafe fn translate_into(self, translator: &mut Translator) -> Result; +} + +macro_rules! implicit_usize_cast { + ($($t:ty),* $(,)?) => { + $( + impl TranslateFrom for $t { + #[inline] + unsafe fn translate_from(wasm_usize: WasmUsize, _: &mut Translator) -> Result { + Ok(wasm_usize as $t) + } + } + + impl TranslateInto for $t { + type Output = Self; + #[inline] + unsafe fn translate_into(self, _: &mut Translator) -> Result { + Ok(self as $t) + } + } + )* + }; +} + +macro_rules! implicit_pointer_translation { + ($($t:ty),* $(,)?) => { + $( + impl TranslateFrom for *mut $t { + #[inline] + unsafe fn translate_from(wasm_usize: WasmUsize, translator: &mut Translator) -> Result { + let ptr = unsafe { translator.translate_to_host(wasm_usize as WasmPointer, true) }; + match ptr { + Some(p) => Ok(p as *mut $t), + None => Err(Error::InvalidPointer), + } + } + } + + impl TranslateFrom for *const $t { + #[inline] + unsafe fn translate_from(wasm_usize: WasmUsize, translator: &mut Translator) -> Result { + let ptr = unsafe { translator.translate_to_host(wasm_usize as WasmPointer, true) }; + match ptr { + Some(p) => Ok(p as *const $t), + None => Err(Error::InvalidPointer), + } + } + } + + impl TranslateInto for *mut $t { + type Output = WasmUsize; + + #[inline] + unsafe fn translate_into(self, translator: &mut Translator) -> Result { + let ptr = unsafe { translator.translate_to_guest(self, true) }; + match ptr { + Some(p) => Ok(p as WasmUsize), + None => Err(Error::InvalidPointer), + } + } + } + + impl TranslateInto for *const $t { + type Output = WasmUsize; + + #[inline] + unsafe fn translate_into(self, translator: &mut Translator) -> Result { + let ptr = unsafe { translator.translate_to_guest(self as *mut $t, true) }; + match ptr { + Some(p) => Ok(p as WasmUsize), + None => Err(Error::InvalidPointer), + } + } + } + )* + }; +} + +implicit_pointer_translation!( + lvgl::lv_point_t, + lvgl::lv_point_precise_t, + lvgl::lv_style_t, + lvgl::lv_anim_t, + lvgl::lv_obj_class_t, + lvgl::lv_area_t, + lvgl::lv_style_value_t, + lvgl::lv_color16_t, + lvgl::lv_color32_t, + lvgl::lv_matrix_t, + lvgl::lv_chart_series_t, + lvgl::lv_calendar_date_t, + core::ffi::c_void, + i8, + i16, + i32, + i64, + isize, + u8, + u16, + u32, + u64, + usize, + f32, + f64, +); + +implicit_usize_cast!(u8, u16, u32, usize, i8, i16, i32, isize, f32); + +#[cfg(target_pointer_width = "64")] +implicit_usize_cast!(u64, i64, f64); + +impl TranslateFrom for bool { + #[inline] + unsafe fn translate_from(wasm_usize: WasmUsize, _: &mut Translator) -> Result { + Ok(wasm_usize != 0) + } +} + +impl TranslateInto for bool { + type Output = Self; + #[inline] + unsafe fn translate_into(self, _: &mut Translator) -> Result { + Ok(self) + } +} + +impl TranslateFrom for () { + #[inline] + unsafe fn translate_from(_: WasmUsize, _: &mut Translator) -> Result { + Ok(()) + } +} + +impl TranslateInto for () { + type Output = Self; + + #[inline] + unsafe fn translate_into(self, _: &mut Translator) -> Result { + Ok(()) + } +} + +impl TranslateFrom for *mut lvgl::lv_obj_t { + #[inline] + unsafe fn translate_from(wasm_usize: WasmUsize, translator: &mut Translator) -> Result { + let ptr = unsafe { translator.translate_to_host(wasm_usize as WasmPointer, false) }; + match ptr { + Some(p) => Ok(p as *mut lvgl::lv_obj_t), + None => Err(Error::InvalidPointer), + } + } +} + +impl TranslateFrom for *const lvgl::lv_obj_t { + #[inline] + unsafe fn translate_from(wasm_usize: WasmUsize, translator: &mut Translator) -> Result { + let ptr = unsafe { translator.translate_to_host(wasm_usize as WasmPointer, false) }; + match ptr { + Some(p) => Ok(p as *const lvgl::lv_obj_t), + None => Err(Error::InvalidPointer), + } + } +} + +impl TranslateInto for *mut lvgl::lv_obj_t { + type Output = WasmUsize; + + #[inline] + unsafe fn translate_into(self, translator: &mut Translator) -> Result { + Ok(translator.add_host_translation(self)) + } +} + +impl TranslateInto for *mut *mut lvgl::lv_obj_t { + type Output = WasmUsize; + + #[inline] + unsafe fn translate_into(self, translator: &mut Translator) -> Result { + let ptr = unsafe { translator.translate_to_guest(self as *mut lvgl::lv_obj_t, false) }; + match ptr { + Some(p) => Ok(p as WasmUsize), + None => Err(Error::InvalidPointer), + } + } +} + +// Inlined translations + +impl TranslateFrom for lvgl::lv_color_t { + #[inline] + unsafe fn translate_from(wasm_usize: WasmUsize, _: &mut Translator) -> Result { + Ok(lvgl::lv_color_t { + blue: wasm_usize as u8, + green: (wasm_usize >> 8) as u8, + red: (wasm_usize >> 16) as u8, + }) + } +} + +impl TranslateInto for lvgl::lv_color_t { + type Output = WasmUsize; + + #[inline] + unsafe fn translate_into(self, _: &mut Translator) -> Result { + Ok((self.red as WasmUsize) << 16 + | (self.green as WasmUsize) << 8 + | (self.blue as WasmUsize)) + } +} + +impl TranslateFrom for lvgl::lv_color32_t { + #[inline] + unsafe fn translate_from(wasm_usize: WasmUsize, _: &mut Translator) -> Result { + Ok(lvgl::lv_color32_t { + blue: wasm_usize as u8, + green: (wasm_usize >> 8) as u8, + red: (wasm_usize >> 16) as u8, + alpha: (wasm_usize >> 24) as u8, + }) + } +} + +impl TranslateInto for lvgl::lv_color32_t { + type Output = WasmUsize; + + #[inline] + unsafe fn translate_into(self, _: &mut Translator) -> Result { + Ok((self.alpha as WasmUsize) << 24 + | (self.red as WasmUsize) << 16 + | (self.green as WasmUsize) << 8 + | (self.blue as WasmUsize)) + } +} + +impl TranslateFrom for lvgl::lv_color_hsv_t { + #[inline] + unsafe fn translate_from(wasm_usize: WasmUsize, _: &mut Translator) -> Result { + Ok(lvgl::lv_color_hsv_t { + h: wasm_usize as u16, + s: (wasm_usize >> 16) as u8, + v: (wasm_usize >> 24) as u8, + }) + } +} + +impl TranslateInto for lvgl::lv_color_hsv_t { + type Output = WasmUsize; + + #[inline] + unsafe fn translate_into(self, _: &mut Translator) -> Result { + Ok((self.h as WasmUsize) | (self.s as WasmUsize) << 16 | (self.v as WasmUsize) << 24) + } +} + +impl TranslateFrom for lvgl::lv_color16_t { + #[inline] + unsafe fn translate_from(wasm_usize: WasmUsize, _: &mut Translator) -> Result { + let value = unsafe { transmute::(wasm_usize as u16) }; + Ok(value) + } +} + +impl TranslateInto for lvgl::lv_color16_t { + type Output = WasmUsize; + + #[inline] + unsafe fn translate_into(self, _: &mut Translator) -> Result { + Ok(unsafe { transmute::(self) as WasmUsize }) + } +} + +impl TranslateFrom for lvgl::lv_style_value_t { + #[inline] + unsafe fn translate_from(wasm_usize: WasmUsize, _: &mut Translator) -> Result { + let value = wasm_usize as *mut lv_style_value_t; + unsafe { Ok(*value) } + } +} + +const POINT_Y_OFFSET: WasmUsize = size_of::() as WasmUsize * 8 / 2; +const POINT_MASK: WasmUsize = (1 << POINT_Y_OFFSET) - 1; + +impl TranslateFrom for lvgl::lv_point_t { + #[inline] + unsafe fn translate_from(wasm_usize: WasmUsize, _: &mut Translator) -> Result { + let x = (wasm_usize & POINT_MASK) as i32; + let y = (wasm_usize >> POINT_Y_OFFSET) as i32; + Ok(lvgl::lv_point_t { x, y }) + } +} + +impl TranslateInto for lvgl::lv_point_t { + type Output = WasmUsize; + + #[inline] + unsafe fn translate_into(self, _: &mut Translator) -> Result { + Ok((self.y as WasmUsize) << POINT_Y_OFFSET | (self.x as WasmUsize)) + } +} + +impl TranslateFrom for lvgl::lv_point_precise_t { + #[inline] + unsafe fn translate_from(wasm_usize: WasmUsize, _: &mut Translator) -> Result { + let x = (wasm_usize & POINT_MASK) as i32; + let y = (wasm_usize >> POINT_Y_OFFSET) as i32; + Ok(lvgl::lv_point_precise_t { x, y }) + } +} + +impl TranslateInto for lvgl::lv_point_precise_t { + type Output = WasmUsize; + + #[inline] + unsafe fn translate_into(self, _: &mut Translator) -> Result { + Ok((self.y as WasmUsize) << POINT_Y_OFFSET | (self.x as WasmUsize)) + } +} + +impl TranslateInto for lvgl::lv_style_value_t { + type Output = WasmUsize; + + #[inline] + unsafe fn translate_into(self, _: &mut Translator) -> Result { + Ok(0) + } +} + +// Nested pointer array translation (*const *const char -> need allocation + translation of inner pointers) +impl TranslateFrom for *const *const c_void { + #[inline] + unsafe fn translate_from(wasm_usize: WasmUsize, translator: &mut Translator) -> Result { + unsafe { + log::information!("Translating array of pointers at guest address {wasm_usize:x}"); + + let array: *mut WasmPointer = translator + .translate_to_host(wasm_usize as WasmPointer, true) + .ok_or(Error::InvalidPointer)?; + + log::information!( + "Translating array of pointers at guest address {wasm_usize:x} (host address {array:p})" + ); + + let count = (0..) + .map(|i| *array.add(i)) + .take_while(|&ptr| ptr != 0) + .count(); + + log::information!( + "Array of pointers at guest address {wasm_usize:x} has {count} elements" + ); + + let vec: Result> = (0..count) + .map(|i| *array.add(i)) + .map(|ptr| { + translator + .translate_to_host(ptr as WasmPointer, true) + .map(|p| p as *const c_void) + .ok_or(Error::InvalidPointer) + }) + .collect(); + + let (pointer, _, _) = vec?.into_raw_parts(); + + Ok(pointer) + } + } +} + +impl TranslateFrom for *const *const c_char { + #[inline] + unsafe fn translate_from(wasm_usize: WasmUsize, translator: &mut Translator) -> Result { + unsafe { + <*const *const c_void>::translate_from(wasm_usize, translator) + .map(|ptr| ptr as *const *const c_char) + } + } +} + +impl TranslateFrom for *mut *const c_char { + #[inline] + unsafe fn translate_from(wasm_usize: WasmUsize, translator: &mut Translator) -> Result { + unsafe { + <*const *const c_void>::translate_from(wasm_usize, translator) + .map(|ptr| ptr as *mut *const c_char) + } + } +} diff --git a/executables/wasm/src/host/bindings/mod.rs b/executables/wasm/src/host/bindings/mod.rs new file mode 100644 index 00000000..175de448 --- /dev/null +++ b/executables/wasm/src/host/bindings/mod.rs @@ -0,0 +1 @@ +pub mod graphics; diff --git a/modules/bindings/host/Cargo.toml b/modules/bindings/host/Cargo.toml deleted file mode 100644 index a60cd900..00000000 --- a/modules/bindings/host/Cargo.toml +++ /dev/null @@ -1,43 +0,0 @@ -[package] -name = "host_bindings" -version = "0.1.0" -edition = "2024" -build = "build/main.rs" - -[dependencies] -file_system = { workspace = true } -virtual_machine = { workspace = true } -shared = { workspace = true } -users = { workspace = true } -task = { workspace = true } -graphics = { workspace = true } -time = { workspace = true } -include_bytes_aligned = "0.1" - -log = { workspace = true } -#wit-bindgen = "0.34.0" - -[dev-dependencies] -drivers_native = { workspace = true } -drivers_core = { workspace = true } -drivers_shared = { workspace = true } -drivers_std = { workspace = true } -executable = { workspace = true, features = ["building"] } -little_fs = { workspace = true } -memory = { workspace = true } -virtual_file_system = { workspace = true } -abi_definitions = { workspace = true } -testing = { workspace = true } - -[build-dependencies] -lvgl_rust_sys = { workspace = true } -target = { workspace = true } -cbindgen = { workspace = true } -bindings_utilities = { workspace = true } -syn = { workspace = true, features = ["full", "visit", "extra-traits"] } -quote = { workspace = true } -proc-macro2 = { workspace = true } - -[[test]] -name = "graphics_bindings_tests" -path = "tests/graphics.rs" diff --git a/modules/bindings/host/build/generator.rs b/modules/bindings/host/build/generator.rs deleted file mode 100644 index ed20b857..00000000 --- a/modules/bindings/host/build/generator.rs +++ /dev/null @@ -1,326 +0,0 @@ -use bindings_utilities::{ - context::LvglContext, enumeration, file::write_token_stream_to_file, function::split_inputs, -}; -use std::path::Path; - -use proc_macro2::{Literal, TokenStream}; -use quote::{ToTokens, format_ident, quote}; -use syn::{FnArg, ItemFn, ReturnType, Signature, TypePath}; - -use bindings_utilities::type_tree::TypeTree; - -fn generate_conversion_for_argument( - type_tree: &TypeTree, - argument: &FnArg, -) -> Result { - match argument { - FnArg::Typed(pattern) => { - let identifier = &*pattern.pat; - - match &*pattern.ty { - syn::Type::Ptr(type_value) => { - let type_string = type_value.elem.to_token_stream().to_string(); - - if type_string == "lv_obj_t" { - Ok(quote! { - - let #identifier = __pointer_table.get_native_pointer( - __task, - #identifier.try_into().map_err(|_| Error::InvalidPointer)? - )?; - - }) - } else { - Ok(quote! { - let #identifier : #type_value = unsafe { convert_to_native_pointer( - &__environment, - #identifier - )? }; - }) - } - } - syn::Type::Path(path) => { - let path_string = type_tree.resolve(&path.path); - - let path_string_stripped = path_string.replace(" ", ""); - - let path_string_identifier: TypePath = syn::parse_str(&path_string).unwrap(); - - if path_string_stripped == "bool" { - Ok(quote! { - let #identifier = #identifier != 0; - }) - } else if path_string_stripped == "lv_color_t" { - Ok(quote! { - let #identifier = lv_color_t { - blue: #identifier as u8, - green: (#identifier >> 8) as u8, - red: (#identifier >> 16) as u8, - }; - }) - } else if path_string_stripped == "lv_color32_t" { - Ok(quote! { - let #identifier = unsafe { core::mem::transmute::(#identifier) }; - }) - } else if path_string_stripped == "lv_color16_t" { - Ok(quote! { - let #identifier = unsafe { core::mem::transmute::(#identifier as u16) }; - }) - } else if path_string_stripped == "lv_style_value_t" { - Ok(quote! { - let #identifier = #identifier as *mut lv_style_value_t; - let #identifier = unsafe { *#identifier }; - }) - } else if path_string_stripped == "u32" { - Ok(quote! {}) - } else { - Ok(quote! { - let #identifier = #identifier as #path_string_identifier; - }) - } - } - t => Err(format!("Unsupported type conversion : {t:?}")), - } - } - _ => Err("Unsupported argument type".to_string()), - } -} - -fn generate_conversion_for_output(r#return: &ReturnType) -> Result, String> { - match r#return { - ReturnType::Type(_, r#type) => { - let conversion = match &**r#type { - syn::Type::Ptr(type_value) => { - let type_string = type_value.elem.to_token_stream().to_string(); - - if type_string == "lv_obj_t" { - quote! { - - let __result_2 : *mut u16 = unsafe { convert_to_native_pointer(&__environment, __result)? }; - - let __result : *mut u16 = unsafe { convert_to_native_pointer(&__environment, __result)? }; - - let __current_result = __pointer_table.insert( - __task, - __current_result as *mut core::ffi::c_void - )?; - - - unsafe { - *__result = __current_result; - } - } - } else if type_string == "core :: ffi :: c_void" { - quote! { - let __current_result = unsafe { __environment.convert_to_wasm_pointer( - __current_result - ) }; - - let __result : *mut WasmPointer = unsafe { convert_to_native_pointer(&__environment, __result)? }; - } - } else { - quote! { - let __current_result = unsafe { __environment.convert_to_wasm_pointer( - __current_result as *mut core::ffi::c_void - ) }; - - let __result : *mut WasmPointer = unsafe { convert_to_native_pointer(&__environment, __result)? }; - } - } - } - syn::Type::Path(r#type) => { - quote! { - let __result : *mut #r#type = unsafe { convert_to_native_pointer(&__environment, __result)? }; - } - } - - t => { - return Err(format!("Unsupported return type : {t:?}")); - } - }; - - Ok(Some(quote! { - #conversion - - unsafe { - *__result = __current_result; - } - })) - } - // If the return type is not specified, we don't need to convert it - ReturnType::Default => Ok(None), - } -} - -fn generate_assign(index: usize, argument: &FnArg) -> Result { - match argument { - FnArg::Typed(pattern) => { - let identifier = format_ident!("__argument_{}", index); - let identifier_real = &*pattern.pat; - Ok(quote! { - let #identifier_real = #identifier; - }) - } - _ => Err("Unsupported argument type".to_string()), - } -} - -fn generate_call_argument(argument: &FnArg) -> Result { - match argument { - FnArg::Typed(pattern) => { - let identifier = &*pattern.pat; - Ok(quote! { - #identifier - }) - } - _ => Err("Unsupported argument type".to_string()), - } -} - -fn generate_function_call( - type_tree: &TypeTree, - function: &Signature, -) -> Result { - // - Get the inputs - let inputs = function.inputs.iter().collect::>(); - - let (left_inputs, right_inputs) = split_inputs(&inputs)?; - - // - Generate the assignation of the arguments to variables (let name = __Argument_X;) - let assigns = right_inputs - .iter() - .enumerate() - .map(|(i, argument)| generate_assign(i, argument)) - .collect::, _>>()?; - - // - Generate the conversion of the arguments to the expected types (let name = name as Type;) - let conversion = right_inputs - .iter() - .map(|argument| generate_conversion_for_argument(type_tree, argument)) - .collect::, _>>()?; - - // - Generate the order of the arguments in the function call (name, name, ...) - let call_arguments = left_inputs - .iter() - .chain(right_inputs.iter()) - .map(|argument| generate_call_argument(argument)) - .collect::, _>>()?; - - // - Get the number of arguments - let arguments_count = Literal::usize_unsuffixed(right_inputs.len()); - - // - Get the function identifier - let function_identifier = &function.ident; - let variant_identifier = enumeration::get_variant_identifier(&function.ident); - - // - Generate the return conversion if needed (let __result = __current_result;) - let r#return = generate_conversion_for_output(&function.output)?; - - // - Generate the code for the function call (let __current_result = Function_identifier(arguments);) - let function_call = if let Some(r#return) = &r#return { - quote! { - let __current_result = unsafe { #function_identifier(#( - #call_arguments, - )*) }; - - #r#return - } - } else { - quote! { - unsafe { - #function_identifier(#( - #call_arguments, - )*); - } - } - }; - - Ok(quote! { - FunctionCall::#variant_identifier => { - // Check arguments count - if __arguments_count != #arguments_count { - return Err(Error::InvalidArgumentsCount); - } - // Assign arguments - #( - #assigns - )* - // Convert arguments - #( - #conversion - )* - - // Call function - #function_call - } - }) -} - -pub fn generate_code( - type_tree: &TypeTree, - signatures: Vec, - definitions: Vec, -) -> Result { - let functions_call = signatures - .iter() - .map(|signature| generate_function_call(type_tree, signature)) - .collect::, _>>()?; - - Ok(quote! { - #( #definitions )* - - #[allow(unused_variables)] - #[allow(clippy::too_many_arguments)] - pub unsafe fn call_function( - __environment: Environment, - __pointer_table: &mut PointerTable, - __function: FunctionCall, - __argument_0: WasmUsize, - __argument_1: WasmUsize, - __argument_2: WasmUsize, - __argument_3: WasmUsize, - __argument_4: WasmUsize, - __argument_5: WasmUsize, - __argument_6: WasmUsize, - __arguments_count: u8, - __result: WasmPointer, - ) -> Result<()> - { - let __custom_data = __environment.get_or_initialize_custom_data().map_err(|_| Error::EnvironmentRetrievalFailed)?; - let __task = __custom_data.get_task_identifier(); - - match __function { - #( - #functions_call - )* - - } - - Ok(()) - - } - } - .to_token_stream()) -} - -pub fn generate(output_path: &Path, context: &LvglContext) -> Result<(), String> { - let output_file_path = output_path.join("bindings.rs"); - - let enumerations = enumeration::generate_code(context.get_signatures()); - - let functions = generate_code( - context.get_type_tree(), - context.get_signatures(), - context.get_definitions(), - )?; - - let token_stream = quote! { - #enumerations - - #functions - }; - - write_token_stream_to_file(output_file_path, token_stream)?; - - Ok(()) -} diff --git a/modules/bindings/host/build/main.rs b/modules/bindings/host/build/main.rs deleted file mode 100644 index 1d915ce8..00000000 --- a/modules/bindings/host/build/main.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::{env, path::Path}; - -use syn::visit::Visit; -mod generator; - -fn main() -> Result<(), ()> { - let input = lvgl_rust_sys::_bindgen_raw_src(); - let parsed_input = syn::parse_file(input).expect("Error parsing input file"); - - let mut context = bindings_utilities::context::LvglContext::default(); - context.set_function_filtering(Some( - bindings_utilities::context::LvglContext::filter_function, - )); - context.visit_file(&parsed_input); - context.set_function_filtering(None); - context.visit_file(&syn::parse2(bindings_utilities::additional::get()).unwrap()); - - let out_directory = env::var("OUT_DIR").unwrap(); - let out_directory = Path::new(out_directory.as_str()); - - generator::generate(out_directory, &context).expect("Error generating native bindings"); - - Ok(()) -} diff --git a/modules/bindings/host/src/graphics.rs b/modules/bindings/host/src/graphics.rs deleted file mode 100644 index 3d3f7ef6..00000000 --- a/modules/bindings/host/src/graphics.rs +++ /dev/null @@ -1,198 +0,0 @@ -use std::{ - cell::OnceCell, - collections::{BTreeMap, btree_map::Entry}, - os::raw::c_void, -}; - -pub use graphics::lvgl; -use task::block_on; - -use task::TaskIdentifier; -use virtual_machine::{ - Environment, EnvironmentPointer, FunctionDescriptor, Registrable, WasmPointer, WasmUsize, -}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Error { - InvalidArgumentsCount, - InvalidPointer, - NativePointerNotFound, - WasmPointerNotFound, - PointerTableFull, - EnvironmentRetrievalFailed, -} - -pub type Result = core::result::Result; - -mod generated_bindings { - use super::{Error, PointerTable, Result, TaskIdentifier, lvgl::*}; - use virtual_machine::{Environment, WasmPointer, WasmUsize}; - - unsafe fn convert_to_native_pointer( - environment: &Environment, - pointer: WasmPointer, - ) -> Result<*mut T> { - unsafe { - environment - .convert_to_native_pointer(pointer) - .ok_or(Error::InvalidPointer) - } - } - - include!(concat!(env!("OUT_DIR"), "/bindings.rs")); -} - -pub struct GraphicsBindings; - -impl Registrable for GraphicsBindings { - fn get_functions(&self) -> &[FunctionDescriptor] { - &GRAPHICS_BINDINGS_FUNCTIONS - } - - #[cfg(not(target_arch = "x86_64"))] - fn is_xip(&self) -> bool { - true - } - - fn get_name(&self) -> &'static str { - "xila_graphics\0" - } -} - -pub(crate) struct PointerTable { - to_native_pointer: BTreeMap, - to_wasm_pointer: BTreeMap<*mut c_void, u16>, -} - -impl PointerTable { - pub fn new() -> Self { - Self { - to_native_pointer: BTreeMap::new(), - to_wasm_pointer: BTreeMap::new(), - } - } - - const fn get_identifier(task: TaskIdentifier, identifier: u16) -> usize { - (task.into_inner() as usize) << 32 | identifier as usize - } - - pub fn insert(&mut self, task: TaskIdentifier, pointer: *mut c_void) -> Result { - for i in u16::MIN..u16::MAX { - let identifier = Self::get_identifier(task, i); - - match self.to_native_pointer.entry(identifier) { - Entry::Vacant(entry) => { - entry.insert(pointer); - self.to_wasm_pointer.insert(pointer, i); - return Ok(i); - } - Entry::Occupied(entry_pointer) => { - if *entry_pointer.get() == pointer { - return Ok(i); - } - } - } - } - - Err(Error::PointerTableFull) - } - - pub fn get_native_pointer(&self, task: TaskIdentifier, identifier: u16) -> Result<*mut T> { - let identifier = Self::get_identifier(task, identifier); - - self.to_native_pointer - .get(&identifier) - .map(|pointer| *pointer as *mut T) - .ok_or(Error::NativePointerNotFound) - } - - pub fn get_wasm_pointer(&self, pointer: *mut T) -> Result { - self.to_wasm_pointer - .get(&(pointer as *mut c_void)) - .cloned() - .ok_or(Error::WasmPointerNotFound) - } - - pub fn remove(&mut self, task: TaskIdentifier, identifier: u16) -> Result<*mut T> { - let identifier = Self::get_identifier(task, identifier); - - let pointer = self - .to_native_pointer - .remove(&identifier) - .map(|pointer| pointer as *mut T) - .ok_or(Error::NativePointerNotFound)?; - - self.to_wasm_pointer.remove(&(pointer as *mut _)); - - Ok(pointer) - } -} - -static mut POINTER_TABLE: OnceCell = OnceCell::new(); - -/// Call to graphics API -/// -/// # Safety -/// -/// This function is unsafe because it may dereference raw pointers (e.g. `Environment`, `Result` or `Arguments`). -/// The pointer must be valid and properly aligned (ensured by the virtual machine). -#[allow(clippy::too_many_arguments)] -pub unsafe fn call( - environment: EnvironmentPointer, - function: generated_bindings::FunctionCall, - argument_0: WasmUsize, - argument_1: WasmUsize, - argument_2: WasmUsize, - argument_3: WasmUsize, - argument_4: WasmUsize, - argument_5: WasmUsize, - argument_6: WasmUsize, - arguments_count: u8, - result_pointer: WasmPointer, -) -> i32 { - unsafe { - let environment = Environment::from_raw_pointer(environment).unwrap(); - - let instance = graphics::get_instance(); - - let _lock = block_on(instance.lock()); - - let pointer_table_reference = &raw mut POINTER_TABLE; - - let _ = (*pointer_table_reference).get_or_init(PointerTable::new); - - let pointer_table_reference = (*pointer_table_reference).get_mut().unwrap(); - - let result = generated_bindings::call_function( - environment, - pointer_table_reference, - function, - argument_0, - argument_1, - argument_2, - argument_3, - argument_4, - argument_5, - argument_6, - arguments_count, - result_pointer, - ); - - if let Err(error) = result { - log::error!( - "Error {error:?} durring graphics call: {function:?} with arguments: {argument_0:x}, {argument_1:x}, {argument_2:x}, {argument_3:x}, {argument_4:x}, {argument_5:x}, {argument_6:x}, count: {arguments_count}, result pointer: {result_pointer:x}" - ); - - error as i32 - } else { - 0 - } - - // Lock is automatically released here. - } -} - -const GRAPHICS_BINDINGS_FUNCTIONS: [FunctionDescriptor; 1] = [FunctionDescriptor { - name: "xila_graphics_call", - pointer: call as *mut _, -}]; diff --git a/modules/bindings/host/src/lib.rs b/modules/bindings/host/src/lib.rs deleted file mode 100644 index 2af2a14c..00000000 --- a/modules/bindings/host/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![cfg(not(target_arch = "wasm32"))] - -mod graphics; -pub use graphics::*; diff --git a/modules/bindings/host/tests/graphics.rs b/modules/bindings/host/tests/graphics.rs deleted file mode 100644 index b1c96cbb..00000000 --- a/modules/bindings/host/tests/graphics.rs +++ /dev/null @@ -1,44 +0,0 @@ -#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] -#[ignore] -#[task::test] -async fn test() { - extern crate abi_definitions; - extern crate alloc; - extern crate std; - - use executable::build_crate; - use std::fs; - use time::Duration; - - drivers_std::memory::instantiate_global_allocator!(); - - let binary_path = build_crate(&"host_bindings_wasm_test").unwrap(); - let binary_buffer = fs::read(&binary_path).unwrap(); - - let standard = testing::initialize(true, false).await.split(); - - let virtual_machine = virtual_machine::initialize(&[&host_bindings::GraphicsBindings]); - - let task_manager = task::get_instance(); - let task = task_manager.get_current_task_identifier().await; - - //let window = graphics_manager.create_window().await.unwrap(); - - //let _calendar = unsafe { lvgl::lv_calendar_create(window.into_raw()) }; - - virtual_machine - .execute( - binary_buffer.to_vec(), - 8 * 1024, - standard, - None, - vec![], - task, - ) - .await - .unwrap(); - - loop { - task::Manager::sleep(Duration::from_millis(1000)).await; - } -} diff --git a/modules/bindings/host/tests/wasm_test/.cargo/config.toml b/modules/bindings/host/tests/wasm_test/.cargo/config.toml deleted file mode 100644 index 0438f9bb..00000000 --- a/modules/bindings/host/tests/wasm_test/.cargo/config.toml +++ /dev/null @@ -1,16 +0,0 @@ -[build] -target = "wasm32-wasip1" -rustflags = [ - "-C", - "link-arg=--initial-memory=131072", - "-C", - "link-arg=--max-memory=131072", - "-C", - "link-arg=-zstack-size=8192", - "-C", - "link-arg=--export=__heap_base", - "-C", - "link-arg=--export=__data_end", - "-C", - "link-arg=--strip-all", -] diff --git a/modules/bindings/host/tests/wasm_test/.gitignore b/modules/bindings/host/tests/wasm_test/.gitignore deleted file mode 100644 index 9f970225..00000000 --- a/modules/bindings/host/tests/wasm_test/.gitignore +++ /dev/null @@ -1 +0,0 @@ -target/ \ No newline at end of file diff --git a/modules/bindings/host/tests/wasm_test/Cargo.toml b/modules/bindings/host/tests/wasm_test/Cargo.toml deleted file mode 100644 index f4925b93..00000000 --- a/modules/bindings/host/tests/wasm_test/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "host_bindings_wasm_test" -version = "0.1.0" -edition = "2024" - -[dependencies] -wasm_bindings = { workspace = true } diff --git a/modules/bindings/host/tests/wasm_test/src/main.rs b/modules/bindings/host/tests/wasm_test/src/main.rs deleted file mode 100644 index 998c0486..00000000 --- a/modules/bindings/host/tests/wasm_test/src/main.rs +++ /dev/null @@ -1,48 +0,0 @@ -#[cfg(target_arch = "wasm32")] -fn main() { - use std::thread::sleep; - - println!("Hello, world!"); - - unsafe { - let window = wasm_bindings::window_create().unwrap(); - - let button = wasm_bindings::button_create(window).unwrap(); - - let label = wasm_bindings::label_create(button).unwrap(); - wasm_bindings::label_set_text(label, c"Hello, world!".as_ptr() as *mut _).unwrap(); - - loop { - let mut code = wasm_bindings::EventCode::All; - let mut target: *mut wasm_bindings::Object = core::ptr::null_mut(); - - let _ = wasm_bindings::window_pop_event( - window, - &mut code as *mut _ as *mut _, - &mut target as *mut _ as *mut _, - ); - - match code { - wasm_bindings::EventCode::Clicked => { - println!("Button pressed!"); - - if target == button { - wasm_bindings::label_set_text(label, c"Button pressed!".as_ptr() as *mut _) - .unwrap(); - } - } - wasm_bindings::EventCode::All => {} - event => { - println!("Event : {:?}", event); - } - } - - sleep(std::time::Duration::from_millis(10)); - } - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn main() { - println!("This test is only for the wasm32 target."); -} diff --git a/modules/bindings/utilities/Cargo.toml b/modules/bindings/utilities/Cargo.toml deleted file mode 100644 index 0bd8daf3..00000000 --- a/modules/bindings/utilities/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "bindings_utilities" -version = "0.1.0" -edition = "2024" - -[dependencies] -syn = { workspace = true, features = ["full", "visit", "extra-traits"] } -quote = { workspace = true } -proc-macro2 = { workspace = true } diff --git a/modules/bindings/utilities/src/additional.rs b/modules/bindings/utilities/src/additional.rs deleted file mode 100644 index 9012d030..00000000 --- a/modules/bindings/utilities/src/additional.rs +++ /dev/null @@ -1,141 +0,0 @@ -use proc_macro2::TokenStream; -use quote::quote; - -pub fn get() -> TokenStream { - quote! { - pub unsafe fn object_delete(__pointer_table : &mut PointerTable, __task: TaskIdentifier, object: u16) { - - let object = __pointer_table.remove(__task, object).unwrap(); - - unsafe { - lv_obj_delete(object); - } - } - - pub unsafe fn window_create() -> *mut lv_obj_t { - task::block_on( - graphics::get_instance().create_window() - ).unwrap().into_raw() - } - - pub unsafe fn window_pop_event( - __environment: Environment, - __pointer_table: &mut PointerTable, - window: *mut lv_obj_t, - code: *mut u32, - target: *mut u16 - ) { - let mut window = unsafe { graphics::Window::from_raw(window) }; - - if let Some(event) = window.pop_event() { - - unsafe { - *code = event.code as u32 ; - - *target = __pointer_table - .get_wasm_pointer(event.target) - .unwrap(); - } - } - - core::mem::forget(window); - } - - pub unsafe fn window_get_event_code(window: *mut lv_obj_t) -> u32 { - let window = unsafe { graphics::Window::from_raw(window) }; - - let code = if let Some(event) = window.peek_event() { - event.code as u32 - } else { - graphics::EventKind::All as u32 - }; - - core::mem::forget(window); - - code - } - - pub unsafe fn window_get_event_target(__pointer_table: &mut PointerTable, window: *mut lv_obj_t) -> u16 { - let window = unsafe { graphics::Window::from_raw(window) }; - - let target = if let Some(event) = window.peek_event() { - event.target - } else { - log::warning!("No event available for the window"); - core::ptr::null_mut() - }; - - core::mem::forget(window); - - __pointer_table.get_wasm_pointer(target) - .unwrap() - } - - pub unsafe fn window_next_event(window: *mut lv_obj_t) { - let mut window = unsafe { graphics::Window::from_raw(window) }; - - window.pop_event(); - - core::mem::forget(window); - } - - pub unsafe fn window_set_icon( - __environment : Environment, - __pointer_table : &mut PointerTable, - __task: TaskIdentifier, - window: *mut lv_obj_t, - icon_string: *const core::ffi::c_char, - icon_color: lv_color_t - ) { - let mut window = unsafe { graphics::Window::from_raw(window) }; - - let icon_string = unsafe { core::ffi::CStr::from_ptr(icon_string).to_str().unwrap() }; - - let icon_color = graphics::Color::from_lvgl_color(icon_color); - - window.set_icon(icon_string, icon_color); - - core::mem::forget(window); - } - - pub unsafe fn buttonmatrix_set_map(__environment : Environment, __pointer_table : &mut PointerTable, __task: TaskIdentifier, object: u16, map: *const *const i8) { - - let mut v : Vec<*const i8> = vec![]; - let mut i = 0; - - // Treat Map as a pointer to u32 values instead - let map_as_u32 = map as *const u32; - loop { - let val = unsafe { *map_as_u32.add(i) }; - - let val : *const i8 = unsafe { convert_to_native_pointer(&__environment, val).unwrap() }; - - v.push(val); - - // Check if the converted pointer points to an empty string - if unsafe { *val == 0 } { - break; - } - - i += 1; - - } - - let object = __pointer_table.get_native_pointer(__task, object).unwrap(); - unsafe { - lv_buttonmatrix_set_map(object, v.as_ptr()); - } - - core::mem::forget(v); // ! : deallocate the vector to avoid memory leaks when the button matrix map is deleted - } - - pub unsafe fn percentage( - value: i32, - ) -> i32 { - unsafe { - lv_pct(value) - } - } - - } -} diff --git a/modules/bindings/wasm/.gitignore b/modules/bindings/wasm/.gitignore deleted file mode 100644 index 9f970225..00000000 --- a/modules/bindings/wasm/.gitignore +++ /dev/null @@ -1 +0,0 @@ -target/ \ No newline at end of file diff --git a/modules/bindings/wasm/Cargo.toml b/modules/bindings/wasm/Cargo.toml deleted file mode 100644 index f3cf371a..00000000 --- a/modules/bindings/wasm/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "wasm_bindings" -version = "0.1.0" -edition = "2024" -build = "build/main.rs" - -[features] -default = [] -c_bindings = [] - -[build-dependencies] -lvgl_rust_sys = { git = "https://github.com/Xila-Project/lvgl_rust_sys.git", default-features = false } -bindgen = { workspace = true } -bindings_utilities = { workspace = true } -quote = { workspace = true } -syn = { workspace = true, features = ["full", "visit", "extra-traits"] } -target = { workspace = true } -proc-macro2 = { workspace = true } -cbindgen = { workspace = true } diff --git a/modules/bindings/wasm/build/main.rs b/modules/bindings/wasm/build/main.rs deleted file mode 100644 index 0c4e0533..00000000 --- a/modules/bindings/wasm/build/main.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::{ - env, fs, - path::{Path, PathBuf}, -}; - -use bindings_utilities::format::format_rust; -use cbindgen::{EnumConfig, ExportConfig, FunctionConfig, RenameRule}; -use quote::quote; -use syn::visit::Visit; -use target::Architecture; - -mod generator; - -fn is_c_bindings_enabled() -> bool { - env::var_os("CARGO_FEATURE_C_BINDINGS").is_some() -} - -fn generate_c_functions_module_body(path: impl AsRef) -> Result<(), String> { - let token_stream = quote! { - include!(concat!(env!("OUT_DIR"), "/c_functions.generated.rs")); - }; - - fs::write(&path, token_stream.to_string()) - .map_err(|e| format!("Error writing to file: {}", e))?; - - format_rust(path)?; - - Ok(()) -} - -fn main() { - // Build only for WASM32 architecture. - if Architecture::get() != Architecture::WASM32 { - return; - } - - let input = lvgl_rust_sys::_bindgen_raw_src(); - let parsed_input = syn::parse_file(input).expect("Error parsing input file"); - - let mut context = bindings_utilities::context::LvglContext::default(); - context.set_function_filtering(Some( - bindings_utilities::context::LvglContext::filter_function, - )); - context.visit_file(&parsed_input); - context.set_function_filtering(None); - context.visit_file(&syn::parse2(bindings_utilities::additional::get()).unwrap()); - - let out_directory = PathBuf::from(env::var("OUT_DIR").unwrap()); - let crate_directory = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); - - let enumerations_generated_path = out_directory.join("enumeration.generated.rs"); - let functions_generated_path = out_directory.join("functions.generated.rs"); - let c_functions_generated_path = out_directory.join("c_functions.generated.rs"); - let c_functions_module_path = crate_directory.join("src").join("c_functions.rs"); - let c_header_path = out_directory.join("xila_graphics.h"); - - generator::generate_enumeration(&enumerations_generated_path, &context).unwrap(); - - generator::generate_functions(&functions_generated_path, &context).unwrap(); - - if is_c_bindings_enabled() { - // Overwrite c_functions.rs file with generated C ABI functions - // This is workaround for cbindgen macro expansion limitations - generator::generate_c_abi_functions(&c_functions_module_path, &context).unwrap(); - - generator::generate_c_abi_functions(&c_functions_generated_path, &context).unwrap(); - - let configuration: cbindgen::Config = cbindgen::Config { - language: cbindgen::Language::C, - include_guard: Some("__XILA_GRAPHICS_GENERATED_H_INCLUDED".to_string()), - sys_includes: vec![ - "stdarg.h".to_string(), - "stdbool.h".to_string(), - "stdint.h".to_string(), - ], - export: ExportConfig { - prefix: Some("XilaGraphics".to_string()), - ..Default::default() - }, - function: FunctionConfig { - ..Default::default() - }, - no_includes: true, - enumeration: EnumConfig { - rename_variants: RenameRule::QualifiedScreamingSnakeCase, - ..Default::default() - }, - ..Default::default() - }; - - cbindgen::Builder::new() - .with_crate(crate_directory) - .with_config(configuration) - .generate() - .expect("Unable to generate bindings") - .write_to_file(&c_header_path); - - // Restore c_functions.rs file - generate_c_functions_module_body(&c_functions_module_path).unwrap(); - } -} diff --git a/modules/bindings/wasm/src/bindings.rs b/modules/bindings/wasm/src/bindings.rs deleted file mode 100644 index 9e25d3d5..00000000 --- a/modules/bindings/wasm/src/bindings.rs +++ /dev/null @@ -1 +0,0 @@ -include ! (concat ! (env ! ("OUT_DIR") , "/bindings.rs")) ; \ No newline at end of file diff --git a/modules/bindings/wasm/src/lib.rs b/modules/bindings/wasm/src/lib.rs deleted file mode 100644 index 26922ef8..00000000 --- a/modules/bindings/wasm/src/lib.rs +++ /dev/null @@ -1,22 +0,0 @@ -#![no_std] - -#[cfg(target_arch = "wasm32")] -mod prelude; - -#[cfg(target_arch = "wasm32")] -pub use prelude::*; - -#[cfg(target_arch = "wasm32")] -mod enumeration; - -#[cfg(target_arch = "wasm32")] -use enumeration::*; - -#[cfg(target_arch = "wasm32")] -mod functions; - -#[cfg(target_arch = "wasm32")] -pub use functions::*; - -#[cfg(all(target_arch = "wasm32", feature = "c_bindings"))] -pub mod c_functions; From b2289edf90faad9be28d97baae0bd17995db443c Mon Sep 17 00:00:00 2001 From: Alix ANNERAUD Date: Tue, 10 Feb 2026 23:37:10 +0100 Subject: [PATCH 02/16] Add BijectiveBTreeMap implementation for bidirectional mapping --- modules/shared/src/bijective_map.rs | 80 +++++++++++++++++++++++++++++ modules/shared/src/lib.rs | 2 + 2 files changed, 82 insertions(+) create mode 100644 modules/shared/src/bijective_map.rs diff --git a/modules/shared/src/bijective_map.rs b/modules/shared/src/bijective_map.rs new file mode 100644 index 00000000..c8e75847 --- /dev/null +++ b/modules/shared/src/bijective_map.rs @@ -0,0 +1,80 @@ +use core::fmt::Debug; + +use alloc::collections::btree_map::BTreeMap; + +pub struct BijectiveBTreeMap { + to_left: BTreeMap, + to_right: BTreeMap, +} + +impl BijectiveBTreeMap { + pub const fn new() -> Self { + Self { + to_left: BTreeMap::new(), + to_right: BTreeMap::new(), + } + } + + pub fn insert(&mut self, key: L, value: R) { + self.to_left.insert(value, key); + self.to_right.insert(key, value); + } + + pub fn get_by_left(&self, key: &L) -> Option<&R> { + self.to_right.get(key) + } + + pub fn get_by_right(&self, value: &R) -> Option<&L> { + self.to_left.get(value) + } + + pub fn remove_by_key(&mut self, key: &L) -> Option { + if let Some(value) = self.to_right.remove(key) { + self.to_left.remove(&value); + Some(value) + } else { + None + } + } + + pub fn remove_by_value(&mut self, value: &R) -> Option { + if let Some(key) = self.to_left.remove(value) { + self.to_right.remove(&key); + Some(key) + } else { + None + } + } + + pub fn get_left_keys(&self) -> impl Iterator { + self.to_right.keys() + } + + pub fn get_right_keys(&self) -> impl Iterator { + self.to_left.keys() + } +} + +impl Default for BijectiveBTreeMap { + fn default() -> Self { + Self::new() + } +} + +impl Debug for BijectiveBTreeMap { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("BijectiveBTreeMap") + .field("to_key", &self.to_left) + .field("to_value", &self.to_right) + .finish() + } +} + +impl Clone for BijectiveBTreeMap { + fn clone(&self) -> Self { + Self { + to_left: self.to_left.clone(), + to_right: self.to_right.clone(), + } + } +} diff --git a/modules/shared/src/lib.rs b/modules/shared/src/lib.rs index 7cb22fe7..b189c074 100644 --- a/modules/shared/src/lib.rs +++ b/modules/shared/src/lib.rs @@ -3,6 +3,7 @@ extern crate alloc; mod any; +mod bijective_map; mod error; pub mod flags; mod http; @@ -14,6 +15,7 @@ mod unit; mod utf8; pub use any::*; +pub use bijective_map::*; pub use error::*; pub use http::*; pub use size::*; From 742efc9663f3f65f84ad5e14764d99969d0b843f Mon Sep 17 00:00:00 2001 From: Alix ANNERAUD Date: Tue, 10 Feb 2026 23:38:33 +0100 Subject: [PATCH 03/16] Moved virtual machine from core into wasm executable --- .cargo/config.toml | 2 +- Cargo.toml | 28 +- Makefile.toml | 6 +- executables/calculator/Cargo.toml | 4 +- executables/calculator/src/interface.rs | 20 +- executables/calculator/tests/test.rs | 98 ++++--- executables/wasm/Cargo.toml | 24 +- executables/wasm/locales/en/messages.po | 54 ++++ executables/wasm/locales/fr/messages.po | 54 ++++ executables/wasm/locales/messages.pot | 54 ++++ executables/wasm/src/error.rs | 63 ----- executables/wasm/src/host/mod.rs | 184 +++++++++++++ .../src/host/virtual_machine/custom_data.rs | 10 + .../src/host/virtual_machine/environment.rs | 78 ++++++ .../wasm/src/host/virtual_machine/error.rs | 182 ++++++++++++ .../wasm/src/host/virtual_machine/instance.rs | 68 +++++ .../wasm/src/host/virtual_machine/mod.rs | 31 +++ .../wasm/src/host/virtual_machine}/module.rs | 5 +- .../src/host/virtual_machine/registrable.rs | 11 + .../wasm/src/host/virtual_machine/runtime.rs | 119 ++++++++ .../src/host/virtual_machine/translation.rs | 79 ++++++ executables/wasm/src/lib.rs | 177 +----------- executables/wasm/src/locales.toml | 43 --- executables/wasm/tests/integration_test.rs | 7 +- executables/wasm/tests/wasm_test/Cargo.toml | 1 + executables/wasm/tests/wasm_test/src/main.rs | 167 +++++++++++- .../wasm}/wamr/include/internal.h | 2 +- .../wasm}/wamr/include/platform_internal.h | 2 +- .../wasm}/wamr/include/stubs.h | 2 +- .../wasm}/wamr/shared_platform.cmake | 0 .../wasm}/wamr/src/core.c | 2 +- .../wasm}/wamr/src/extension.c | 2 +- .../wasm}/wamr/src/internal.c | 2 +- modules/virtual_machine/Cargo.toml | 44 --- modules/virtual_machine/src/custom_data.rs | 18 -- modules/virtual_machine/src/environment.rs | 192 ------------- modules/virtual_machine/src/error.rs | 98 ------- modules/virtual_machine/src/instance.rs | 139 ---------- modules/virtual_machine/src/lib.rs | 85 ------ modules/virtual_machine/src/manager.rs | 258 ------------------ modules/virtual_machine/src/registrable.rs | 40 --- modules/virtual_machine/src/runtime.rs | 73 ----- modules/virtual_machine/tests/test.rs | 112 -------- modules/virtual_machine/tests/test_2.rs | 50 ---- modules/virtual_machine/tests/test_3.rs | 139 ---------- .../tests/wasm_test/.cargo/config.toml | 12 - .../tests/wasm_test/Cargo.toml | 6 - .../tests/wasm_test/src/main.rs | 166 ----------- src/lib.rs | 32 +-- 49 files changed, 1211 insertions(+), 1834 deletions(-) delete mode 100644 executables/wasm/src/error.rs create mode 100644 executables/wasm/src/host/mod.rs create mode 100644 executables/wasm/src/host/virtual_machine/custom_data.rs create mode 100644 executables/wasm/src/host/virtual_machine/environment.rs create mode 100644 executables/wasm/src/host/virtual_machine/error.rs create mode 100644 executables/wasm/src/host/virtual_machine/instance.rs create mode 100644 executables/wasm/src/host/virtual_machine/mod.rs rename {modules/virtual_machine/src => executables/wasm/src/host/virtual_machine}/module.rs (95%) create mode 100644 executables/wasm/src/host/virtual_machine/registrable.rs create mode 100644 executables/wasm/src/host/virtual_machine/runtime.rs create mode 100644 executables/wasm/src/host/virtual_machine/translation.rs delete mode 100644 executables/wasm/src/locales.toml rename {modules/virtual_machine => executables/wasm}/wamr/include/internal.h (94%) rename {modules/virtual_machine => executables/wasm}/wamr/include/platform_internal.h (98%) rename {modules/virtual_machine => executables/wasm}/wamr/include/stubs.h (93%) rename {modules/virtual_machine => executables/wasm}/wamr/shared_platform.cmake (100%) rename {modules/virtual_machine => executables/wasm}/wamr/src/core.c (99%) rename {modules/virtual_machine => executables/wasm}/wamr/src/extension.c (99%) rename {modules/virtual_machine => executables/wasm}/wamr/src/internal.c (98%) delete mode 100644 modules/virtual_machine/Cargo.toml delete mode 100644 modules/virtual_machine/src/custom_data.rs delete mode 100644 modules/virtual_machine/src/environment.rs delete mode 100644 modules/virtual_machine/src/error.rs delete mode 100644 modules/virtual_machine/src/instance.rs delete mode 100644 modules/virtual_machine/src/lib.rs delete mode 100644 modules/virtual_machine/src/manager.rs delete mode 100644 modules/virtual_machine/src/registrable.rs delete mode 100644 modules/virtual_machine/src/runtime.rs delete mode 100644 modules/virtual_machine/tests/test.rs delete mode 100644 modules/virtual_machine/tests/test_2.rs delete mode 100644 modules/virtual_machine/tests/test_3.rs delete mode 100644 modules/virtual_machine/tests/wasm_test/.cargo/config.toml delete mode 100644 modules/virtual_machine/tests/wasm_test/Cargo.toml delete mode 100644 modules/virtual_machine/tests/wasm_test/src/main.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index 0a2f7bfd..2f292aef 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -23,7 +23,7 @@ LVGL_FONTS_DIR = { value = "modules/graphics/fonts_generator/generated_fonts", r WAMR_BUILD_PLATFORM = "XILA" -WAMR_SHARED_PLATFORM_CONFIG = { value = "modules/virtual_machine/wamr/shared_platform.cmake", relative = true } +WAMR_SHARED_PLATFORM_CONFIG = { value = "executables/wasm/wamr/shared_platform.cmake", relative = true } LLVM_LIB_CFG_PATH = "/usr/lib/llvm-19/cmake/" LITTLEFS_CONFIG = { value = "modules/little_fs/include/little_fs_config.h", relative = true } diff --git a/Cargo.toml b/Cargo.toml index 0bb2ba55..4044cdb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,15 +18,12 @@ lto = true # - Always included internationalization = { workspace = true } -# - WASM -wasm_bindings = { workspace = true, optional = true } # - Host abi_context = { workspace = true, optional = true } abi_declarations = { workspace = true, optional = true } abi_definitions = { workspace = true, optional = true } little_fs = { workspace = true, optional = true } -virtual_machine = { workspace = true, optional = true } virtual_file_system = { workspace = true, optional = true } graphics = { workspace = true, optional = true } task = { workspace = true, optional = true } @@ -40,16 +37,11 @@ authentication = { workspace = true, optional = true } log = { workspace = true, optional = true } shared = { workspace = true, optional = true } synchronization = { workspace = true, optional = true } -host_bindings = { workspace = true, optional = true } bootsplash = { workspace = true, optional = true } network = { workspace = true, optional = true } [features] -default = [] - -executable_building = ["executable/building"] - -host = [ +default = [ "dep:abi_context", "dep:abi_declarations", "dep:abi_definitions", @@ -71,23 +63,19 @@ host = [ "abi_definitions", "dep:network", ] + +executable_building = ["executable/building"] +host = [] abi_definitions = ["dep:abi_definitions"] -virtual_machine = ["dep:virtual_machine", "dep:host_bindings"] graphics_rendering_rgb565 = ["graphics/rendering_rgb565"] graphics_rendering_xrgb8888 = ["graphics/rendering_xrgb8888"] -wasm = ["dep:wasm_bindings"] -wasm_c_bindings = ["wasm", "wasm_bindings/c_bindings"] [workspace.dependencies] -# - Modules -wasm_bindings = { path = "modules/bindings/wasm" } - abi_context = { path = "modules/abi/context" } abi_declarations = { path = "modules/abi/declarations" } abi_definitions = { path = "modules/abi/definitions" } little_fs = { path = "modules/little_fs" } -virtual_machine = { path = "modules/virtual_machine" } virtual_file_system = { path = "modules/virtual_file_system" } graphics = { path = "modules/graphics" } time = { path = "modules/time" } @@ -96,8 +84,6 @@ users = { path = "modules/users" } memory = { path = "modules/memory" } executable = { path = "modules/executable" } file_system = { path = "modules/file_system" } -host_bindings = { path = "modules/bindings/host" } -bindings_utilities = { path = "modules/bindings/utilities" } authentication = { path = "modules/authentication" } log = { path = "modules/log" } shared = { path = "modules/shared" } @@ -153,7 +139,6 @@ embedded-io = { version = "0.7", features = ["alloc"] } [workspace] members = [ "modules/file_system", - "modules/virtual_machine", "modules/graphics", "modules/task", "modules/users", @@ -166,10 +151,6 @@ members = [ "modules/virtual_file_system", "modules/little_fs", "modules/target", - "modules/bindings/utilities", - "modules/bindings/host", - "modules/bindings/host/tests/wasm_test", - "modules/bindings/wasm", "executables/file_manager", "executables/shell/command_line", "modules/executable", @@ -193,7 +174,6 @@ members = [ "drivers/shared", "drivers/std", "drivers/wasm", - "modules/virtual_machine/tests/wasm_test", "modules/internationalization", "modules/internationalization/macros", "modules/graphics/fonts_generator", diff --git a/Makefile.toml b/Makefile.toml index 92fc6246..398a30e9 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -34,7 +34,7 @@ args = [ "--target", "x86_64-unknown-linux-gnu", "--features", - "host,abi_definitions,virtual_machine,graphics_rendering_xrgb8888", + "host,abi_definitions,graphics_rendering_xrgb8888", ] [tasks.check-wasm] @@ -54,7 +54,7 @@ command = "cargo" args = [ "doc", "--features", - "host,abi_definitions,virtual_machine,graphics_rendering_xrgb8888", + "host,abi_definitions,graphics_rendering_xrgb8888", "--target", "x86_64-unknown-linux-gnu", ] @@ -95,7 +95,7 @@ args = [ "--target", "x86_64-unknown-linux-gnu", "--features", - "host,abi_definitions,virtual_machine,graphics_rendering_xrgb8888", + "host,abi_definitions,graphics_rendering_xrgb8888", "--all-targets", "--", "-D", diff --git a/executables/calculator/Cargo.toml b/executables/calculator/Cargo.toml index 5447893e..a4a6cc46 100644 --- a/executables/calculator/Cargo.toml +++ b/executables/calculator/Cargo.toml @@ -4,13 +4,12 @@ version = "0.1.0" edition = "2024" [target.'cfg(target_arch = "wasm32")'.dependencies] -xila = { path = "../../", features = ["wasm"] } +wasm = { workspace = true, features = ["guest"] } [target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))'.dev-dependencies] xila = { path = "../../", features = [ "host", "executable_building", - "virtual_machine", "graphics_rendering_xrgb8888", ] } drivers_core = { workspace = true } @@ -18,6 +17,7 @@ drivers_std = { workspace = true } drivers_shared = { workspace = true } drivers_native = { workspace = true } testing = { workspace = true } +wasm = { workspace = true, features = ["default_host"] } [[test]] name = "calculator_test" diff --git a/executables/calculator/src/interface.rs b/executables/calculator/src/interface.rs index b25dd4fe..d2a7357d 100644 --- a/executables/calculator/src/interface.rs +++ b/executables/calculator/src/interface.rs @@ -1,6 +1,6 @@ use crate::{evaluator::Evaluator, parser::Parser}; use std::{ptr::null_mut, thread::sleep}; -use xila::bindings::{ +use wasm::{ self, Color, EventCode, FlexFlow, Object, ObjectFlag, buttonmatrix_create, buttonmatrix_get_selected_button, buttonmatrix_set_map, label_create, label_set_text, object_add_flag, object_create, object_set_flex_flow, object_set_flex_grow, object_set_height, @@ -117,7 +117,7 @@ const BUTTON_MAP: [*const i8; 48] = [ c"pi".as_ptr(), c"e".as_ptr(), c"\n".as_ptr(), - c"".as_ptr(), // End marker + null_mut(), // Extra null to fill the 48-button map ]; impl ButtonIdentifier { @@ -144,7 +144,7 @@ pub struct Interface { } impl Interface { - pub fn new() -> bindings::Result { + pub fn new() -> wasm::Result { let window = unsafe { Self::create_window() }?; let (display, display_label) = unsafe { Self::create_display(window) }?; @@ -162,7 +162,7 @@ impl Interface { }) } - unsafe fn create_window() -> bindings::Result<*mut Object> { + unsafe fn create_window() -> wasm::Result<*mut Object> { // Create main window unsafe { let window = window_create()?; @@ -183,7 +183,7 @@ impl Interface { } } - unsafe fn create_display(window: *mut Object) -> bindings::Result<(*mut Object, *mut Object)> { + unsafe fn create_display(window: *mut Object) -> wasm::Result<(*mut Object, *mut Object)> { unsafe { // Create display container using generic object create let display = object_create(window)?; @@ -204,7 +204,7 @@ impl Interface { } } - unsafe fn create_button_matrix(window: *mut Object) -> bindings::Result<*mut Object> { + unsafe fn create_button_matrix(window: *mut Object) -> wasm::Result<*mut Object> { // Create button matrix for calculator unsafe { let button_matrix = buttonmatrix_create(window)?; @@ -223,7 +223,7 @@ impl Interface { // Define button layout - Extended scientific calculator // Set the button map - buttonmatrix_set_map(button_matrix as u16, BUTTON_MAP.as_ptr())?; + buttonmatrix_set_map(button_matrix, BUTTON_MAP.as_ptr())?; // Optional: Make some buttons wider if needed // buttonmatrix_set_button_width(self.button_matrix, 31, 2); // Make "0" wider @@ -232,7 +232,7 @@ impl Interface { } } - fn update_display(&mut self) -> bindings::Result<()> { + fn update_display(&mut self) -> wasm::Result<()> { unsafe { let display_text = if self.show_result { self.current_expression.clone() @@ -257,7 +257,7 @@ impl Interface { Ok(()) } - unsafe fn handle_button_matrix_event(&mut self) -> bindings::Result<()> { + unsafe fn handle_button_matrix_event(&mut self) -> wasm::Result<()> { // Get the selected button ID from the button matrix let selected_button_id: u32 = unsafe { buttonmatrix_get_selected_button(self.button_matrix) }?; @@ -269,7 +269,7 @@ impl Interface { Ok(()) } - fn handle_button_press(&mut self, button: ButtonIdentifier) -> bindings::Result<()> { + fn handle_button_press(&mut self, button: ButtonIdentifier) -> wasm::Result<()> { match button { ButtonIdentifier::Clear => { self.current_expression.clear(); diff --git a/executables/calculator/tests/test.rs b/executables/calculator/tests/test.rs index 1123071a..4bcc2f37 100644 --- a/executables/calculator/tests/test.rs +++ b/executables/calculator/tests/test.rs @@ -1,52 +1,66 @@ #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] #[xila::task::test(task_path = xila::task)] -#[ignore] async fn main() { drivers_std::memory::instantiate_global_allocator!(); extern crate alloc; - use drivers_std::executor::new_thread_executor; - use std::fs; - use xila::executable::build_crate; - use xila::host_bindings; + use drivers_std::loader::load_to_virtual_file_system; + use wasm::WasmExecutable; + use xila::executable; + use xila::executable::{build_crate, mount_executables}; use xila::task; - use xila::time::Duration; - use xila::virtual_machine; - - let binary_path = build_crate("calculator").unwrap(); - let binary_buffer = fs::read(binary_path).unwrap(); - - let standard = testing::initialize(true, false).await.split(); - - let task_manager = task::get_instance(); - let virtual_machine = virtual_machine::initialize(&[&host_bindings::GraphicsBindings]); - - let additional_spawner = new_thread_executor().await; - - task_manager - .spawn( - task_manager.get_current_task_identifier().await, - "Runner", - Some(additional_spawner), - async move |task| { - virtual_machine - .execute( - binary_buffer.to_vec(), - 8 * 1024, - standard, - None, - vec![], - task, - ) - .await - .unwrap(); - }, - ) - .await - .unwrap(); - - loop { - task::Manager::sleep(Duration::from_millis(1000)).await; + use xila::virtual_file_system; + + let standard = testing::initialize(true, false).await; + + let virtual_file_system = virtual_file_system::get_instance(); + let task_instance = task::get_instance(); + let task = task_instance.get_current_task_identifier().await; + + let binary_path = build_crate(&"calculator").unwrap(); + load_to_virtual_file_system( + virtual_file_system, + binary_path, + "/binaries/calculator.wasm", + ) + .await + .unwrap(); + + fn new_thread_executor_wrapper() + -> core::pin::Pin + Send>> { + use drivers_std::executor::new_thread_executor; + + Box::pin(new_thread_executor()) } + + mount_executables!( + virtual_file_system, + task, + &[( + "/binaries/wasm", + WasmExecutable::new(Some(new_thread_executor_wrapper)) + )] + ) + .await + .unwrap(); + + let result = executable::execute( + "/binaries/wasm", + vec!["/binaries/calculator.wasm".to_string()], + standard, + None, + ) + .await + .unwrap() + .join() + .await; + + // let result = executable::execute("/binaries/command_line_shell", vec![], standard, None) + // .await + // .unwrap() + // .join() + // .await; + + assert!(result == 0); } diff --git a/executables/wasm/Cargo.toml b/executables/wasm/Cargo.toml index 91befdeb..cd1b1684 100644 --- a/executables/wasm/Cargo.toml +++ b/executables/wasm/Cargo.toml @@ -2,9 +2,31 @@ name = "wasm" version = "0.1.0" edition = "2024" +build = "build/main.rs" [dependencies] -xila = { path = "../../", features = ["host", "virtual_machine"] } +xila = { path = "../../", optional = true } +wamr-rust-sdk = { git = "https://github.com/bytecodealliance/wamr-rust-sdk.git", features = [ + "multi-module", +], optional = true } + +[features] +default = [] +graphics = [] +host = ["dep:xila", "dep:wamr-rust-sdk"] +guest = [] +wasm32 = [] +wasm64 = [] +default_host = ["host", "graphics", "wasm32"] + +[build-dependencies] +lvgl_rust_sys = { git = "https://github.com/Xila-Project/lvgl_rust_sys.git", default-features = false } +bindgen = { workspace = true } +quote = { workspace = true } +syn = { workspace = true, features = ["full", "visit", "extra-traits"] } +target = { workspace = true } +proc-macro2 = { workspace = true } +cbindgen = { workspace = true } [target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))'.dev-dependencies] drivers_native = { workspace = true } diff --git a/executables/wasm/locales/en/messages.po b/executables/wasm/locales/en/messages.po index eb35294a..c528f53a 100644 --- a/executables/wasm/locales/en/messages.po +++ b/executables/wasm/locales/en/messages.po @@ -45,3 +45,57 @@ msgstr "Failed to open standard file: {:?}" #: error.failed_to_spawn_task msgid "Failed to spawn task: {:?}" msgstr "Failed to spawn task: {:?}" + +msgid "Invalid pointer provided" +msgstr "Invalid pointer provided" + +msgid "Invalid UTF-8 string" +msgstr "Invalid UTF-8 string" + +msgid "Slice conversion failed: {:?}" +msgstr "Slice conversion failed: {:?}" + +msgid "Functionality not implemented" +msgstr "Functionality not implemented" + +msgid "WASM runtime initialization failed" +msgstr "WASM runtime initialization failed" + +msgid "WASM compilation error: {}" +msgstr "WASM compilation error: {}" + +msgid "WASM instantiation failure: {}" +msgstr "WASM instantiation failure: {}" + +msgid "WASM instantiation error: {}" +msgstr "WASM instantiation error: {}" + +msgid "WASM execution error: {}" +msgstr "WASM execution error: {}" + +msgid "Requested function not found in module" +msgstr "Requested function not found in module" + +msgid "Memory allocation failed" +msgstr "Memory allocation failed" + +msgid "Failed to get task information: {:?}" +msgstr "Failed to get task information: {:?}" + +msgid "Mutex or lock is poisoned" +msgstr "Mutex or lock is poisoned" + +msgid "Invalid WASM module format or structure" +msgstr "Invalid WASM module format or structure" + +msgid "Internal runtime error" +msgstr "Internal runtime error" + +msgid "Invalid thread identifier provided" +msgstr "Invalid thread identifier provided" + +msgid "Time-related error: {:?}" +msgstr "Time-related error: {:?}" + +msgid "Failed to register file context" +msgstr "Failed to register file context" diff --git a/executables/wasm/locales/fr/messages.po b/executables/wasm/locales/fr/messages.po index d5b0fea9..83e049ea 100644 --- a/executables/wasm/locales/fr/messages.po +++ b/executables/wasm/locales/fr/messages.po @@ -45,3 +45,57 @@ msgstr "Échec de l'ouverture du fichier standard: {:?}" #: error.failed_to_spawn_task msgid "Failed to spawn task: {:?}" msgstr "Échec de la création de la tâche: {:?}" + +msgid "Invalid pointer provided" +msgstr "Pointeur invalide fourni" + +msgid "Invalid UTF-8 string" +msgstr "Chaîne UTF-8 invalide" + +msgid "Slice conversion failed: {:?}" +msgstr "Échec de la conversion de tranche: {:?}" + +msgid "Functionality not implemented" +msgstr "Fonctionnalité non implémentée" + +msgid "WASM runtime initialization failed" +msgstr "Échec de l'initialisation du runtime WASM" + +msgid "WASM compilation error: {}" +msgstr "Erreur de compilation WASM: {}" + +msgid "WASM instantiation failure: {}" +msgstr "Échec de l'instanciation WASM: {}" + +msgid "WASM instantiation error: {}" +msgstr "Erreur d'instanciation WASM: {}" + +msgid "WASM execution error: {}" +msgstr "Erreur d'exécution WASM: {}" + +msgid "Requested function not found in module" +msgstr "Fonction demandée non trouvée dans le module" + +msgid "Memory allocation failed" +msgstr "Échec de l'allocation mémoire" + +msgid "Failed to get task information: {:?}" +msgstr "Échec de la récupération des informations de tâche: {:?}" + +msgid "Mutex or lock is poisoned" +msgstr "Le mutex ou le verrou est empoisonné" + +msgid "Invalid WASM module format or structure" +msgstr "Format ou structure de module WASM invalide" + +msgid "Internal runtime error" +msgstr "Erreur interne du runtime" + +msgid "Invalid thread identifier provided" +msgstr "Identifiant de thread invalide fourni" + +msgid "Time-related error: {:?}" +msgstr "Erreur liée au temps: {:?}" + +msgid "Failed to register file context" +msgstr "Échec de l'enregistrement du contexte de fichier" diff --git a/executables/wasm/locales/messages.pot b/executables/wasm/locales/messages.pot index 33aafd47..ac186180 100644 --- a/executables/wasm/locales/messages.pot +++ b/executables/wasm/locales/messages.pot @@ -45,3 +45,57 @@ msgstr "" #: error.failed_to_spawn_task msgid "Failed to spawn task: {:?}" msgstr "" + +msgid "Invalid pointer provided" +msgstr "" + +msgid "Invalid UTF-8 string" +msgstr "" + +msgid "Slice conversion failed: {:?}" +msgstr "" + +msgid "Functionality not implemented" +msgstr "" + +msgid "WASM runtime initialization failed" +msgstr "" + +msgid "WASM compilation error: {}" +msgstr "" + +msgid "WASM instantiation failure: {}" +msgstr "" + +msgid "WASM instantiation error: {}" +msgstr "" + +msgid "WASM execution error: {}" +msgstr "" + +msgid "Requested function not found in module" +msgstr "" + +msgid "Memory allocation failed" +msgstr "" + +msgid "Failed to get task information: {:?}" +msgstr "" + +msgid "Mutex or lock is poisoned" +msgstr "" + +msgid "Invalid WASM module format or structure" +msgstr "" + +msgid "Internal runtime error" +msgstr "" + +msgid "Invalid thread identifier provided" +msgstr "" + +msgid "Time-related error: {:?}" +msgstr "" + +msgid "Failed to register file context" +msgstr "" diff --git a/executables/wasm/src/error.rs b/executables/wasm/src/error.rs deleted file mode 100644 index 6334b3a0..00000000 --- a/executables/wasm/src/error.rs +++ /dev/null @@ -1,63 +0,0 @@ -use core::{ - fmt, - num::{NonZeroU8, NonZeroUsize}, -}; - -use xila::{internationalization::translate, task, virtual_file_system, virtual_machine}; - -#[repr(u8)] -pub enum Error { - MissingArgument(&'static str) = 1, - FailedToGetCurrentDirectory, - InvalidPath, - NotAWasmFile, - FailedToOpenFile, - FailedToReadFile, - FailedToDuplicateStandard(virtual_file_system::Error), - FailedToTransferStandard(virtual_file_system::Error), - FailedToExecute(virtual_machine::Error), - FailedToOpenStandardFile(virtual_file_system::Error), - FailedToSpawnTask(task::Error), -} - -impl Error { - pub fn get_discriminant(&self) -> NonZeroU8 { - unsafe { *(self as *const Self as *const NonZeroU8) } - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::MissingArgument(argument) => { - write!(f, translate!("Missing argument: {}"), argument) - } - Error::FailedToGetCurrentDirectory => { - write!(f, translate!("Failed to get current directory")) - } - Error::InvalidPath => write!(f, translate!("Invalid path")), - Error::NotAWasmFile => write!(f, translate!("Not a WASM file")), - Error::FailedToOpenFile => write!(f, translate!("Failed to open file")), - Error::FailedToReadFile => write!(f, translate!("Failed to read file")), - Error::FailedToDuplicateStandard(e) => { - write!(f, translate!("Failed to duplicate standard: {:?}"), e) - } - Error::FailedToTransferStandard(e) => { - write!(f, translate!("Failed to transfer standard: {:?}"), e) - } - Error::FailedToExecute(e) => write!(f, translate!("Failed to execute: {:?}"), e), - Error::FailedToOpenStandardFile(e) => { - write!(f, translate!("Failed to open standard file: {:?}"), e) - } - Error::FailedToSpawnTask(e) => { - write!(f, translate!("Failed to spawn task: {:?}"), e) - } - } - } -} - -impl From for NonZeroUsize { - fn from(error: Error) -> Self { - error.get_discriminant().into() - } -} diff --git a/executables/wasm/src/host/mod.rs b/executables/wasm/src/host/mod.rs new file mode 100644 index 00000000..04733204 --- /dev/null +++ b/executables/wasm/src/host/mod.rs @@ -0,0 +1,184 @@ +mod bindings; +mod virtual_machine; + +use alloc::boxed::Box; +use alloc::string::ToString; +use alloc::{borrow::ToOwned, string::String, vec, vec::Vec}; +use core::fmt::Write; +use core::num::NonZeroUsize; +use core::pin::Pin; +use xila::executable::ArgumentsParser; +use xila::executable::ExecutableTrait; +use xila::executable::MainFuture; +use xila::executable::Standard; +use xila::file_system::{Kind, Path}; +use xila::synchronization::once_lock::OnceLock; +use xila::task::{self, SpawnerIdentifier}; +use xila::virtual_file_system::{self, File}; + +#[cfg(feature = "graphics")] +use crate::host::bindings::graphics::GraphicsBindings; +use crate::host::virtual_machine::{Error, Registrable}; + +pub struct WasmExecutable; + +type NewThreadExecutor = + fn() -> Pin + Send>>; + +static NEW_THREAD_EXECUTOR: OnceLock = OnceLock::new(); +static RUNTIME: OnceLock = OnceLock::new(); +const REGISTRABLES: &[&dyn Registrable] = &[ + #[cfg(feature = "graphics")] + &GraphicsBindings, +]; +const START_FUNCTION_NAME: &str = "_start"; +const INSTALL_FUNCTION_NAME: &str = "__install"; +const DEFAULT_STACK_SIZE: usize = 4096; + +impl WasmExecutable { + pub fn new(new_thread_executor: Option) -> Self { + if let Some(new_thread_executor) = new_thread_executor { + let _ = NEW_THREAD_EXECUTOR.init(new_thread_executor); + } + + Self + } +} + +impl ExecutableTrait for WasmExecutable { + fn main(standard: Standard, arguments: Vec) -> MainFuture { + Box::pin(async move { main(standard, arguments).await }) + } +} + +pub async fn inner_main(standard: Standard, arguments: Vec) -> Result<(), Error> { + let parsed_arguments = ArgumentsParser::new(&arguments); + + let install = parsed_arguments + .clone() + .any(|a| a.options.get_option("install").is_some()); + let stack_size = parsed_arguments + .clone() + .find_map(|a| { + a.options + .get_option("stack-size") + .and_then(|s| s.parse::().ok()) + }) + .unwrap_or(DEFAULT_STACK_SIZE); + let path = parsed_arguments + .last() + .and_then(|arg| arg.value.map(Path::new)) + .ok_or(Error::MissingArgument("path"))?; + + let task = task::get_instance().get_current_task_identifier().await; + + let path = if path.is_absolute() { + path.to_owned() + } else { + let current_path = task::get_instance() + .get_environment_variable(task, "Current_directory") + .await + .map_err(|_| Error::FailedToGetCurrentDirectory)?; + + let current_path = current_path.get_value(); + + let current_path = Path::new(current_path); + + current_path.join(path).ok_or(Error::InvalidPath)? + }; + + let virtual_file_system = virtual_file_system::get_instance(); + + let statistics = virtual_file_system + .get_statistics(&path) + .await + .map_err(|_| Error::InvalidPath)?; + + if statistics.kind != Kind::File { + return Err(Error::NotAWasmFile); + } + + let mut buffer = Vec::with_capacity(statistics.size as usize); + + File::read_from_path(virtual_file_system, task, &path, &mut buffer) + .await + .map_err(|_| Error::FailedToReadFile)?; + + let name = path.get_file_name().to_string(); + + let function_name = if install { + INSTALL_FUNCTION_NAME + } else { + START_FUNCTION_NAME + }; + + let runtime = RUNTIME + .get_or_init(|| virtual_machine::Runtime::new(REGISTRABLES.iter().copied()).unwrap()); + + let standard = standard.split(); + + if let Some(new_thread_executor) = NEW_THREAD_EXECUTOR.try_get() { + let spawner_identifier = new_thread_executor().await; + + task::get_instance() + .spawn( + task, + "WASM Execution", + Some(spawner_identifier), + move |task_identifier| async move { + runtime + .execute( + &name, + buffer, + stack_size, + standard, + function_name, + vec![], + task_identifier, + ) + .await + }, + ) + .await + .map_err(Error::FailedToSpawnTask)? + .0 + .join() + .await?; + } else { + let task_identifier = task::get_instance().get_current_task_identifier().await; + + runtime + .execute( + &name, + buffer, + stack_size, + standard, + function_name, + vec![], + task_identifier, + ) + .await?; + } + + Ok(()) +} + +pub async fn main(standard: Standard, arguments: Vec) -> Result<(), NonZeroUsize> { + let mut duplicated_standard = standard + .duplicate() + .await + .map_err(Error::FailedToDuplicateStandard)?; + + match inner_main(standard, arguments).await { + Ok(()) => Ok(()), + Err(error) => { + writeln!( + duplicated_standard.standard_error, + "WASM Executable Error: {}", + error + ) + .unwrap(); + Err(error.into()) + } + } +} diff --git a/executables/wasm/src/host/virtual_machine/custom_data.rs b/executables/wasm/src/host/virtual_machine/custom_data.rs new file mode 100644 index 00000000..fdfc425b --- /dev/null +++ b/executables/wasm/src/host/virtual_machine/custom_data.rs @@ -0,0 +1,10 @@ +use core::ffi::c_void; + +use xila::shared::BijectiveBTreeMap; + +use crate::host::virtual_machine::WasmPointer; + +#[derive(Debug, Clone, Default)] +pub struct CustomData { + pub translation_map: BijectiveBTreeMap, +} diff --git a/executables/wasm/src/host/virtual_machine/environment.rs b/executables/wasm/src/host/virtual_machine/environment.rs new file mode 100644 index 00000000..6f26ce67 --- /dev/null +++ b/executables/wasm/src/host/virtual_machine/environment.rs @@ -0,0 +1,78 @@ +use crate::host::virtual_machine::{Result, WasmPointer}; +use alloc::boxed::Box; +use core::ffi::c_void; +use wamr_rust_sdk::sys::{ + WASMExecEnv, wasm_exec_env_t, wasm_module_inst_t, wasm_runtime_addr_app_to_native, + wasm_runtime_addr_native_to_app, wasm_runtime_get_custom_data, wasm_runtime_get_module_inst, + wasm_runtime_set_custom_data, +}; + +pub type EnvironmentPointer = wasm_exec_env_t; + +#[repr(transparent)] +pub struct Environment(WASMExecEnv); + +impl Environment { + pub unsafe fn from_raw_pointer<'a>(raw_pointer: *mut WASMExecEnv) -> &'a mut Self { + unsafe { &mut *(raw_pointer as *mut Self) } + } + + pub unsafe fn get_or_initialize_custom_data<'a, 'b, T: Default>( + &'a mut self, + ) -> Result<&'b mut T> { + unsafe { + let custom_data = wasm_runtime_get_custom_data(self.get_instance_pointer()) as *mut T; + + let custom_data = if custom_data.is_null() { + let custom_data = Box::new(T::default()); + + wasm_runtime_set_custom_data( + self.get_instance_pointer(), + Box::into_raw(custom_data) as *mut c_void, + ); + + wasm_runtime_get_custom_data(self.get_instance_pointer()) as *mut T + } else { + custom_data + }; + + Ok(&mut *custom_data) + } + } + + /// # Safety + /// + /// This function is unsafe because it is not checked that the address is valid. + /// Please use `Validate_WASM_pointer` to check the address. + pub unsafe fn translate_to_host(&mut self, address: WasmPointer) -> Option<*mut T> { + unsafe { + let pointer = + wasm_runtime_addr_app_to_native(self.get_instance_pointer(), address as u64); + + if pointer.is_null() { + return None; + } + + if (pointer as usize) % core::mem::align_of::() != 0 { + return None; + } + + Some(pointer as *mut T) + } + } + + /// # Safety + /// + /// This function is unsafe because it is not checked that the address is valid. + /// Please use `Validate_WASM_pointer` to check the address. + pub unsafe fn translate_to_guest(&mut self, pointer: *mut T) -> WasmPointer { + unsafe { + wasm_runtime_addr_native_to_app(self.get_instance_pointer(), pointer as *mut c_void) + as WasmPointer + } + } + + fn get_instance_pointer(&mut self) -> wasm_module_inst_t { + unsafe { wasm_runtime_get_module_inst(&mut self.0) } + } +} diff --git a/executables/wasm/src/host/virtual_machine/error.rs b/executables/wasm/src/host/virtual_machine/error.rs new file mode 100644 index 00000000..0e69893d --- /dev/null +++ b/executables/wasm/src/host/virtual_machine/error.rs @@ -0,0 +1,182 @@ +//! Error types and result handling for the Virtual Machine module. +//! +//! This module defines comprehensive error types that can occur during WASM module +//! loading, compilation, instantiation, and execution. + +use alloc::string::String; +use core::{ + fmt::{self}, + num::{NonZeroU8, NonZeroUsize}, +}; +use wamr_rust_sdk::RuntimeError; +use xila::{internationalization::translate, shared, task, time, virtual_file_system}; + +/// Result type alias for Virtual Machine operations +pub type Result = core::result::Result; + +/// Comprehensive error types for Virtual Machine operations +/// +/// This enum covers all possible error conditions that can occur during +/// WASM module lifecycle operations, from loading to execution. +#[derive(Debug)] +#[repr(C)] +pub enum Error { + MissingArgument(&'static str), + FailedToGetCurrentDirectory, + InvalidPath, + NotAWasmFile, + FailedToOpenFile, + FailedToReadFile, + FailedToDuplicateStandard(virtual_file_system::Error), + FailedToTransferStandard(virtual_file_system::Error), + FailedToOpenStandardFile(virtual_file_system::Error), + FailedToSpawnTask(task::Error), + + /// Invalid pointer provided to a function + InvalidPointer, + + /// String contains invalid UTF-8 sequences + InvalidUtf8String, + + /// Failed to convert between slice types + SliceConversionFailed(shared::Error), + + /// Requested functionality is not yet implemented + NotImplemented, + + /// WASM runtime initialization failed + InitializationFailure, + + /// WASM module compilation failed with detailed error message + CompilationError(String), + + /// WASM module instantiation failed with detailed error message + InstantiationFailure(String), + + /// WASM function execution failed with detailed error message + ExecutionError(String), + + /// Requested function was not found in the module + FunctionNotFound, + + /// Memory allocation failed + AllocationFailure, + + /// Failed to retrieve task information + FailedToGetTaskInformations(task::Error), + + /// Mutex or lock was poisoned + PoisonedLock, + + /// Invalid WASM module format or structure + InvalidModule, + + /// Internal runtime error + InternalError, + + /// Invalid thread identifier provided + InvalidThreadIdentifier, + + /// Time-related operation failed + Time(time::Error), + + /// Failed to transfert file identifiers + FailedToRegisterFileContext, +} + +impl Error { + pub fn get_discriminant(&self) -> NonZeroU8 { + unsafe { *(self as *const Self as *const NonZeroU8) } + } +} + +impl core::error::Error for Error {} + +impl From for Error { + fn from(error: RuntimeError) -> Self { + match error { + RuntimeError::NotImplemented => Error::NotImplemented, + RuntimeError::InitializationFailure => Error::InitializationFailure, + RuntimeError::WasmFileFSError(_) => Error::InitializationFailure, + RuntimeError::CompilationError(e) => Error::CompilationError(e), + RuntimeError::InstantiationFailure(e) => Error::InstantiationFailure(e), + RuntimeError::ExecutionError(e) => Error::ExecutionError(e.message), + RuntimeError::FunctionNotFound => Error::FunctionNotFound, + } + } +} +impl From for Error { + fn from(error: task::Error) -> Self { + Error::FailedToGetTaskInformations(error) + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::MissingArgument(argument) => { + write!(f, translate!("Missing argument: {}"), argument) + } + Error::FailedToGetCurrentDirectory => { + write!(f, translate!("Failed to get current directory")) + } + Error::InvalidPath => write!(f, translate!("Invalid path")), + Error::NotAWasmFile => write!(f, translate!("Not a WASM file")), + Error::FailedToOpenFile => write!(f, translate!("Failed to open file")), + Error::FailedToReadFile => write!(f, translate!("Failed to read file")), + Error::FailedToDuplicateStandard(e) => { + write!(f, translate!("Failed to duplicate standard: {:?}"), e) + } + Error::FailedToTransferStandard(e) => { + write!(f, translate!("Failed to transfer standard: {:?}"), e) + } + Error::FailedToOpenStandardFile(e) => { + write!(f, translate!("Failed to open standard file: {:?}"), e) + } + Error::FailedToSpawnTask(e) => { + write!(f, translate!("Failed to spawn task: {:?}"), e) + } + Error::InvalidPointer => write!(f, translate!("Invalid pointer provided")), + Error::InvalidUtf8String => write!(f, translate!("Invalid UTF-8 string")), + Error::SliceConversionFailed(e) => { + write!(f, translate!("Slice conversion failed: {:?}"), e) + } + Error::NotImplemented => write!(f, translate!("Functionality not implemented")), + Error::InitializationFailure => { + write!(f, translate!("WASM runtime initialization failed")) + } + Error::CompilationError(e) => write!(f, translate!("WASM compilation error: {}"), e), + Error::ExecutionError(e) => write!(f, translate!("WASM execution error: {}"), e), + Error::FunctionNotFound => { + write!(f, translate!("Requested function not found in module")) + } + Error::AllocationFailure => write!(f, translate!("Memory allocation failed")), + Error::FailedToGetTaskInformations(e) => { + write!(f, translate!("Failed to get task information: {:?}"), e) + } + Error::PoisonedLock => write!(f, translate!("Mutex or lock is poisoned")), + Error::InvalidModule => { + write!(f, translate!("Invalid WASM module format or structure")) + } + Error::InternalError => write!(f, translate!("Internal runtime error")), + Error::InvalidThreadIdentifier => { + write!(f, translate!("Invalid thread identifier provided")) + } + Error::Time(e) => { + write!(f, translate!("Time-related error: {:?}"), e) + } + Error::FailedToRegisterFileContext => { + write!(f, translate!("Failed to register file context")) + } + Error::InstantiationFailure(e) => { + write!(f, translate!("WASM instantiation error: {}"), e) + } + } + } +} + +impl From for NonZeroUsize { + fn from(error: Error) -> Self { + error.get_discriminant().into() + } +} diff --git a/executables/wasm/src/host/virtual_machine/instance.rs b/executables/wasm/src/host/virtual_machine/instance.rs new file mode 100644 index 00000000..42a9629b --- /dev/null +++ b/executables/wasm/src/host/virtual_machine/instance.rs @@ -0,0 +1,68 @@ +use crate::host::virtual_machine::{CustomData, Result, module::Module, runtime::Runtime}; +use alloc::{boxed::Box, vec, vec::Vec}; +use wamr_rust_sdk::{ + function::Function, instance, sys::wasm_runtime_get_custom_data, value::WasmValue, +}; + +pub struct Instance<'module> { + instance: instance::Instance<'module>, +} + +unsafe impl Send for Instance<'_> {} + +impl Drop for Instance<'_> { + fn drop(&mut self) { + let instance = self.get_inner_reference().get_inner_instance(); + unsafe { + let user_data = wasm_runtime_get_custom_data(instance) as *mut CustomData; + + if !user_data.is_null() { + let _ = Box::from_raw(user_data); + } + } + + // User data is dropped here. + } +} + +impl<'module> Instance<'module> { + pub fn new( + runtime: &Runtime, + module: &'module Module<'module>, + stack_size: usize, + ) -> Result { + let wamr_instance = instance::Instance::new( + runtime.get_inner_reference(), + module.get_inner_reference(), + stack_size as u32, + )?; + + let instance = Instance { + instance: wamr_instance, + }; + + Ok(instance) + } + + pub fn call_exported_function( + &self, + name: &str, + parameters: &Vec, + ) -> Result> { + if parameters.is_empty() { + Ok( + Function::find_export_func(self.get_inner_reference(), name)? + .call(&self.instance, &vec![WasmValue::I32(0)])?, + ) + } else { + Ok( + Function::find_export_func(self.get_inner_reference(), name)? + .call(&self.instance, parameters)?, + ) + } + } + + pub fn get_inner_reference(&'_ self) -> &'_ instance::Instance<'_> { + &self.instance + } +} diff --git a/executables/wasm/src/host/virtual_machine/mod.rs b/executables/wasm/src/host/virtual_machine/mod.rs new file mode 100644 index 00000000..fd9eb132 --- /dev/null +++ b/executables/wasm/src/host/virtual_machine/mod.rs @@ -0,0 +1,31 @@ +mod custom_data; +mod environment; +mod error; +mod instance; +mod module; +mod registrable; +mod runtime; +mod translation; + +// Re-export all public types from modules +pub use custom_data::*; +pub use environment::*; +pub use error::*; +pub use instance::*; +pub use module::*; +pub use registrable::*; +pub use runtime::*; +pub use translation::*; + +#[cfg(feature = "wasm32")] +/// Type alias for WASM pointer addresses in the 32-bit WASM address space +pub type WasmPointer = u32; +#[cfg(feature = "wasm64")] +pub type WasmPointer = u64; + +#[cfg(feature = "wasm32")] +/// Type alias for WASM size values (32-bit) +pub type WasmUsize = u32; + +#[cfg(feature = "wasm64")] +pub type WasmUsize = u64; diff --git a/modules/virtual_machine/src/module.rs b/executables/wasm/src/host/virtual_machine/module.rs similarity index 95% rename from modules/virtual_machine/src/module.rs rename to executables/wasm/src/host/virtual_machine/module.rs index a0938783..80923126 100644 --- a/modules/virtual_machine/src/module.rs +++ b/executables/wasm/src/host/virtual_machine/module.rs @@ -1,10 +1,9 @@ use core::{ffi::CStr, ptr::null_mut}; -use abi_context::FileIdentifier; +use crate::host::virtual_machine::{Error, Result, runtime::Runtime}; use alloc::vec::Vec; use wamr_rust_sdk::{module, sys::wasm_runtime_set_wasi_args_ex}; - -use crate::{Error, Result, runtime::Runtime}; +use xila::{abi_context::FileIdentifier, task}; pub struct Module<'runtime> { module: module::Module<'runtime>, diff --git a/executables/wasm/src/host/virtual_machine/registrable.rs b/executables/wasm/src/host/virtual_machine/registrable.rs new file mode 100644 index 00000000..7bc97804 --- /dev/null +++ b/executables/wasm/src/host/virtual_machine/registrable.rs @@ -0,0 +1,11 @@ +use core::ffi::c_void; + +pub type FunctionPointer = *mut c_void; + +pub struct FunctionDescriptor { + pub name: &'static str, + pub pointer: FunctionPointer, +} +pub trait Registrable { + fn get_functions(&self) -> &[FunctionDescriptor]; +} diff --git a/executables/wasm/src/host/virtual_machine/runtime.rs b/executables/wasm/src/host/virtual_machine/runtime.rs new file mode 100644 index 00000000..0cd4d18a --- /dev/null +++ b/executables/wasm/src/host/virtual_machine/runtime.rs @@ -0,0 +1,119 @@ +//! WASM Runtime management and configuration. +//! +//! This module provides a wrapper around the WAMR runtime with a builder pattern +//! for configuring and creating runtime instances with registered host functions. + +use crate::host::virtual_machine::{Error, Instance, Module, Registrable, Result}; +use alloc::vec::Vec; +use wamr_rust_sdk::value::WasmValue; +use xila::{ + abi_context::{self, FileIdentifier}, + task::TaskIdentifier, + virtual_file_system::File, +}; + +pub struct Runtime(wamr_rust_sdk::runtime::Runtime); + +unsafe impl Send for Runtime {} +unsafe impl Sync for Runtime {} + +impl Runtime { + pub fn new<'a>(registrables: impl IntoIterator) -> Result { + let mut runtime_builder = wamr_rust_sdk::runtime::Runtime::builder().use_system_allocator(); + + for registrable in registrables { + for function_descriptor in registrable.get_functions() { + runtime_builder = runtime_builder + .register_host_function(function_descriptor.name, function_descriptor.pointer); + } + } + + let runtime = runtime_builder.build()?; + + Ok(Self(runtime)) + } + + pub fn get_inner_reference(&self) -> &wamr_rust_sdk::runtime::Runtime { + &self.0 + } + + /// Execute a WASM module with the specified I/O configuration. + /// + /// This is the main entry point for executing WASM modules. It creates a new + /// module instance, sets up the execution environment with proper I/O redirection, + /// and calls the module's main function. + /// + /// # Arguments + /// + /// * `Buffer` - The WASM module bytecode to execute + /// * `Stack_size` - Stack size in bytes for the WASM instance + /// * `Standard_in` - File identifier for standard input + /// * `Standard_out` - File identifier for standard output + /// * `Standard_error` - File identifier for standard error + /// + /// # Returns + /// + /// The return values from the WASM module's main function + /// + /// # Errors + /// + /// Returns an error if module loading, instantiation, or execution fails + pub async fn execute( + &'static self, + name: &str, + buffer: Vec, + stack_size: usize, + (standard_in, standard_out, standard_error): (File, File, File), + function_name: &str, + mut function_arguments: Vec, + task: TaskIdentifier, + ) -> Result> { + let abi_context = abi_context::get_instance(); + + if function_arguments.is_empty() { + function_arguments.push(WasmValue::I32(0)); + } + + abi_context + .call_abi(async || { + let standard_in = abi_context + .insert_file( + task, + standard_in.into_synchronous_file(), + Some(FileIdentifier::STANDARD_IN), + ) + .ok_or(Error::FailedToRegisterFileContext)?; + let standard_out = abi_context + .insert_file( + task, + standard_out.into_synchronous_file(), + Some(FileIdentifier::STANDARD_OUT), + ) + .ok_or(Error::FailedToRegisterFileContext)?; + let standard_error = abi_context + .insert_file( + task, + standard_error.into_synchronous_file(), + Some(FileIdentifier::STANDARD_ERROR), + ) + .ok_or(Error::FailedToRegisterFileContext)?; + + let module = Module::from_buffer( + &self, + buffer, + name, + standard_in, + standard_out, + standard_error, + ) + .await?; + + let instance = Instance::new(&self, &module, stack_size as _)?; + + let result = instance.call_exported_function(function_name, &function_arguments)?; + + Ok(result) + }) + .await + } +} diff --git a/executables/wasm/src/host/virtual_machine/translation.rs b/executables/wasm/src/host/virtual_machine/translation.rs new file mode 100644 index 00000000..a73a436d --- /dev/null +++ b/executables/wasm/src/host/virtual_machine/translation.rs @@ -0,0 +1,79 @@ +use core::ffi::c_void; + +use crate::host::virtual_machine::{CustomData, Environment, Result, WasmPointer}; + +pub struct Translator<'a> { + environment: &'a mut Environment, + custom_data: &'a mut CustomData, +} + +impl<'a> Translator<'a> { + pub unsafe fn from_environment(environment: &'a mut Environment) -> Result { + let custom_data = unsafe { environment.get_or_initialize_custom_data()? }; + + Ok(Self { + environment, + custom_data, + }) + } + + pub fn add_host_translation(&mut self, host_address: *mut T) -> WasmPointer { + let guest_address = { + let mut next_id = 1; // Start from your preferred minimum (0 or 1) + + for &id in self.custom_data.translation_map.get_left_keys() { + if id > next_id { + // We found a gap! + return next_id; + } + next_id = id + 1; + } + + next_id + }; + + self.custom_data + .translation_map + .insert(guest_address, host_address as *mut c_void); + + guest_address + } + + pub fn remove_host_translation(&mut self, guest_address: WasmPointer) -> Option<*mut T> { + self.custom_data + .translation_map + .remove_by_key(&guest_address) + .map(|ptr| ptr as *mut T) + } + + pub unsafe fn translate_to_host( + &mut self, + wasm_address: WasmPointer, + owned_by_guest: bool, + ) -> Option<*mut T> { + if owned_by_guest { + unsafe { self.environment.translate_to_host(wasm_address) } + } else { + self.custom_data + .translation_map + .get_by_left(&wasm_address) + .copied() + .map(|ptr| ptr as *mut T) + } + } + + pub unsafe fn translate_to_guest( + &mut self, + host_address: *mut T, + owned_by_guest: bool, + ) -> Option { + if owned_by_guest { + unsafe { Some(self.environment.translate_to_guest(host_address)) } + } else { + self.custom_data + .translation_map + .get_by_right(&(host_address as *mut c_void)) + .copied() + } + } +} diff --git a/executables/wasm/src/lib.rs b/executables/wasm/src/lib.rs index 2bbb6eec..7a1eba6e 100644 --- a/executables/wasm/src/lib.rs +++ b/executables/wasm/src/lib.rs @@ -1,176 +1,15 @@ #![no_std] -#![cfg(not(target_arch = "wasm32"))] - -mod error; extern crate alloc; -use crate::Error; -use alloc::boxed::Box; -use alloc::{borrow::ToOwned, string::String, vec, vec::Vec}; -use core::fmt::Write; -use core::num::NonZeroUsize; -use core::pin::Pin; -use xila::executable::ArgumentsParser; -use xila::executable::ExecutableTrait; -use xila::executable::MainFuture; -use xila::executable::Standard; -use xila::file_system::{Kind, Path}; -use xila::synchronization::once_lock::OnceLock; -use xila::task::{self, SpawnerIdentifier}; -use xila::virtual_file_system::{self, File}; -use xila::virtual_machine; - -pub use error::*; - -pub struct WasmExecutable; - -type NewThreadExecutor = - fn() -> Pin + Send>>; - -static NEW_THREAD_EXECUTOR: OnceLock = OnceLock::new(); - -const DEFAULT_STACK_SIZE: usize = 4096; - -impl WasmExecutable { - pub fn new(new_thread_executor: Option) -> Self { - if let Some(new_thread_executor) = new_thread_executor { - let _ = NEW_THREAD_EXECUTOR.init(new_thread_executor); - } - - Self - } -} - -impl ExecutableTrait for WasmExecutable { - fn main(standard: Standard, arguments: Vec) -> MainFuture { - Box::pin(async move { main(standard, arguments).await }) - } -} - -pub async fn inner_main(standard: Standard, arguments: Vec) -> Result<(), Error> { - let parsed_arguments = ArgumentsParser::new(&arguments); - - let install = parsed_arguments - .clone() - .any(|a| a.options.get_option("install").is_some()); - let stack_size = parsed_arguments - .clone() - .find_map(|a| { - a.options - .get_option("stack-size") - .and_then(|s| s.parse::().ok()) - }) - .unwrap_or(DEFAULT_STACK_SIZE); - let path = parsed_arguments - .last() - .and_then(|arg| arg.value.map(Path::new)) - .ok_or(Error::MissingArgument("path"))?; - - let task = task::get_instance().get_current_task_identifier().await; - - let path = if path.is_absolute() { - path.to_owned() - } else { - let current_path = task::get_instance() - .get_environment_variable(task, "Current_directory") - .await - .map_err(|_| Error::FailedToGetCurrentDirectory)?; - - let current_path = current_path.get_value(); - - let current_path = Path::new(current_path); - - current_path.join(path).ok_or(Error::InvalidPath)? - }; - - let virtual_file_system = virtual_file_system::get_instance(); - - let statistics = virtual_file_system - .get_statistics(&path) - .await - .map_err(|_| Error::InvalidPath)?; - - if statistics.kind != Kind::File { - return Err(Error::NotAWasmFile); - } - - let mut buffer = Vec::with_capacity(statistics.size as usize); - - File::read_from_path(virtual_file_system, task, path, &mut buffer) - .await - .map_err(|_| Error::FailedToReadFile)?; - - let function_name = if install { Some("__install") } else { None }; - - if let Some(new_thread_executor) = NEW_THREAD_EXECUTOR.try_get() { - let spawner_identifier = new_thread_executor().await; - - task::get_instance() - .spawn( - task, - "WASM Execution", - Some(spawner_identifier), - move |task_identifier| async move { - let standards = standard.split(); - - let result = virtual_machine::get_instance() - .execute( - buffer, - stack_size, - standards, - function_name, - vec![], - task_identifier, - ) - .await - .map_err(Error::FailedToExecute)?; - - Ok(result) - }, - ) - .await - .map_err(Error::FailedToSpawnTask)? - .0 - .join() - .await?; - } else { - let standards = standard.split(); - - let task_identifier = task::get_instance().get_current_task_identifier().await; - - virtual_machine::get_instance() - .execute( - buffer, - stack_size, - standards, - function_name, - vec![], - task_identifier, - ) - .await - .map_err(Error::FailedToExecute)?; - } +#[cfg(feature = "host")] +mod host; - Ok(()) -} +#[cfg(feature = "host")] +pub use host::*; -pub async fn main(standard: Standard, arguments: Vec) -> Result<(), NonZeroUsize> { - let mut duplicated_standard = standard - .duplicate() - .await - .map_err(Error::FailedToDuplicateStandard)?; +#[cfg(feature = "guest")] +mod guest; - match inner_main(standard, arguments).await { - Ok(()) => Ok(()), - Err(error) => { - writeln!( - duplicated_standard.standard_error, - "WASM Executable Error: {}", - error - ) - .unwrap(); - Err(error.into()) - } - } -} +#[cfg(feature = "guest")] +pub use guest::*; diff --git a/executables/wasm/src/locales.toml b/executables/wasm/src/locales.toml deleted file mode 100644 index 1660a66e..00000000 --- a/executables/wasm/src/locales.toml +++ /dev/null @@ -1,43 +0,0 @@ -[error.missing_argument] -fr = "Argument manquant: {}" -en = "Missing argument: {}" - -[error.failed_to_get_current_directory] -fr = "Échec de la récupération du répertoire actuel" -en = "Failed to get current directory" - -[error.invalid_path] -fr = "Chemin invalide" -en = "Invalid path" - -[error.not_a_wasm_file] -fr = "Pas un fichier WASM" -en = "Not a WASM file" - -[error.failed_to_open_file] -fr = "Échec de l'ouverture du fichier" -en = "Failed to open file" - -[error.failed_to_read_file] -fr = "Échec de la lecture du fichier" -en = "Failed to read file" - -[error.failed_to_duplicate_standard] -fr = "Échec de la duplication du standard: {:?}" -en = "Failed to duplicate standard: {:?}" - -[error.failed_to_transfer_standard] -fr = "Échec du transfert du standard: {:?}" -en = "Failed to transfer standard: {:?}" - -[error.failed_to_execute] -fr = "Échec de l'exécution: {:?}" -en = "Failed to execute: {:?}" - -[error.failed_to_open_standard_file] -fr = "Échec de l'ouverture du fichier standard: {:?}" -en = "Failed to open standard file: {:?}" - -[error.failed_to_spawn_task] -fr = "Échec de la création de la tâche: {:?}" -en = "Failed to spawn task: {:?}" diff --git a/executables/wasm/tests/integration_test.rs b/executables/wasm/tests/integration_test.rs index 61e5423d..5779bfb0 100644 --- a/executables/wasm/tests/integration_test.rs +++ b/executables/wasm/tests/integration_test.rs @@ -1,5 +1,5 @@ #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] -#[ignore] +#[cfg(all(feature = "host", feature = "graphics"))] #[xila::task::test(task_path = xila::task)] async fn main() { drivers_std::memory::instantiate_global_allocator!(); @@ -13,16 +13,13 @@ async fn main() { use xila::executable::{build_crate, mount_executables}; use xila::task; use xila::virtual_file_system; - use xila::virtual_machine; - let standard = testing::initialize(false, false).await; + let standard = testing::initialize(true, false).await; let virtual_file_system = virtual_file_system::get_instance(); let task_instance = task::get_instance(); let task = task_instance.get_current_task_identifier().await; - let _ = virtual_machine::initialize(&[]); - let binary_path = build_crate(&"wasm_wasm_test").unwrap(); load_to_virtual_file_system(virtual_file_system, binary_path, "/test_wasm.wasm") .await diff --git a/executables/wasm/tests/wasm_test/Cargo.toml b/executables/wasm/tests/wasm_test/Cargo.toml index d71886dc..36690336 100644 --- a/executables/wasm/tests/wasm_test/Cargo.toml +++ b/executables/wasm/tests/wasm_test/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2024" [dependencies] +wasm = { workspace = true, features = ["guest"] } diff --git a/executables/wasm/tests/wasm_test/src/main.rs b/executables/wasm/tests/wasm_test/src/main.rs index 8503ebed..d9117c64 100644 --- a/executables/wasm/tests/wasm_test/src/main.rs +++ b/executables/wasm/tests/wasm_test/src/main.rs @@ -1,3 +1,166 @@ -fn main() { - println!("Hello, world from WASM!"); +use std::{ + fs::{OpenOptions, create_dir, read_dir, rename}, + io::{Read, Write}, +}; + +#[unsafe(export_name = "gcd")] +pub fn gcd(mut a: i32, mut b: i32) -> i32 { + while b != 0 { + let t = b; + b = a % b; + a = t; + } + a +} + +fn test_stdio() -> Result<(), ()> { + println!("Test stdout"); + eprintln!("Test stderr"); + + let mut input = String::new(); + + std::io::stdin().read_line(&mut input).unwrap(); + + println!("Input: {}", input); + + Ok(()) +} + +fn test_file() { + println!("Testing file operations..."); + + { + let mut file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open("/test.txt") + .unwrap(); + + println!("Writing to file..."); + + file.write_all(b"Hello World from WASM!").unwrap(); + } + + println!("File written successfully."); + + { + let mut file = OpenOptions::new().read(true).open("/test.txt").unwrap(); + + let mut string = String::new(); + + file.read_to_string(&mut string).unwrap(); + + assert_eq!(string, "Hello World from WASM!"); + } + + println!("File read successfully."); + + { + rename("/test.txt", "/test2.txt").unwrap(); + + let mut file = OpenOptions::new().read(true).open("/test2.txt").unwrap(); + + let mut string = String::new(); + + file.read_to_string(&mut string).unwrap(); + + assert_eq!(string, "Hello World from WASM!"); + } + + println!("File renamed and read successfully."); +} + +fn test_environment_variables() { + println!("Environment variables:"); + + let environment = std::env::vars(); + + for (key, value) in environment { + println!("{}: {}", key, value); + } +} + +fn test_directory() { + println!("Testing directory operations..."); + + create_dir("/test_dir").unwrap(); + + println!("Directory created successfully."); + + { + let mut file = OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open("/test_dir/file1.txt") + .unwrap(); + + file.write_all(b"File 1 in directory").unwrap(); + } + + { + let mut file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open("/test_dir/file2.txt") + .unwrap(); + + file.write_all(b"File 2 in directory").unwrap(); + } + + for entry in read_dir("/").unwrap() { + let entry = entry.unwrap(); + + let r#type = entry.file_type().unwrap(); + + let r#type = if r#type.is_dir() { + "Directory" + } else if r#type.is_file() { + "File" + } else if r#type.is_symlink() { + "Symlink" + } else { + "Unknown" + }; + + println!("{:?} - {}", entry.file_name(), r#type); + } +} + +/// Allocate memory +/// +/// # Safety +/// +/// This function is unsafe because it may return an invalid pointer. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn Allocate(size: usize) -> *mut u8 { + let layout = std::alloc::Layout::from_size_align(size, std::mem::size_of::()).unwrap(); + + unsafe { std::alloc::alloc(layout) } +} + +/// Deallocate memory +/// +/// # Safety +/// +/// This function is unsafe because it may cause undefined behavior if the pointer is invalid. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn Deallocate(pointer: *mut u8, size: usize) { + let layout = std::alloc::Layout::from_size_align(size, std::mem::size_of::()).unwrap(); + + unsafe { std::alloc::dealloc(pointer, layout) } +} + +fn main() -> Result<(), ()> { + test_stdio()?; + + test_file(); + + test_directory(); + + test_environment_variables(); + + Ok(()) } diff --git a/modules/virtual_machine/wamr/include/internal.h b/executables/wasm/wamr/include/internal.h similarity index 94% rename from modules/virtual_machine/wamr/include/internal.h rename to executables/wasm/wamr/include/internal.h index 5d8c7987..057738eb 100644 --- a/modules/virtual_machine/wamr/include/internal.h +++ b/executables/wasm/wamr/include/internal.h @@ -1,4 +1,4 @@ -#include "../../../abi/xila.h" +#include "../../../../modules/abi/xila.h" #include "platform_api_extension.h" #include "platform_common.h" diff --git a/modules/virtual_machine/wamr/include/platform_internal.h b/executables/wasm/wamr/include/platform_internal.h similarity index 98% rename from modules/virtual_machine/wamr/include/platform_internal.h rename to executables/wasm/wamr/include/platform_internal.h index d2a57113..7851eae0 100644 --- a/modules/virtual_machine/wamr/include/platform_internal.h +++ b/executables/wasm/wamr/include/platform_internal.h @@ -6,7 +6,7 @@ #ifndef _XILA_PLATFORM_INTERNAL_H #define _XILA_PLATFORM_INTERNAL_H -#include "../../../abi/xila.h" +#include "../../../../modules/abi/xila.h" //#include "stubs.h" #include diff --git a/modules/virtual_machine/wamr/include/stubs.h b/executables/wasm/wamr/include/stubs.h similarity index 93% rename from modules/virtual_machine/wamr/include/stubs.h rename to executables/wasm/wamr/include/stubs.h index 919f2467..79fc1bf8 100644 --- a/modules/virtual_machine/wamr/include/stubs.h +++ b/executables/wasm/wamr/include/stubs.h @@ -1,5 +1,5 @@ -#include "../../../abi/xila.h" +#include "../../../../modules/abi/xila.h" #define CLOCK_REALTIME XilaTimeClockIdentifier_Realtime #define CLOCK_MONOTONIC XilaTimeClockIdentifier_Monotonic diff --git a/modules/virtual_machine/wamr/shared_platform.cmake b/executables/wasm/wamr/shared_platform.cmake similarity index 100% rename from modules/virtual_machine/wamr/shared_platform.cmake rename to executables/wasm/wamr/shared_platform.cmake diff --git a/modules/virtual_machine/wamr/src/core.c b/executables/wasm/wamr/src/core.c similarity index 99% rename from modules/virtual_machine/wamr/src/core.c rename to executables/wasm/wamr/src/core.c index 40374c93..cda29adb 100644 --- a/modules/virtual_machine/wamr/src/core.c +++ b/executables/wasm/wamr/src/core.c @@ -1,4 +1,4 @@ -#include "../../../abi/xila.h" +#include "../../../../modules/abi/xila.h" #include "platform_api_vmcore.h" /**************************************************** diff --git a/modules/virtual_machine/wamr/src/extension.c b/executables/wasm/wamr/src/extension.c similarity index 99% rename from modules/virtual_machine/wamr/src/extension.c rename to executables/wasm/wamr/src/extension.c index 3bf13984..df073b48 100644 --- a/modules/virtual_machine/wamr/src/extension.c +++ b/executables/wasm/wamr/src/extension.c @@ -1,4 +1,4 @@ -#include "../../../abi/xila.h" +#include "../../../../modules/abi/xila.h" #include "../include/internal.h" #include "platform_internal.h" diff --git a/modules/virtual_machine/wamr/src/internal.c b/executables/wasm/wamr/src/internal.c similarity index 98% rename from modules/virtual_machine/wamr/src/internal.c rename to executables/wasm/wamr/src/internal.c index 0c667b71..031a1c12 100644 --- a/modules/virtual_machine/wamr/src/internal.c +++ b/executables/wasm/wamr/src/internal.c @@ -1,4 +1,4 @@ -#include "../../../abi/xila.h" +#include "../../../../modules/abi/xila.h" #include "platform_api_extension.h" #include "platform_common.h" diff --git a/modules/virtual_machine/Cargo.toml b/modules/virtual_machine/Cargo.toml deleted file mode 100644 index dd7dfcee..00000000 --- a/modules/virtual_machine/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -name = "virtual_machine" -version = "0.1.0" -edition = "2024" - -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -wamr-rust-sdk = { git = "https://github.com/bytecodealliance/wamr-rust-sdk.git", features = [ - "multi-module", -] } - -shared = { workspace = true } -memory = { workspace = true } -file_system = { workspace = true } -virtual_file_system = { workspace = true } -little_fs = { workspace = true } -task = { workspace = true } -time = { workspace = true } -abi_declarations = { workspace = true } -abi_context = { workspace = true } -synchronization = { workspace = true } - -[dev-dependencies] -abi_definitions = { workspace = true } -users = { workspace = true } -drivers_native = { workspace = true } -drivers_core = { workspace = true } -drivers_shared = { workspace = true } -drivers_std = { workspace = true } - -log = { workspace = true } -executable = { workspace = true, features = ["building"] } -testing = { workspace = true } - -[[test]] -name = "virtual_machine_test" -path = "tests/test.rs" - -[[test]] -name = "virtual_machine_test_2" -path = "tests/test_2.rs" - -[[test]] -name = "virtual_machine_test_3" -path = "tests/test_3.rs" diff --git a/modules/virtual_machine/src/custom_data.rs b/modules/virtual_machine/src/custom_data.rs deleted file mode 100644 index 4e588a8f..00000000 --- a/modules/virtual_machine/src/custom_data.rs +++ /dev/null @@ -1,18 +0,0 @@ -use task::TaskIdentifier; - -#[derive(Debug, Clone)] -pub struct CustomData { - task_identifier: TaskIdentifier, -} - -impl CustomData { - pub const fn new(task: TaskIdentifier) -> Self { - Self { - task_identifier: task, - } - } - - pub const fn get_task_identifier(&self) -> TaskIdentifier { - self.task_identifier - } -} diff --git a/modules/virtual_machine/src/environment.rs b/modules/virtual_machine/src/environment.rs deleted file mode 100644 index 0580e354..00000000 --- a/modules/virtual_machine/src/environment.rs +++ /dev/null @@ -1,192 +0,0 @@ -use core::{ - ffi::{CStr, c_void}, - marker::PhantomData, -}; - -use alloc::{ - boxed::Box, - string::{String, ToString}, - vec::Vec, -}; -use wamr_rust_sdk::{ - sys::{ - wasm_exec_env_t, wasm_module_inst_t, wasm_runtime_addr_app_to_native, - wasm_runtime_addr_native_to_app, wasm_runtime_call_indirect, wasm_runtime_create_exec_env, - wasm_runtime_get_custom_data, wasm_runtime_get_exception, - wasm_runtime_get_exec_env_singleton, wasm_runtime_get_module_inst, - wasm_runtime_set_custom_data, wasm_runtime_set_instruction_count_limit, - wasm_runtime_validate_app_addr, wasm_runtime_validate_native_addr, - }, - value::WasmValue, -}; - -use crate::{CustomData, Error, Instance, Result, WasmPointer, WasmUsize}; - -pub type EnvironmentPointer = wasm_exec_env_t; - -#[derive(Debug, Clone, Copy)] -pub struct Environment<'a>(EnvironmentPointer, PhantomData<&'a ()>); - -unsafe impl Send for Environment<'_> {} - -unsafe impl Sync for Environment<'_> {} - -impl Environment<'_> { - pub fn from_raw_pointer(raw_pointer: EnvironmentPointer) -> Result { - if raw_pointer.is_null() { - return Err(Error::InvalidPointer); - } - - Ok(Self(raw_pointer as EnvironmentPointer, PhantomData)) - } - - pub fn from_instance(instance: &Instance) -> Result { - let instance_pointer = instance.get_inner_reference().get_inner_instance(); - - if instance_pointer.is_null() { - return Err(Error::InvalidPointer); - } - Ok(Self( - unsafe { wasm_runtime_get_exec_env_singleton(instance_pointer) }, - PhantomData, - )) - } - - pub fn get_or_initialize_custom_data(&self) -> Result<&CustomData> { - unsafe { - let custom_data = - wasm_runtime_get_custom_data(self.get_instance_pointer()) as *const CustomData; - - let custom_data = if custom_data.is_null() { - let task = abi_context::get_instance().get_current_task_identifier(); - - let custom_data = Box::new(CustomData::new(task)); - - wasm_runtime_set_custom_data( - self.get_instance_pointer(), - Box::into_raw(custom_data) as *mut c_void, - ); - - wasm_runtime_get_custom_data(self.get_instance_pointer()) as *const CustomData - } else { - custom_data - }; - - Ok(&*custom_data) - } - } - - /// # Safety - /// - /// This function is unsafe because it is not checked that the address is valid. - /// Please use `Validate_WASM_pointer` to check the address. - pub unsafe fn convert_to_native_pointer(&self, address: WasmPointer) -> Option<*mut T> { - unsafe { - let pointer = - wasm_runtime_addr_app_to_native(self.get_instance_pointer(), address as u64); - - if pointer.is_null() { - return None; - } - - Some(pointer as *mut T) - } - } - - /// # Safety - /// - /// This function is unsafe because it is not checked that the address is valid. - /// Please use `Validate_WASM_pointer` to check the address. - pub unsafe fn convert_to_wasm_pointer(&self, pointer: *const T) -> WasmPointer { - unsafe { - wasm_runtime_addr_native_to_app(self.get_instance_pointer(), pointer as *mut c_void) - as WasmPointer - } - } - - pub fn validate_wasm_pointer(&self, address: WasmPointer, size: WasmUsize) -> bool { - unsafe { - wasm_runtime_validate_app_addr(self.get_instance_pointer(), address as u64, size as u64) - } - } - - pub fn validate_native_pointer(&self, pointer: *const T, size: u64) -> bool { - unsafe { - wasm_runtime_validate_native_addr( - self.get_instance_pointer(), - pointer as *mut c_void, - size, - ) - } - } - - /// Make an indirect function call (call a function by its index which is not exported). - /// For exported functions use `Call_export_function`. - pub fn call_indirect_function( - &self, - function_index: u32, - parameters: &Vec, - ) -> Result<()> { - let mut arguments = Vec::new(); - - for parameter in parameters { - arguments.append(&mut parameter.encode()); - } - - if arguments.is_empty() { - arguments.append(&mut WasmValue::I32(0).encode()); - } - - if !unsafe { - wasm_runtime_call_indirect( - self.0, - function_index, - arguments.len() as u32, - arguments.as_mut_ptr(), - ) - } { - let exception_message = - unsafe { wasm_runtime_get_exception(self.get_instance_pointer()) }; - let exception_message = unsafe { CStr::from_ptr(exception_message) }; - let exception_message = - String::from_utf8_lossy(exception_message.to_bytes()).to_string(); - - return Err(Error::ExecutionError(exception_message)); - } - - Ok(()) - } - - /// Create a new execution environment. - /// This environment should be initialized with `Initialize_thread_environment` and deinitialized with `Deinitialize_thread_environment`. - pub fn create_environment(&self, stack_size: usize) -> Result { - let execution_environment = - unsafe { wasm_runtime_create_exec_env(self.get_instance_pointer(), stack_size as u32) }; - - if execution_environment.is_null() { - return Err(Error::ExecutionError( - "Execution environment creation failed".to_string(), - )); - } - - Ok(Self(execution_environment, PhantomData)) - } - - // pub fn set_instruction_count_limit(&self, limit: Option) { - // unsafe { - // wasm_runtime_set_instruction_count_limit( - // self.get_inner_reference(), - // limit.map(|limit| limit as i32).unwrap_or(-1), - // ); - // } - // } - - fn get_instance_pointer(&self) -> wasm_module_inst_t { - unsafe { wasm_runtime_get_module_inst(self.0) } - } - - #[allow(dead_code)] - pub(crate) fn get_inner_reference(&self) -> EnvironmentPointer { - self.0 - } -} diff --git a/modules/virtual_machine/src/error.rs b/modules/virtual_machine/src/error.rs deleted file mode 100644 index c4035cba..00000000 --- a/modules/virtual_machine/src/error.rs +++ /dev/null @@ -1,98 +0,0 @@ -//! Error types and result handling for the Virtual Machine module. -//! -//! This module defines comprehensive error types that can occur during WASM module -//! loading, compilation, instantiation, and execution. - -use core::fmt::Display; - -use alloc::string::String; -use wamr_rust_sdk::RuntimeError; - -/// Result type alias for Virtual Machine operations -pub type Result = core::result::Result; - -/// Comprehensive error types for Virtual Machine operations -/// -/// This enum covers all possible error conditions that can occur during -/// WASM module lifecycle operations, from loading to execution. -#[derive(Debug)] -#[repr(C)] -pub enum Error { - /// Invalid pointer provided to a function - InvalidPointer, - - /// String contains invalid UTF-8 sequences - InvalidUtf8String, - - /// Failed to convert between slice types - SliceConversionFailed(shared::Error), - - /// Requested functionality is not yet implemented - NotImplemented, - - /// WASM runtime initialization failed - InitializationFailure, - - /// WASM module compilation failed with detailed error message - CompilationError(String), - - /// WASM module instantiation failed with detailed error message - InstantiationFailure(String), - - /// WASM function execution failed with detailed error message - ExecutionError(String), - - /// Requested function was not found in the module - FunctionNotFound, - - /// Memory allocation failed - AllocationFailure, - - /// Failed to retrieve task information - FailedToGetTaskInformations(task::Error), - - /// Mutex or lock was poisoned - PoisonedLock, - - /// Invalid WASM module format or structure - InvalidModule, - - /// Internal runtime error - InternalError, - - /// Invalid thread identifier provided - InvalidThreadIdentifier, - - /// Time-related operation failed - Time(time::Error), - - /// Failed to transfert file identifiers - FailedToRegisterFileContext, -} - -impl Display for Error { - fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(formatter, "{:?}", self) - } -} - -impl core::error::Error for Error {} - -impl From for Error { - fn from(error: RuntimeError) -> Self { - match error { - RuntimeError::NotImplemented => Error::NotImplemented, - RuntimeError::InitializationFailure => Error::InitializationFailure, - RuntimeError::WasmFileFSError(_) => Error::InitializationFailure, - RuntimeError::CompilationError(e) => Error::CompilationError(e), - RuntimeError::InstantiationFailure(e) => Error::InstantiationFailure(e), - RuntimeError::ExecutionError(e) => Error::ExecutionError(e.message), - RuntimeError::FunctionNotFound => Error::FunctionNotFound, - } - } -} -impl From for Error { - fn from(error: task::Error) -> Self { - Error::FailedToGetTaskInformations(error) - } -} diff --git a/modules/virtual_machine/src/instance.rs b/modules/virtual_machine/src/instance.rs deleted file mode 100644 index 187f2833..00000000 --- a/modules/virtual_machine/src/instance.rs +++ /dev/null @@ -1,139 +0,0 @@ -use core::ffi::c_void; - -use alloc::{boxed::Box, vec, vec::Vec}; -use wamr_rust_sdk::{ - function::Function, - instance, - sys::{ - wasm_runtime_addr_app_to_native, wasm_runtime_addr_native_to_app, - wasm_runtime_get_custom_data, wasm_runtime_validate_native_addr, - }, - value::WasmValue, -}; - -use crate::{CustomData, Error, Result, WasmPointer, module::Module, runtime::Runtime}; - -pub struct Instance<'module> { - instance: instance::Instance<'module>, -} - -unsafe impl Send for Instance<'_> {} - -impl Drop for Instance<'_> { - fn drop(&mut self) { - let instance = self.get_inner_reference().get_inner_instance(); - unsafe { - let user_data = wasm_runtime_get_custom_data(instance) as *mut CustomData; - - if !user_data.is_null() { - let _ = Box::from_raw(user_data); - } - } - - // User data is dropped here. - } -} - -impl<'module> Instance<'module> { - pub fn new( - runtime: &Runtime, - module: &'module Module<'module>, - stack_size: usize, - ) -> Result { - let wamr_instance = instance::Instance::new( - runtime.get_inner_reference(), - module.get_inner_reference(), - stack_size as u32, - )?; - - let instance = Instance { - instance: wamr_instance, - }; - - Ok(instance) - } - - pub fn validate_native_pointer(&self, pointer: *const T, size: usize) -> bool { - unsafe { - wasm_runtime_validate_native_addr( - self.get_inner_reference().get_inner_instance(), - pointer as *mut c_void, - size as u64, - ) - } - } - - pub fn validate_wasm_pointer(&self, address: WasmPointer, size: usize) -> bool { - unsafe { - wasm_runtime_validate_native_addr( - self.get_inner_reference().get_inner_instance(), - address as *mut c_void, - size as u64, - ) - } - } - - pub fn convert_to_wasm_pointer(&self, pointer: *const T) -> WasmPointer { - unsafe { - wasm_runtime_addr_native_to_app( - self.get_inner_reference().get_inner_instance(), - pointer as *mut c_void, - ) as WasmPointer - } - } - - /// # Safety - /// - /// This function is unsafe because it is not checked that the address is valid. - #[allow(clippy::mut_from_ref)] - pub unsafe fn convert_to_native_pointer(&self, address: WasmPointer) -> *mut T { - unsafe { - wasm_runtime_addr_app_to_native( - self.get_inner_reference().get_inner_instance(), - address as u64, - ) as *mut T - } - } - - pub fn call_export_function( - &self, - name: &str, - parameters: &Vec, - ) -> Result> { - if parameters.is_empty() { - Ok( - Function::find_export_func(self.get_inner_reference(), name)? - .call(&self.instance, &vec![WasmValue::I32(0)])?, - ) - } else { - Ok( - Function::find_export_func(self.get_inner_reference(), name)? - .call(&self.instance, parameters)?, - ) - } - } - - pub fn call_main(&self, parameters: &Vec) -> Result> { - self.call_export_function("_start", parameters) - } - - pub fn allocate(&mut self, size: usize) -> Result<*mut T> { - let result = self.call_export_function("Allocate", &vec![WasmValue::I32(size as i32)])?; - - if let Some(WasmValue::I32(pointer)) = result.first() { - let pointer = unsafe { self.convert_to_native_pointer(*pointer as u32) }; - - Ok(pointer) - } else { - Err(Error::AllocationFailure) - } - } - - pub fn deallocate(&mut self, data: *mut T) { - let _ = self.call_export_function("Deallocate", &vec![WasmValue::I32(data as i32)]); - } - - pub fn get_inner_reference(&'_ self) -> &'_ instance::Instance<'_> { - &self.instance - } -} diff --git a/modules/virtual_machine/src/lib.rs b/modules/virtual_machine/src/lib.rs deleted file mode 100644 index 6c7ac848..00000000 --- a/modules/virtual_machine/src/lib.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! # Virtual Machine Module -//! -//! This crate provides a WebAssembly (WASM) virtual machine implementation for the Xila operating system. -//! It is built on top of the WAMR (WebAssembly Micro Runtime) and provides a high-level interface -//! for executing WASM modules in a no_std environment. -//! -//! ## Features -//! -//! - **Module Management**: Load, instantiate, and execute WASM modules -//! - **Runtime Environment**: Provides isolated execution environments for WASM instances -//! - **Host Function Registration**: Register native functions that can be called from WASM -//! - **Memory Management**: Safe pointer conversion between WASM and native address spaces -//! - **Error Handling**: Comprehensive error types for debugging and error recovery -//! - **XIP Support**: Execute-in-place for AOT compiled modules -//! - **WASI Integration**: WebAssembly System Interface support with custom I/O redirection -//! -//! ## Architecture -//! -//! The crate is organized into several key components: -//! -//! - [`Manager`]: Global singleton that manages the WASM runtime and loaded modules -//! - [`Runtime`]: Represents a WASM runtime instance with registered host functions -//! - [`Module`]: Represents a loaded WASM module ready for instantiation -//! - [`Instance`]: An instantiated WASM module that can execute functions -//! - [`Environment`]: Execution environment providing context for function calls -//! -//! ## Usage Example -//! -//! ```rust,ignore -//! use virtual_machine::*; -//! -//! // Define host functions that WASM can call -//! struct MyRegistrable; -//! impl Registrable_trait for MyRegistrable { -//! fn get_functions(&self) -> &[Function_descriptor_type] { -//! &[Function_descriptor!(my_host_function)] -//! } -//! fn get_name(&self) -> &'static str { "my_module" } -//! } -//! -//! // Initialize the virtual machine manager -//! let manager = Initialize(&[&MyRegistrable]); -//! -//! // Execute a WASM module -//! let result = manager.Execute( -//! wasm_bytes, -//! stack_size, -//! stdin_fd, -//! stdout_fd, -//! stderr_fd -//! ).await; -//! ``` - -#![no_std] -#![allow(unused_imports)] -#![cfg(not(target_arch = "wasm32"))] -extern crate alloc; - -mod custom_data; -mod environment; -mod error; -mod instance; -mod manager; -mod module; -mod registrable; -mod runtime; - -// Re-export key types from WAMR -pub use wamr_rust_sdk::value::WasmValue; - -// Re-export all public types from modules -pub use custom_data::*; -pub use environment::*; -pub use error::*; -pub use instance::*; -pub use manager::*; -pub use module::*; -pub use registrable::*; -pub use runtime::*; - -/// Type alias for WASM pointer addresses in the 32-bit WASM address space -pub type WasmPointer = u32; - -/// Type alias for WASM size values (32-bit) -pub type WasmUsize = u32; diff --git a/modules/virtual_machine/src/manager.rs b/modules/virtual_machine/src/manager.rs deleted file mode 100644 index efb63ac8..00000000 --- a/modules/virtual_machine/src/manager.rs +++ /dev/null @@ -1,258 +0,0 @@ -//! Virtual Machine Manager - Global singleton for WASM runtime management. - -//! -//! The Manager provides a centralized interface for initializing the WASM runtime, -//! registering host functions, and executing WASM modules. It maintains a global -//! singleton instance that can be accessed throughout the system. - -use core::{ffi::CStr, mem::forget}; - -use abi_context::FileIdentifier; -use alloc::{string::ToString, vec, vec::Vec}; -use synchronization::once_lock::OnceLock; -use task::TaskIdentifier; -use virtual_file_system::File; -use wamr_rust_sdk::{ - sys::{wasm_runtime_is_xip_file, wasm_runtime_load, wasm_runtime_register_module}, - value::WasmValue, -}; - -use crate::{Error, Instance, Module, Registrable, Result, Runtime}; - -/// Global singleton instance of the Virtual Machine Manager -static MANAGER_INSTANCE: OnceLock = OnceLock::new(); - -/// Initialize the Virtual Machine Manager with a set of registrable host functions. -/// -/// This function must be called once before any WASM operations can be performed. -/// It creates a global singleton Manager instance that will persist for the -/// lifetime of the application. -/// -/// # Arguments -/// -/// * `Registrables` - Array of traits implementing host functions that can be called from WASM -/// -/// # Returns -/// -/// A static reference to the initialized Manager instance -/// -/// # Example -/// -/// ```rust,ignore -/// let manager = Initialize(&[&MyHostFunctions]); -/// ``` -pub fn initialize(registrables: &[&dyn Registrable]) -> &'static Manager { - MANAGER_INSTANCE - .get_or_init(|| Manager::new(registrables).expect("Cannot create virtual machine manager")); - - get_instance() -} - -/// Get a reference to the initialized Virtual Machine Manager instance. -/// -/// # Panics -/// -/// Panics if called before `Initialize()` has been called. -/// -/// # Returns -/// -/// A static reference to the Manager instance -pub fn get_instance() -> &'static Manager { - MANAGER_INSTANCE - .try_get() - .expect("Cannot get virtual machine manager instance before initialization") -} - -/// The Virtual Machine Manager handles WASM runtime lifecycle and module execution. -/// -/// This struct encapsulates the WASM runtime and provides high-level operations -/// for executing WASM modules with proper I/O redirection and resource management. -pub struct Manager { - runtime: Runtime, -} - -unsafe impl Send for Manager {} - -unsafe impl Sync for Manager {} - -impl Manager { - /// Create a new Virtual Machine Manager with the given registrable host functions. - /// - /// This function initializes the WASM runtime, registers all provided host functions, - /// and pre-loads any modules that the registrables provide. - /// - /// # Arguments - /// - /// * `Registrables` - Array of objects implementing host functions and optionally providing WASM modules - /// - /// # Returns - /// - /// A new Manager instance or an error if initialization fails - /// - /// # Errors - /// - /// Returns an error if: - /// - Runtime initialization fails - /// - Host function registration fails - /// - Module loading fails - pub fn new(registrables: &[&dyn Registrable]) -> Result { - let mut runtime_builder = Runtime::builder(); - - for registrable in registrables { - runtime_builder = runtime_builder.register(*registrable); - } - - let runtime = runtime_builder.build()?; - - let manager = Self { runtime }; - - for registrable in registrables { - if let Some(module_binary) = registrable.get_binary() { - manager.load_module(module_binary, registrable.is_xip(), registrable.get_name())?; - } - } - - Ok(manager) - } - - /// Load a WASM module from a buffer for execution. - /// - /// This method loads a WASM module into the runtime, either as a regular module - /// or as an XIP (execute-in-place) module for AOT compiled binaries. - /// - /// # Arguments - /// - /// * `Buffer` - The WASM module bytecode - /// * `XIP` - Whether this is an XIP AOT compiled module - /// * `Name` - Name to register the module under - /// - /// # Returns - /// - /// Success or an error if loading fails - /// - /// # Errors - /// - /// Returns an error if the module is not an XIP AOT compiled module or if the module cannot be loaded from the buffer. - fn load_module(&self, buffer: &[u8], xip: bool, name: &str) -> Result<()> { - if unsafe { xip && !wasm_runtime_is_xip_file(buffer.as_ptr(), buffer.len() as u32) } { - return Err(Error::InvalidModule); - } - - unsafe { - let mut buffer = if xip { - Vec::from_raw_parts(buffer.as_ptr() as *mut u8, buffer.len(), buffer.len()) - } else { - buffer.to_vec() - }; - - let mut error_buffer = [0_i8; 128]; - - let module = wasm_runtime_load( - buffer.as_mut_ptr(), - buffer.len() as u32, - error_buffer.as_mut_ptr(), - error_buffer.len() as u32, - ); - - if module.is_null() { - return Err(Error::CompilationError( - CStr::from_ptr(error_buffer.as_ptr()) - .to_string_lossy() - .to_string(), - )); - } - - if !wasm_runtime_register_module( - name.as_ptr() as *const i8, - module, - error_buffer.as_mut_ptr(), - error_buffer.len() as u32, - ) { - return Err(Error::InternalError); - } - - forget(buffer); - } - - Ok(()) - } - - /// Execute a WASM module with the specified I/O configuration. - /// - /// This is the main entry point for executing WASM modules. It creates a new - /// module instance, sets up the execution environment with proper I/O redirection, - /// and calls the module's main function. - /// - /// # Arguments - /// - /// * `Buffer` - The WASM module bytecode to execute - /// * `Stack_size` - Stack size in bytes for the WASM instance - /// * `Standard_in` - File identifier for standard input - /// * `Standard_out` - File identifier for standard output - /// * `Standard_error` - File identifier for standard error - /// - /// # Returns - /// - /// The return values from the WASM module's main function - /// - /// # Errors - /// - /// Returns an error if module loading, instantiation, or execution fails - pub async fn execute( - &'static self, - buffer: Vec, - stack_size: usize, - (standard_in, standard_out, standard_error): (File, File, File), - function_name: Option<&str>, - function_arguments: Vec, - task: TaskIdentifier, - ) -> Result> { - let abi_context = abi_context::get_instance(); - - abi_context - .call_abi(async || { - let standard_in = abi_context - .insert_file( - task, - standard_in.into_synchronous_file(), - Some(FileIdentifier::STANDARD_IN), - ) - .ok_or(Error::FailedToRegisterFileContext)?; - let standard_out = abi_context - .insert_file( - task, - standard_out.into_synchronous_file(), - Some(FileIdentifier::STANDARD_OUT), - ) - .ok_or(Error::FailedToRegisterFileContext)?; - let standard_error = abi_context - .insert_file( - task, - standard_error.into_synchronous_file(), - Some(FileIdentifier::STANDARD_ERROR), - ) - .ok_or(Error::FailedToRegisterFileContext)?; - - let module = Module::from_buffer( - &self.runtime, - buffer, - "module", - standard_in, - standard_out, - standard_error, - ) - .await?; - - let instance = Instance::new(&self.runtime, &module, stack_size)?; - - let result = if let Some(function_name) = function_name { - instance.call_export_function(function_name, &function_arguments)? - } else { - instance.call_main(function_arguments.as_ref())? - }; - - Ok(result) - }) - .await - } -} diff --git a/modules/virtual_machine/src/registrable.rs b/modules/virtual_machine/src/registrable.rs deleted file mode 100644 index 79d59d04..00000000 --- a/modules/virtual_machine/src/registrable.rs +++ /dev/null @@ -1,40 +0,0 @@ -use core::ffi::c_void; - -pub type FunctionPointer = *mut c_void; - -#[macro_export] -macro_rules! Function_descriptor { - ($Function:ident) => { - $crate::Function_descriptor_type { - Name: stringify!($Function), - Pointer: $Function as *mut std::ffi::c_void, - } - }; -} - -#[macro_export] -macro_rules! Function_descriptors { - ($($Function:ident),*) => { - [$( - $crate::Function_descriptor!($Function), - )*] - }; -} - -pub struct FunctionDescriptor { - pub name: &'static str, - pub pointer: FunctionPointer, -} -pub trait Registrable { - fn get_functions(&self) -> &[FunctionDescriptor]; - - fn is_xip(&self) -> bool { - false - } - - fn get_binary(&self) -> Option<&'static [u8]> { - None - } - - fn get_name(&self) -> &'static str; -} diff --git a/modules/virtual_machine/src/runtime.rs b/modules/virtual_machine/src/runtime.rs deleted file mode 100644 index e97081b7..00000000 --- a/modules/virtual_machine/src/runtime.rs +++ /dev/null @@ -1,73 +0,0 @@ -//! WASM Runtime management and configuration. -//! -//! This module provides a wrapper around the WAMR runtime with a builder pattern -//! for configuring and creating runtime instances with registered host functions. - -use core::ffi::c_void; - -use wamr_rust_sdk::{ - runtime, - sys::{wasm_runtime_destroy_thread_env, wasm_runtime_init_thread_env}, -}; - -use crate::{Registrable, Result}; - -/// Builder for configuring and creating WASM runtime instances. -/// -/// This builder allows incremental configuration of the runtime with -/// host functions before creating the final runtime instance. -pub struct RuntimeBuilder(runtime::RuntimeBuilder); - -impl Default for RuntimeBuilder { - fn default() -> Self { - Self::new() - } -} - -impl RuntimeBuilder { - pub fn new() -> Self { - let runtime_builder = runtime::Runtime::builder().use_system_allocator(); - - Self(runtime_builder) - } - - pub fn register_function(self, name: &str, function_pointer: *mut c_void) -> Self { - Self(self.0.register_host_function(name, function_pointer)) - } - - pub fn register(mut self, registrable: &dyn Registrable) -> Self { - for function_descriptor in registrable.get_functions() { - self = self.register_function(function_descriptor.name, function_descriptor.pointer); - } - - self - } - - pub fn build(self) -> Result { - Ok(Runtime(self.0.build()?)) - } -} - -pub struct Runtime(runtime::Runtime); - -impl Runtime { - pub fn builder() -> RuntimeBuilder { - RuntimeBuilder::new() - } - - pub(crate) fn get_inner_reference(&self) -> &runtime::Runtime { - &self.0 - } - - pub fn initialize_thread_environment() -> Option<()> { - if unsafe { wasm_runtime_init_thread_env() } { - Some(()) - } else { - None - } - } - - pub fn deinitialize_thread_environment() { - unsafe { wasm_runtime_destroy_thread_env() } - } -} diff --git a/modules/virtual_machine/tests/test.rs b/modules/virtual_machine/tests/test.rs deleted file mode 100644 index 42657fa1..00000000 --- a/modules/virtual_machine/tests/test.rs +++ /dev/null @@ -1,112 +0,0 @@ -extern crate abi_definitions; -extern crate alloc; -extern crate std; - -use abi_context::FileIdentifier; -use alloc::vec; -use executable::build_crate; -use std::fs; -use task::test; -use virtual_machine::{ - Environment, Function_descriptors, FunctionDescriptor, Instance, Module, Registrable, Runtime, -}; -use wamr_rust_sdk::value::WasmValue; - -drivers_std::memory::instantiate_global_allocator!(); - -pub struct WasmTest; - -impl Registrable for WasmTest { - fn get_functions(&self) -> &[FunctionDescriptor] { - &FUNCTIONS - } - - fn get_name(&self) -> &'static str { - "Virtual_machine_WASM_test" - } -} - -const FUNCTIONS: [FunctionDescriptor; 0] = Function_descriptors! {}; - -#[ignore] -#[test] -async fn integration_test() { - let (standard_in, standard_out, standard_error) = - testing::initialize(false, false).await.split(); - - let task_instance = task::get_instance(); - let task = task_instance.get_current_task_identifier().await; - - let binary_path = build_crate(&"virtual_machine_wasm_test").unwrap(); - let binary_buffer = fs::read(&binary_path).expect("Failed to read the binary file"); - - abi_context::get_instance() - .call_abi(async || { - // Register the functions - - let standard_in = abi_context::get_instance() - .insert_file( - task, - standard_in.into_synchronous_file(), - Some(FileIdentifier::STANDARD_IN), - ) - .unwrap(); - - let standard_out = abi_context::get_instance() - .insert_file( - task, - standard_out.into_synchronous_file(), - Some(FileIdentifier::STANDARD_OUT), - ) - .unwrap(); - - let standard_error = abi_context::get_instance() - .insert_file( - task, - standard_error.into_synchronous_file(), - Some(FileIdentifier::STANDARD_ERROR), - ) - .unwrap(); - - let runtime = Runtime::builder().register(&WasmTest).build().unwrap(); - - let module = Module::from_buffer( - &runtime, - binary_buffer.to_vec(), - "main", - standard_in, - standard_out, - standard_error, - ) - .await - .unwrap(); - - let mut instance = - Instance::new(&runtime, &module, 1024 * 4).expect("Failed to instantiate module"); - - let _ = - Environment::from_instance(&instance).expect("Failed to get execution environment"); - - assert_eq!(instance.call_main(&vec![]).unwrap(), [WasmValue::Void]); - - assert_eq!( - instance - .call_export_function("gcd", &vec![WasmValue::I32(9), WasmValue::I32(27)]) - .unwrap(), - [WasmValue::I32(9)] - ); - - // Test allocation and deallocation - - let pointer = instance.allocate::(4).unwrap(); - - unsafe { - pointer.write(1234); - - assert_eq!(1234, pointer.read()); - } - - instance.deallocate(pointer); - }) - .await; -} diff --git a/modules/virtual_machine/tests/test_2.rs b/modules/virtual_machine/tests/test_2.rs deleted file mode 100644 index 33d0fc74..00000000 --- a/modules/virtual_machine/tests/test_2.rs +++ /dev/null @@ -1,50 +0,0 @@ -extern crate abi_definitions; -extern crate alloc; -extern crate std; - -use executable::build_crate; -use std::fs; -use task::test; -use virtual_machine::{Function_descriptors, FunctionDescriptor, Registrable}; - -drivers_std::memory::instantiate_global_allocator!(); - -pub struct WasmTest; - -impl Registrable for WasmTest { - fn get_functions(&self) -> &[FunctionDescriptor] { - &FUNCTIONS - } - - fn get_name(&self) -> &'static str { - "Virtual_machine_WASM_test" - } -} - -const FUNCTIONS: [FunctionDescriptor; 0] = Function_descriptors! {}; - -#[ignore] -#[test] -async fn integration_test_2() { - let standard = testing::initialize(false, false).await.split(); - - let task_instance = task::get_instance(); - let task = task_instance.get_current_task_identifier().await; - - let binary_path = build_crate(&"virtual_machine_wasm_test").unwrap(); - let binary_buffer = fs::read(&binary_path).expect("Failed to read the binary file"); - - let virtual_machine = virtual_machine::initialize(&[&WasmTest]); - - virtual_machine - .execute( - binary_buffer.to_vec(), - 4 * 1024, - standard, - None, - vec![], - task, - ) - .await - .unwrap(); -} diff --git a/modules/virtual_machine/tests/test_3.rs b/modules/virtual_machine/tests/test_3.rs deleted file mode 100644 index ed7b78ba..00000000 --- a/modules/virtual_machine/tests/test_3.rs +++ /dev/null @@ -1,139 +0,0 @@ -extern crate alloc; - -extern crate abi_definitions; - -use abi_context::FileIdentifier; -use alloc::vec; -use executable::build_crate; -use log::information; -use std::fs; -use task::test; -use virtual_machine::{ - Function_descriptors, FunctionDescriptor, Instance, Module, Registrable, Runtime, -}; -use wamr_rust_sdk::{RuntimeError, function::Function, value::WasmValue}; - -drivers_std::memory::instantiate_global_allocator!(); - -pub struct WasmTest; - -impl Registrable for WasmTest { - fn get_functions(&self) -> &[FunctionDescriptor] { - &FUNCTIONS - } - - fn get_name(&self) -> &'static str { - "Virtual_machine_WASM_test" - } -} - -const FUNCTIONS: [FunctionDescriptor; 0] = Function_descriptors! {}; - -#[ignore] -#[test] -async fn integration_test() { - let (standard_in, standard_out, standard_error) = - testing::initialize(false, false).await.split(); - - let task_instance = task::get_instance(); - let task = task_instance.get_current_task_identifier().await; - - let binary_path = build_crate(&"virtual_machine_wasm_test").unwrap(); - let binary_buffer = fs::read(&binary_path).expect("Failed to read the binary file"); - - abi_context::get_instance() - .call_abi(async || { - // Register the functions - - let standard_in = abi_context::get_instance() - .insert_file( - task, - standard_in.into_synchronous_file(), - Some(FileIdentifier::STANDARD_IN), - ) - .unwrap(); - - let standard_out = abi_context::get_instance() - .insert_file( - task, - standard_out.into_synchronous_file(), - Some(FileIdentifier::STANDARD_OUT), - ) - .unwrap(); - - let standard_error = abi_context::get_instance() - .insert_file( - task, - standard_error.into_synchronous_file(), - Some(FileIdentifier::STANDARD_ERROR), - ) - .unwrap(); - - let runtime = Runtime::builder().register(&WasmTest).build().unwrap(); - - let module = Module::from_buffer( - &runtime, - binary_buffer.to_vec(), - "main", - standard_in, - standard_out, - standard_error, - ) - .await - .unwrap(); - - let mut instance = - Instance::new(&runtime, &module, 1024 * 4).expect("Failed to instantiate module"); - - let function = Function::find_export_func(instance.get_inner_reference(), "_start") - .expect("Failed to find _start function"); - - loop { - let result = function.call(instance.get_inner_reference(), &vec![]); - - println!("Result: {result:?}"); - - match result { - Ok(values) => { - if values == [WasmValue::Void] { - information!("Function returned without qnything successfully."); - } else { - assert_eq!(values.len(), 1); - assert_eq!(values[0], WasmValue::Void); - break; - } - } - Err(RuntimeError::ExecutionError(e)) => { - if e.message != "Exception: instruction limit exceeded" { - panic!("Unexpected exception: {}", e.message); - } - - information!("Caught exception: {}", e.message); - } - Err(error) => { - panic!("Unexpected error: {error:?}"); - } - } - } - - assert_eq!( - instance - .call_export_function("GCD", &vec![WasmValue::I32(9), WasmValue::I32(27)]) - .unwrap(), - [WasmValue::I32(9)] - ); - - // Test allocation and deallocation - - let pointer = instance.allocate::(4).unwrap(); - - unsafe { - pointer.write(1234); - - assert_eq!(1234, pointer.read()); - } - - instance.deallocate(pointer); - }) - .await; -} diff --git a/modules/virtual_machine/tests/wasm_test/.cargo/config.toml b/modules/virtual_machine/tests/wasm_test/.cargo/config.toml deleted file mode 100644 index 827adcdc..00000000 --- a/modules/virtual_machine/tests/wasm_test/.cargo/config.toml +++ /dev/null @@ -1,12 +0,0 @@ -[build] -target = "wasm32-wasip1" - -[target.wasm32-wasip1] -rustflags = [ - "-C", - "link-arg=--export=__heap_base", - "-C", - "link-arg=--export=__data_end", - "-C", - "link-arg=-zstack-size=8192", -] diff --git a/modules/virtual_machine/tests/wasm_test/Cargo.toml b/modules/virtual_machine/tests/wasm_test/Cargo.toml deleted file mode 100644 index ad58eb82..00000000 --- a/modules/virtual_machine/tests/wasm_test/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "virtual_machine_wasm_test" -version = "0.1.0" -edition = "2024" - -[dependencies] diff --git a/modules/virtual_machine/tests/wasm_test/src/main.rs b/modules/virtual_machine/tests/wasm_test/src/main.rs deleted file mode 100644 index d9117c64..00000000 --- a/modules/virtual_machine/tests/wasm_test/src/main.rs +++ /dev/null @@ -1,166 +0,0 @@ -use std::{ - fs::{OpenOptions, create_dir, read_dir, rename}, - io::{Read, Write}, -}; - -#[unsafe(export_name = "gcd")] -pub fn gcd(mut a: i32, mut b: i32) -> i32 { - while b != 0 { - let t = b; - b = a % b; - a = t; - } - a -} - -fn test_stdio() -> Result<(), ()> { - println!("Test stdout"); - eprintln!("Test stderr"); - - let mut input = String::new(); - - std::io::stdin().read_line(&mut input).unwrap(); - - println!("Input: {}", input); - - Ok(()) -} - -fn test_file() { - println!("Testing file operations..."); - - { - let mut file = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open("/test.txt") - .unwrap(); - - println!("Writing to file..."); - - file.write_all(b"Hello World from WASM!").unwrap(); - } - - println!("File written successfully."); - - { - let mut file = OpenOptions::new().read(true).open("/test.txt").unwrap(); - - let mut string = String::new(); - - file.read_to_string(&mut string).unwrap(); - - assert_eq!(string, "Hello World from WASM!"); - } - - println!("File read successfully."); - - { - rename("/test.txt", "/test2.txt").unwrap(); - - let mut file = OpenOptions::new().read(true).open("/test2.txt").unwrap(); - - let mut string = String::new(); - - file.read_to_string(&mut string).unwrap(); - - assert_eq!(string, "Hello World from WASM!"); - } - - println!("File renamed and read successfully."); -} - -fn test_environment_variables() { - println!("Environment variables:"); - - let environment = std::env::vars(); - - for (key, value) in environment { - println!("{}: {}", key, value); - } -} - -fn test_directory() { - println!("Testing directory operations..."); - - create_dir("/test_dir").unwrap(); - - println!("Directory created successfully."); - - { - let mut file = OpenOptions::new() - .write(true) - .truncate(true) - .create(true) - .open("/test_dir/file1.txt") - .unwrap(); - - file.write_all(b"File 1 in directory").unwrap(); - } - - { - let mut file = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open("/test_dir/file2.txt") - .unwrap(); - - file.write_all(b"File 2 in directory").unwrap(); - } - - for entry in read_dir("/").unwrap() { - let entry = entry.unwrap(); - - let r#type = entry.file_type().unwrap(); - - let r#type = if r#type.is_dir() { - "Directory" - } else if r#type.is_file() { - "File" - } else if r#type.is_symlink() { - "Symlink" - } else { - "Unknown" - }; - - println!("{:?} - {}", entry.file_name(), r#type); - } -} - -/// Allocate memory -/// -/// # Safety -/// -/// This function is unsafe because it may return an invalid pointer. -#[unsafe(no_mangle)] -pub unsafe extern "C" fn Allocate(size: usize) -> *mut u8 { - let layout = std::alloc::Layout::from_size_align(size, std::mem::size_of::()).unwrap(); - - unsafe { std::alloc::alloc(layout) } -} - -/// Deallocate memory -/// -/// # Safety -/// -/// This function is unsafe because it may cause undefined behavior if the pointer is invalid. -#[unsafe(no_mangle)] -pub unsafe extern "C" fn Deallocate(pointer: *mut u8, size: usize) { - let layout = std::alloc::Layout::from_size_align(size, std::mem::size_of::()).unwrap(); - - unsafe { std::alloc::dealloc(pointer, layout) } -} - -fn main() -> Result<(), ()> { - test_stdio()?; - - test_file(); - - test_directory(); - - test_environment_variables(); - - Ok(()) -} diff --git a/src/lib.rs b/src/lib.rs index 10db419b..deaecf27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,48 +2,24 @@ pub mod about; -pub use internationalization; - -#[cfg(all(target_arch = "wasm32", feature = "wasm"))] -pub use wasm_bindings as bindings; - -#[cfg(feature = "host")] pub use abi_context; -#[cfg(feature = "host")] pub use abi_declarations; -#[cfg(feature = "abi_definitions")] -pub use abi_definitions; -#[cfg(feature = "host")] pub use authentication; -#[cfg(feature = "host")] pub use bootsplash; -#[cfg(feature = "host")] pub use executable; -#[cfg(feature = "host")] pub use file_system; -#[cfg(feature = "host")] pub use graphics; -#[cfg(feature = "virtual_machine")] -pub use host_bindings; -#[cfg(feature = "host")] +pub use internationalization; pub use little_fs; -#[cfg(feature = "host")] pub use log; -#[cfg(feature = "host")] pub use memory; -#[cfg(feature = "host")] pub use network; -#[cfg(feature = "host")] pub use shared; -#[cfg(feature = "host")] pub use synchronization; -#[cfg(feature = "host")] pub use task; -#[cfg(feature = "host")] pub use time; -#[cfg(feature = "host")] pub use users; -#[cfg(feature = "host")] pub use virtual_file_system; -#[cfg(feature = "virtual_machine")] -pub use virtual_machine; + +#[cfg(feature = "abi_definitions")] +pub use abi_definitions; From a8fd0e9233b44814f63d2409504fb75981898414 Mon Sep 17 00:00:00 2001 From: Alix ANNERAUD Date: Tue, 10 Feb 2026 23:38:42 +0100 Subject: [PATCH 04/16] Remove virtual machine references from native example and update WASM features --- examples/native/Cargo.toml | 3 +-- examples/native/src/main.rs | 5 ----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/examples/native/Cargo.toml b/examples/native/Cargo.toml index 90981c0b..d531bd50 100644 --- a/examples/native/Cargo.toml +++ b/examples/native/Cargo.toml @@ -6,14 +6,13 @@ edition = "2024" [target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))'.dependencies] xila = { path = "../../", features = [ "host", - "virtual_machine", "graphics_rendering_xrgb8888", "executable_building", ] } file_manager = { workspace = true } command_line_shell = { workspace = true } graphical_shell = { workspace = true } -wasm = { workspace = true } +wasm = { workspace = true, features = ["default_host"] } terminal = { workspace = true } settings = { workspace = true } drivers_core = { workspace = true } diff --git a/examples/native/src/main.rs b/examples/native/src/main.rs index 1eb95156..8eaac68c 100644 --- a/examples/native/src/main.rs +++ b/examples/native/src/main.rs @@ -20,7 +20,6 @@ async fn main() { use xila::file_system::mbr::Mbr; use xila::file_system::mbr::PartitionKind; use xila::graphics; - use xila::host_bindings; use xila::little_fs; use xila::log; use xila::network::{self, ADD_DNS_SERVER, ADD_IP_ADDRESS, ADD_ROUTE}; @@ -30,7 +29,6 @@ async fn main() { use xila::virtual_file_system; use xila::virtual_file_system::File; use xila::virtual_file_system::mount_static; - use xila::virtual_machine; // - Initialize the system log::initialize(&drivers_std::log::Logger).unwrap(); @@ -201,9 +199,6 @@ async fn main() { file.close(virtual_file_system).await.unwrap(); - // Initialize the virtual machine - virtual_machine::initialize(&[&host_bindings::GraphicsBindings]); - // Mount static executables let virtual_file_system = virtual_file_system::get_instance(); From 561eda1fb7252531b90a55175611c842a733d698 Mon Sep 17 00:00:00 2001 From: Alix ANNERAUD Date: Wed, 11 Feb 2026 13:26:52 +0100 Subject: [PATCH 05/16] Refactor WASM build configuration and graphics module structure --- Makefile.toml | 31 ++----------------- executables/calculator/Cargo.toml | 2 +- .../wasm/src/guest/{ => graphics}/bindings.rs | 0 .../src/guest/{ => graphics}/c_functions.rs | 0 .../src/guest/{ => graphics}/enumeration.rs | 0 .../src/guest/{ => graphics}/functions.rs | 0 executables/wasm/src/guest/graphics/mod.rs | 7 +++++ .../wasm/src/guest/{ => graphics}/prelude.rs | 0 executables/wasm/src/guest/mod.rs | 10 +++--- 9 files changed, 14 insertions(+), 36 deletions(-) rename executables/wasm/src/guest/{ => graphics}/bindings.rs (100%) rename executables/wasm/src/guest/{ => graphics}/c_functions.rs (100%) rename executables/wasm/src/guest/{ => graphics}/enumeration.rs (100%) rename executables/wasm/src/guest/{ => graphics}/functions.rs (100%) create mode 100644 executables/wasm/src/guest/graphics/mod.rs rename executables/wasm/src/guest/{ => graphics}/prelude.rs (100%) diff --git a/Makefile.toml b/Makefile.toml index 398a30e9..bf2830c7 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -37,17 +37,6 @@ args = [ "host,abi_definitions,graphics_rendering_xrgb8888", ] -[tasks.check-wasm] -toolchain = "stable" -command = "cargo" -args = [ - "check", - "--target", - "wasm32-unknown-unknown", - "--features", - "wasm,wasm_c_bindings", -] - [tasks.doc-host] toolchain = "stable" command = "cargo" @@ -68,23 +57,7 @@ args = ["doc", "--features", "wasm", "--target", "wasm32-unknown-unknown"] dependencies = ["doc-host", "doc-wasm"] [tasks.check] -dependencies = ["check-host", "check-wasm"] - -[tasks.clippy-wasm] -install_crate = "clippy" -toolchain = "stable" -command = "cargo" -args = [ - "clippy", - "--target", - "wasm32-unknown-unknown", - "--features", - "wasm,wasm_c_bindings", - "--all-targets", - "--", - "-D", - "warnings", -] +dependencies = ["check-host"] [tasks.clippy-host] install_crate = "clippy" @@ -103,7 +76,7 @@ args = [ ] [tasks.clippy] -dependencies = ["clippy-host", "clippy-wasm"] +dependencies = ["clippy-host"] [tasks.lint] install_crate = "clippy" diff --git a/executables/calculator/Cargo.toml b/executables/calculator/Cargo.toml index a4a6cc46..a43aa040 100644 --- a/executables/calculator/Cargo.toml +++ b/executables/calculator/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [target.'cfg(target_arch = "wasm32")'.dependencies] -wasm = { workspace = true, features = ["guest"] } +wasm = { workspace = true, features = ["guest", "graphics"] } [target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))'.dev-dependencies] xila = { path = "../../", features = [ diff --git a/executables/wasm/src/guest/bindings.rs b/executables/wasm/src/guest/graphics/bindings.rs similarity index 100% rename from executables/wasm/src/guest/bindings.rs rename to executables/wasm/src/guest/graphics/bindings.rs diff --git a/executables/wasm/src/guest/c_functions.rs b/executables/wasm/src/guest/graphics/c_functions.rs similarity index 100% rename from executables/wasm/src/guest/c_functions.rs rename to executables/wasm/src/guest/graphics/c_functions.rs diff --git a/executables/wasm/src/guest/enumeration.rs b/executables/wasm/src/guest/graphics/enumeration.rs similarity index 100% rename from executables/wasm/src/guest/enumeration.rs rename to executables/wasm/src/guest/graphics/enumeration.rs diff --git a/executables/wasm/src/guest/functions.rs b/executables/wasm/src/guest/graphics/functions.rs similarity index 100% rename from executables/wasm/src/guest/functions.rs rename to executables/wasm/src/guest/graphics/functions.rs diff --git a/executables/wasm/src/guest/graphics/mod.rs b/executables/wasm/src/guest/graphics/mod.rs new file mode 100644 index 00000000..938621cb --- /dev/null +++ b/executables/wasm/src/guest/graphics/mod.rs @@ -0,0 +1,7 @@ +mod enumeration; +mod functions; + +pub use enumeration::*; +pub use functions::*; +pub use prelude::*; +pub mod prelude; diff --git a/executables/wasm/src/guest/prelude.rs b/executables/wasm/src/guest/graphics/prelude.rs similarity index 100% rename from executables/wasm/src/guest/prelude.rs rename to executables/wasm/src/guest/graphics/prelude.rs diff --git a/executables/wasm/src/guest/mod.rs b/executables/wasm/src/guest/mod.rs index 938621cb..8651c101 100644 --- a/executables/wasm/src/guest/mod.rs +++ b/executables/wasm/src/guest/mod.rs @@ -1,7 +1,5 @@ -mod enumeration; -mod functions; +#[cfg(feature = "graphics")] +mod graphics; -pub use enumeration::*; -pub use functions::*; -pub use prelude::*; -pub mod prelude; +#[cfg(feature = "graphics")] +pub use graphics::*; From 942253ffd6e8c6a0e7c06b457a93d63ba3f40baa Mon Sep 17 00:00:00 2001 From: Alix ANNERAUD Date: Wed, 11 Feb 2026 13:30:49 +0100 Subject: [PATCH 06/16] Remove Clippy check for WASM from CI workflow --- .github/workflows/check.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index f5e2ffd3..32d913f3 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -36,9 +36,6 @@ jobs: - name: Check | Clippy (Host) run: cargo make clippy-host - - name: Check | Clippy (WASM) - run: cargo make clippy-wasm - - name: Check | Documentation (Host) run: RUSTDOCFLAGS="--deny warnings" cargo make doc-host From 01dc953139debe217a5a02727d892a8ea545aa9a Mon Sep 17 00:00:00 2001 From: Alix ANNERAUD Date: Wed, 11 Feb 2026 13:41:31 +0100 Subject: [PATCH 07/16] Add unit tests for BijectiveBTreeMap functionality --- modules/shared/src/bijective_map.rs | 108 ++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/modules/shared/src/bijective_map.rs b/modules/shared/src/bijective_map.rs index c8e75847..0922bb24 100644 --- a/modules/shared/src/bijective_map.rs +++ b/modules/shared/src/bijective_map.rs @@ -78,3 +78,111 @@ impl Clone for BijectiveBTreeMap { } } } + +#[cfg(test)] +mod tests { + use alloc::{format, vec, vec::Vec}; + + use super::*; + + #[test] + fn test_new() { + let map: BijectiveBTreeMap = BijectiveBTreeMap::new(); + assert!(map.get_by_left(&1).is_none()); + assert!(map.get_by_right(&1).is_none()); + } + + #[test] + fn test_insert_and_get() { + let mut map = BijectiveBTreeMap::new(); + map.insert(1, 10); + map.insert(2, 20); + + assert_eq!(map.get_by_left(&1), Some(&10)); + assert_eq!(map.get_by_left(&2), Some(&20)); + assert_eq!(map.get_by_right(&10), Some(&1)); + assert_eq!(map.get_by_right(&20), Some(&2)); + } + + #[test] + fn test_insert_overwrite() { + let mut map = BijectiveBTreeMap::new(); + map.insert(1, 10); + map.insert(1, 20); + + assert_eq!(map.get_by_left(&1), Some(&20)); + assert!(map.get_by_right(&10).is_some()); + } + + #[test] + fn test_remove_by_key() { + let mut map = BijectiveBTreeMap::new(); + map.insert(1, 10); + map.insert(2, 20); + + assert_eq!(map.remove_by_key(&1), Some(10)); + assert!(map.get_by_left(&1).is_none()); + assert!(map.get_by_right(&10).is_none()); + assert_eq!(map.get_by_left(&2), Some(&20)); + } + + #[test] + fn test_remove_by_value() { + let mut map = BijectiveBTreeMap::new(); + map.insert(1, 10); + map.insert(2, 20); + + assert_eq!(map.remove_by_value(&10), Some(1)); + assert!(map.get_by_left(&1).is_none()); + assert!(map.get_by_right(&10).is_none()); + assert_eq!(map.get_by_left(&2), Some(&20)); + } + + #[test] + fn test_remove_nonexistent() { + let mut map = BijectiveBTreeMap::new(); + map.insert(1, 10); + + assert_eq!(map.remove_by_key(&2), None); + assert_eq!(map.remove_by_value(&20), None); + } + + #[test] + fn test_iterators() { + let mut map = BijectiveBTreeMap::new(); + map.insert(1, 10); + map.insert(2, 20); + map.insert(3, 30); + + let left_keys: Vec<_> = map.get_left_keys().copied().collect(); + assert_eq!(left_keys, vec![1, 2, 3]); + + let right_keys: Vec<_> = map.get_right_keys().copied().collect(); + assert_eq!(right_keys, vec![10, 20, 30]); + } + + #[test] + fn test_clone() { + let mut map = BijectiveBTreeMap::new(); + map.insert(1, 10); + map.insert(2, 20); + + let cloned = map.clone(); + assert_eq!(cloned.get_by_left(&1), Some(&10)); + assert_eq!(cloned.get_by_right(&20), Some(&2)); + } + + #[test] + fn test_default() { + let map: BijectiveBTreeMap = BijectiveBTreeMap::default(); + assert!(map.get_by_left(&1).is_none()); + } + + #[test] + fn test_debug() { + let mut map = BijectiveBTreeMap::new(); + map.insert(1, 10); + let debug_str = format!("{:?}", map); + assert!(debug_str.contains("BijectiveBTreeMap")); + } +} From ee5e252117e4e23d921b88c83888a595a81b5dd9 Mon Sep 17 00:00:00 2001 From: Alix ANNERAUD Date: Wed, 11 Feb 2026 13:54:16 +0100 Subject: [PATCH 08/16] Add ignore attribute to interactive tests in calculator and wasm integration --- executables/calculator/tests/test.rs | 1 + executables/wasm/tests/integration_test.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/executables/calculator/tests/test.rs b/executables/calculator/tests/test.rs index 4bcc2f37..70ad32a2 100644 --- a/executables/calculator/tests/test.rs +++ b/executables/calculator/tests/test.rs @@ -1,5 +1,6 @@ #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] #[xila::task::test(task_path = xila::task)] +#[ignore = "This test is meant to be run interactively"] async fn main() { drivers_std::memory::instantiate_global_allocator!(); diff --git a/executables/wasm/tests/integration_test.rs b/executables/wasm/tests/integration_test.rs index 5779bfb0..2f6bd0c6 100644 --- a/executables/wasm/tests/integration_test.rs +++ b/executables/wasm/tests/integration_test.rs @@ -1,6 +1,7 @@ #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] #[cfg(all(feature = "host", feature = "graphics"))] #[xila::task::test(task_path = xila::task)] +#[ignore = "This test is meant to be run interactively"] async fn main() { drivers_std::memory::instantiate_global_allocator!(); From bd3c2851640a5a8cd5b64eac256018d81f6553fa Mon Sep 17 00:00:00 2001 From: Alix ANNERAUD Date: Wed, 11 Feb 2026 13:54:26 +0100 Subject: [PATCH 09/16] Remove WASM graphics bindings file to decouple virtual machine from executable --- executables/wasm/src/guest/graphics/bindings.rs | 1 - 1 file changed, 1 deletion(-) delete mode 100644 executables/wasm/src/guest/graphics/bindings.rs diff --git a/executables/wasm/src/guest/graphics/bindings.rs b/executables/wasm/src/guest/graphics/bindings.rs deleted file mode 100644 index 44ae78fc..00000000 --- a/executables/wasm/src/guest/graphics/bindings.rs +++ /dev/null @@ -1 +0,0 @@ -include!(concat!(env!("OUT_DIR"), "/bindings.rs")); From ebacabd5ef6245df69fb980e42c4f9d9acf99b74 Mon Sep 17 00:00:00 2001 From: Alix ANNERAUD Date: Wed, 11 Feb 2026 13:54:36 +0100 Subject: [PATCH 10/16] Add c_bindings feature to WASM configuration and update graphics module --- executables/wasm/Cargo.toml | 1 + executables/wasm/src/guest/graphics/mod.rs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/executables/wasm/Cargo.toml b/executables/wasm/Cargo.toml index cd1b1684..a89cc92b 100644 --- a/executables/wasm/Cargo.toml +++ b/executables/wasm/Cargo.toml @@ -13,6 +13,7 @@ wamr-rust-sdk = { git = "https://github.com/bytecodealliance/wamr-rust-sdk.git", [features] default = [] graphics = [] +c_bindings = [] host = ["dep:xila", "dep:wamr-rust-sdk"] guest = [] wasm32 = [] diff --git a/executables/wasm/src/guest/graphics/mod.rs b/executables/wasm/src/guest/graphics/mod.rs index 938621cb..1f415ee2 100644 --- a/executables/wasm/src/guest/graphics/mod.rs +++ b/executables/wasm/src/guest/graphics/mod.rs @@ -1,6 +1,11 @@ +#[cfg(feature = "c_bindings")] +mod c_functions; mod enumeration; mod functions; +#[cfg(feature = "c_bindings")] +pub use c_functions::*; + pub use enumeration::*; pub use functions::*; pub use prelude::*; From 4670f029bbbe7b4bce86ea3b350c7de017cb407c Mon Sep 17 00:00:00 2001 From: Alix ANNERAUD Date: Wed, 11 Feb 2026 13:54:51 +0100 Subject: [PATCH 11/16] Refactor WASM virtual machine: decouple ABI functions, update error types, and streamline execution logic --- executables/wasm/build/guest.rs | 11 +++++------ .../wasm/src/host/bindings/graphics/translate.rs | 11 ----------- .../wasm/src/host/virtual_machine/environment.rs | 6 ++---- .../wasm/src/host/virtual_machine/error.rs | 16 ++++++++-------- .../wasm/src/host/virtual_machine/runtime.rs | 5 +++-- 5 files changed, 18 insertions(+), 31 deletions(-) diff --git a/executables/wasm/build/guest.rs b/executables/wasm/build/guest.rs index e3d8e9fb..70576719 100644 --- a/executables/wasm/build/guest.rs +++ b/executables/wasm/build/guest.rs @@ -12,7 +12,6 @@ use proc_macro2::TokenStream; use quote::{ToTokens, format_ident, quote}; use syn::visit::Visit; use syn::{FnArg, Ident, ReturnType, Signature, Type}; -use target::Architecture; fn convert_type(mut ty: Type) -> Type { match &mut ty { @@ -275,6 +274,11 @@ fn generate_c_abi_functions( #( #generated_c_abi_functions )* }; + println!( + "cargo:warning=Generating C ABI functions at path: {}", + path.as_ref().display() + ); + write_token_stream_to_file(path, token_stream)?; Ok(()) @@ -298,11 +302,6 @@ fn generate_c_functions_module_body(path: impl AsRef) -> Result<(), String } pub fn generate(output_path: &Path) { - // Build only for WASM32 architecture. - if Architecture::get() != Architecture::WASM32 { - return; - } - let input = lvgl_rust_sys::_bindgen_raw_src(); let parsed_input = syn::parse_file(input).expect("Error parsing input file"); diff --git a/executables/wasm/src/host/bindings/graphics/translate.rs b/executables/wasm/src/host/bindings/graphics/translate.rs index 334da640..f6bdb39a 100644 --- a/executables/wasm/src/host/bindings/graphics/translate.rs +++ b/executables/wasm/src/host/bindings/graphics/translate.rs @@ -5,7 +5,6 @@ use crate::host::bindings::graphics::{Error, Result}; use crate::host::virtual_machine::{Translator, WasmPointer, WasmUsize}; use alloc::vec::Vec; use xila::graphics::lvgl::{self, lv_style_value_t}; -use xila::log; pub trait TranslateFrom { unsafe fn translate_from(wasm_usize: WasmUsize, translator: &mut Translator) -> Result @@ -348,25 +347,15 @@ impl TranslateFrom for *const *const c_void { #[inline] unsafe fn translate_from(wasm_usize: WasmUsize, translator: &mut Translator) -> Result { unsafe { - log::information!("Translating array of pointers at guest address {wasm_usize:x}"); - let array: *mut WasmPointer = translator .translate_to_host(wasm_usize as WasmPointer, true) .ok_or(Error::InvalidPointer)?; - log::information!( - "Translating array of pointers at guest address {wasm_usize:x} (host address {array:p})" - ); - let count = (0..) .map(|i| *array.add(i)) .take_while(|&ptr| ptr != 0) .count(); - log::information!( - "Array of pointers at guest address {wasm_usize:x} has {count} elements" - ); - let vec: Result> = (0..count) .map(|i| *array.add(i)) .map(|ptr| { diff --git a/executables/wasm/src/host/virtual_machine/environment.rs b/executables/wasm/src/host/virtual_machine/environment.rs index 6f26ce67..c1694138 100644 --- a/executables/wasm/src/host/virtual_machine/environment.rs +++ b/executables/wasm/src/host/virtual_machine/environment.rs @@ -17,9 +17,7 @@ impl Environment { unsafe { &mut *(raw_pointer as *mut Self) } } - pub unsafe fn get_or_initialize_custom_data<'a, 'b, T: Default>( - &'a mut self, - ) -> Result<&'b mut T> { + pub unsafe fn get_or_initialize_custom_data<'b, T: Default>(&mut self) -> Result<&'b mut T> { unsafe { let custom_data = wasm_runtime_get_custom_data(self.get_instance_pointer()) as *mut T; @@ -53,7 +51,7 @@ impl Environment { return None; } - if (pointer as usize) % core::mem::align_of::() != 0 { + if !(pointer as usize).is_multiple_of(core::mem::align_of::()) { return None; } diff --git a/executables/wasm/src/host/virtual_machine/error.rs b/executables/wasm/src/host/virtual_machine/error.rs index 0e69893d..a367702e 100644 --- a/executables/wasm/src/host/virtual_machine/error.rs +++ b/executables/wasm/src/host/virtual_machine/error.rs @@ -48,13 +48,13 @@ pub enum Error { InitializationFailure, /// WASM module compilation failed with detailed error message - CompilationError(String), + Compilation(String), /// WASM module instantiation failed with detailed error message InstantiationFailure(String), /// WASM function execution failed with detailed error message - ExecutionError(String), + Execution(String), /// Requested function was not found in the module FunctionNotFound, @@ -72,7 +72,7 @@ pub enum Error { InvalidModule, /// Internal runtime error - InternalError, + Internal, /// Invalid thread identifier provided InvalidThreadIdentifier, @@ -98,9 +98,9 @@ impl From for Error { RuntimeError::NotImplemented => Error::NotImplemented, RuntimeError::InitializationFailure => Error::InitializationFailure, RuntimeError::WasmFileFSError(_) => Error::InitializationFailure, - RuntimeError::CompilationError(e) => Error::CompilationError(e), + RuntimeError::CompilationError(e) => Error::Compilation(e), RuntimeError::InstantiationFailure(e) => Error::InstantiationFailure(e), - RuntimeError::ExecutionError(e) => Error::ExecutionError(e.message), + RuntimeError::ExecutionError(e) => Error::Execution(e.message), RuntimeError::FunctionNotFound => Error::FunctionNotFound, } } @@ -145,8 +145,8 @@ impl fmt::Display for Error { Error::InitializationFailure => { write!(f, translate!("WASM runtime initialization failed")) } - Error::CompilationError(e) => write!(f, translate!("WASM compilation error: {}"), e), - Error::ExecutionError(e) => write!(f, translate!("WASM execution error: {}"), e), + Error::Compilation(e) => write!(f, translate!("WASM compilation error: {}"), e), + Error::Execution(e) => write!(f, translate!("WASM execution error: {}"), e), Error::FunctionNotFound => { write!(f, translate!("Requested function not found in module")) } @@ -158,7 +158,7 @@ impl fmt::Display for Error { Error::InvalidModule => { write!(f, translate!("Invalid WASM module format or structure")) } - Error::InternalError => write!(f, translate!("Internal runtime error")), + Error::Internal => write!(f, translate!("Internal runtime error")), Error::InvalidThreadIdentifier => { write!(f, translate!("Invalid thread identifier provided")) } diff --git a/executables/wasm/src/host/virtual_machine/runtime.rs b/executables/wasm/src/host/virtual_machine/runtime.rs index 0cd4d18a..19f02c47 100644 --- a/executables/wasm/src/host/virtual_machine/runtime.rs +++ b/executables/wasm/src/host/virtual_machine/runtime.rs @@ -58,6 +58,7 @@ impl Runtime { /// # Errors /// /// Returns an error if module loading, instantiation, or execution fails + #[allow(clippy::too_many_arguments)] pub async fn execute( &'static self, name: &str, @@ -99,7 +100,7 @@ impl Runtime { .ok_or(Error::FailedToRegisterFileContext)?; let module = Module::from_buffer( - &self, + self, buffer, name, standard_in, @@ -108,7 +109,7 @@ impl Runtime { ) .await?; - let instance = Instance::new(&self, &module, stack_size as _)?; + let instance = Instance::new(self, &module, stack_size as _)?; let result = instance.call_exported_function(function_name, &function_arguments)?; From 5af2c354991a8a97d44af610bceb76a40e899096 Mon Sep 17 00:00:00 2001 From: Alix ANNERAUD Date: Wed, 11 Feb 2026 21:51:35 +0100 Subject: [PATCH 12/16] Update c_functions module path to reflect new directory structure for graphics --- executables/wasm/build/guest.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/executables/wasm/build/guest.rs b/executables/wasm/build/guest.rs index 70576719..ad9d447d 100644 --- a/executables/wasm/build/guest.rs +++ b/executables/wasm/build/guest.rs @@ -316,7 +316,11 @@ pub fn generate(output_path: &Path) { let enumerations_generated_path = output_path.join("enumeration.generated.rs"); let functions_generated_path = output_path.join("functions.generated.rs"); let c_functions_generated_path = output_path.join("c_functions.generated.rs"); - let c_functions_module_path = crate_directory.join("src").join("c_functions.rs"); + let c_functions_module_path = crate_directory + .join("src") + .join("guest") + .join("graphics") + .join("c_functions.rs"); let c_header_path = output_path.join("xila_graphics.h"); generate_enumeration(&enumerations_generated_path, &context).unwrap(); From 32a2a37acabe681a77ece8105efd7cc0e1b5c8df Mon Sep 17 00:00:00 2001 From: Alix ANNERAUD Date: Wed, 11 Feb 2026 21:51:41 +0100 Subject: [PATCH 13/16] Add guest tasks for WASM: check, doc, and clippy; update default_guest features --- Makefile.toml | 49 +++++++++++++++++++++++++++++++++---- executables/wasm/Cargo.toml | 1 + 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/Makefile.toml b/Makefile.toml index bf2830c7..e90b4973 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -37,6 +37,19 @@ args = [ "host,abi_definitions,graphics_rendering_xrgb8888", ] +[tasks.check-guest] +toolchain = "stable" +command = "cargo" +args = [ + "check", + "--target", + "wasm32-unknown-unknown", + "--package", + "wasm", + "--features", + "default_guest", +] + [tasks.doc-host] toolchain = "stable" command = "cargo" @@ -48,16 +61,24 @@ args = [ "x86_64-unknown-linux-gnu", ] -[tasks.doc-wasm] +[tasks.doc-guest] toolchain = "stable" command = "cargo" -args = ["doc", "--features", "wasm", "--target", "wasm32-unknown-unknown"] +args = [ + "doc", + "--package", + "wasm", + "--features", + "default_guest", + "--target", + "wasm32-unknown-unknown", +] [tasks.doc] -dependencies = ["doc-host", "doc-wasm"] +dependencies = ["doc-host", "doc-guest"] [tasks.check] -dependencies = ["check-host"] +dependencies = ["check-host", "check-guest"] [tasks.clippy-host] install_crate = "clippy" @@ -75,8 +96,26 @@ args = [ "warnings", ] +[tasks.clippy-guest] +install_crate = "clippy" +toolchain = "stable" +command = "cargo" +args = [ + "clippy", + "--package", + "wasm", + "--features", + "default_guest", + "--target", + "wasm32-unknown-unknown", + "--all-targets", + "--", + "-D", + "warnings", +] + [tasks.clippy] -dependencies = ["clippy-host"] +dependencies = ["clippy-host", "clippy-guest"] [tasks.lint] install_crate = "clippy" diff --git a/executables/wasm/Cargo.toml b/executables/wasm/Cargo.toml index a89cc92b..013e6ef7 100644 --- a/executables/wasm/Cargo.toml +++ b/executables/wasm/Cargo.toml @@ -19,6 +19,7 @@ guest = [] wasm32 = [] wasm64 = [] default_host = ["host", "graphics", "wasm32"] +default_guest = ["guest", "graphics", "c_bindings"] [build-dependencies] lvgl_rust_sys = { git = "https://github.com/Xila-Project/lvgl_rust_sys.git", default-features = false } From 4e7376a8fab5bf55df111531fb10ced5063ea346 Mon Sep 17 00:00:00 2001 From: Alix ANNERAUD Date: Wed, 11 Feb 2026 21:51:45 +0100 Subject: [PATCH 14/16] Refactor deployment workflow: update documentation build steps for WASM and remove obsolete index.html --- .github/workflows/deploy.yml | 8 +++----- documentation/index.html | 14 -------------- 2 files changed, 3 insertions(+), 19 deletions(-) delete mode 100644 documentation/index.html diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 29735442..15e93022 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -29,15 +29,13 @@ jobs: run: | cargo make doc-host mkdir -p ./pages/host/ - cp ./documentation/index.html ./pages/host/ cp -r ./target/x86_64-unknown-linux-gnu/doc/* ./pages/host/ - name: Build | Documentation (WASM) run: | - cargo make doc-wasm - mkdir -p ./pages/wasm/ - cp ./documentation/index.html ./pages/wasm - cp -r ./target/wasm32-unknown-unknown/doc/* ./pages/wasm/ + cargo make doc-guest + mkdir -p ./pages/guest/ + cp -r ./target/wasm32-unknown-unknown/doc/guest/* ./pages/guest/ - name: Build | WASM demonstration (en) working-directory: ./examples/wasm diff --git a/documentation/index.html b/documentation/index.html deleted file mode 100644 index 46d5dc03..00000000 --- a/documentation/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - Redirecting... - - -

- If you are not redirected automatically, follow this - link. -

- - From eba897165969c220c0035ad06a7916a35f3780e6 Mon Sep 17 00:00:00 2001 From: Alix ANNERAUD Date: Wed, 11 Feb 2026 21:53:51 +0100 Subject: [PATCH 15/16] Update CI workflows: add Clippy and documentation checks for Guest, rename WASM documentation step to Guest --- .github/workflows/check.yml | 7 +++++-- .github/workflows/deploy.yml | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 32d913f3..653e0b4f 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -36,11 +36,14 @@ jobs: - name: Check | Clippy (Host) run: cargo make clippy-host + - name: Check | Clippy (Guest) + run: cargo make clippy-guest + - name: Check | Documentation (Host) run: RUSTDOCFLAGS="--deny warnings" cargo make doc-host - - name: Check | Documentation (WASM) - run: RUSTDOCFLAGS="--deny warnings" cargo make doc-wasm + - name: Check | Documentation (Guest) + run: RUSTDOCFLAGS="--deny warnings" cargo make doc-guest - name: Build | Example (Native) run: cargo make build -p native_example && cargo clean -p native_example diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 15e93022..d26ce7dd 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -31,7 +31,7 @@ jobs: mkdir -p ./pages/host/ cp -r ./target/x86_64-unknown-linux-gnu/doc/* ./pages/host/ - - name: Build | Documentation (WASM) + - name: Build | Documentation (Guest) run: | cargo make doc-guest mkdir -p ./pages/guest/ From 05793e13ca4f9bd6cf3492c15362dea3c6535d7d Mon Sep 17 00:00:00 2001 From: Alix ANNERAUD Date: Wed, 11 Feb 2026 22:04:12 +0100 Subject: [PATCH 16/16] Refactor host module imports: streamline virtual machine usage by removing unnecessary Environment import --- executables/wasm/build/host.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/executables/wasm/build/host.rs b/executables/wasm/build/host.rs index f8e0e37d..cb3622c2 100644 --- a/executables/wasm/build/host.rs +++ b/executables/wasm/build/host.rs @@ -177,11 +177,7 @@ pub fn generate_inner(output_path: &Path, context: &LvglContext) -> Result<(), S let functions = generate_code(context.get_signatures(), context.get_definitions())?; let token_stream = quote! { - use crate::host::{ - virtual_machine::{ - Environment, WasmPointer, WasmUsize, Translator - } - }; + use crate::host::virtual_machine::{Translator, WasmPointer, WasmUsize}; use crate::host::bindings::graphics::error::Result; #enumerations