Skip to content

Commit

Permalink
Example finally works!
Browse files Browse the repository at this point in the history
  • Loading branch information
dphfox committed Jun 13, 2024
1 parent f2a13a9 commit 125b9e3
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 29 deletions.
22 changes: 22 additions & 0 deletions examples/01-hello-lingua/src-luau/add_panic_handler.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-- When Rust panics, it doesn't exit helpfully by default. This function gives
-- the Rust side an external function it can call with details of the panic,
-- so the error can be emitted on the Luau side instead.

return function(extern_fns)
local current_panic = nil
function extern_fns.panic_reporter(
len_or_byte: number
): ()
if current_panic == nil then
current_panic = {
len = len_or_byte,
bytes = ""
}
else
current_panic.bytes ..= string.char(len_or_byte)
if #current_panic.bytes == current_panic.len then
error(current_panic.bytes, 0)
end
end
end
end
36 changes: 20 additions & 16 deletions examples/01-hello-lingua/src-luau/init.server.luau
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,31 @@
local lingua = require(script.Parent.lingua)
local example_wasm_loader = require(script.Parent.target_luau.example_wasm)

local wasm_env = {}
wasm_env.func_list = {}
local add_panic_handler = require(script.add_panic_handler)

-- Lingua will return an API for us later, when the module is initialised.
local lingua_api: lingua.Api

function wasm_env.func_list.ask_luau_to_say_hello(): lingua.DataFromLuauHandle
print("Saying hello to Rust...")
local data = {
greeting_from_luau = "Hello, Rust!"
}
local handle = lingua_api.send_to_rust(data)
return handle
end
local extern_fns = {
ask_luau_to_say_hello = function(): lingua.DataFromLuauHandle
print("Saying hello to Rust...")
local data = {
greeting_from_luau = "Hello, Rust!"
}
local handle = lingua_api.send_to_rust(data)
return handle
end,

function wasm_env.func_list.respond_to_luau_greeting(
response: lingua.DataFromRustHandle
): ()
local data = lingua_api.receive_from_rust(response)
print("Rust has responded:", data)
end
respond_to_luau_greeting = function(
response: lingua.DataFromRustHandle
): ()
local data = lingua_api.receive_from_rust(response)
print("Rust has responded:", data)
end
}
add_panic_handler(extern_fns)

local wasm_env = { func_list = extern_fns }
local finish_lingua_init = lingua.init(wasm_env)
local example_wasm_module = example_wasm_loader({env = wasm_env})
lingua_api = finish_lingua_init(example_wasm_module)
Expand Down
22 changes: 12 additions & 10 deletions examples/01-hello-lingua/src-rust/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
#[cfg(not(target_arch = "wasm32"))]
compile_error!("This project must target WebAssembly to compile correctly.");

use lingua::{receive_from_luau, send_to_luau};
use serde::{Serialize, Deserialize};

mod panic_handler;

// When you include a Rust module in a Luau project with Wasynth, you can't
// easily send complex data between the two. Extern functions can only send
// simple numbers.
Expand All @@ -11,17 +19,9 @@
// as your data can be represented neatly as JSON, it'll transfer to the
// other side just fine.

// -----------------------------------------------------------------------------

#[cfg(not(target_arch = "wasm32"))]
compile_error!("This project must target WebAssembly to compile correctly.");

use lingua::{receive_from_luau, send_to_luau};
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct LuauGreeting {
greeting_from_lua: String
greeting_from_luau: String
}

#[derive(Serialize)]
Expand All @@ -36,6 +36,8 @@ extern "C" {
}

fn main() {
panic_handler::connect();

unsafe {
let luau_greeting: LuauGreeting = receive_from_luau(
ask_luau_to_say_hello().into()
Expand All @@ -48,6 +50,6 @@ fn main() {

respond_to_luau_greeting(
send_to_luau(&rust_greeting).unwrap().into()
)
);
}
}
20 changes: 20 additions & 0 deletions examples/01-hello-lingua/src-rust/panic_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// This forwards on any panics to the Luau side, where they can become visible
// in the output.

use std::panic;

extern "C" {
fn panic_reporter(
len_or_byte: u32
);
}

pub fn connect() {
panic::set_hook(
Box::new(|panic| {
let foo = format!("{panic}");
unsafe { panic_reporter(foo.len() as u32); }
foo.bytes().for_each(|byte| unsafe { panic_reporter(byte as u32); });
})
);
}
13 changes: 10 additions & 3 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ use std::{cell::RefCell, collections::HashMap, num::Wrapping, panic::{catch_unwi
use serde::{de::DeserializeOwned, Serialize};
use thiserror::Error;

// Initialises a new string filled with a character that's easy to identify.
// If data isn't allocated property, the character can be seen, which aids in
// debugging bugs in memory writing.
fn new_string_filled(
num_bytes: u32
) -> String {
String::from_utf8((0..num_bytes).map(|_| '$' as u8).collect()).unwrap()
}

/// The return values of Lingua FFI functions are used to indicate whether the
/// FFI call was successful. Note that this has nothing to do with the specific
/// operation - it's specifically used to communicate low-level failures.
Expand Down Expand Up @@ -128,9 +137,7 @@ extern "C" fn lingua_send_json_to_rust_alloc(
) -> *mut u8 {
let mut return_ptr = 0 as *mut u8;
ffi_panic_boundary(AssertUnwindSafe(|| {
// Fill the string with something that's easy to recognise if part of
// the string remains uninitialised.
let mut str = String::from_iter((0..len).map(|_| '£'));
let mut str = new_string_filled(len);
assert!(
str.capacity() >= len as usize,
"[lingua] sanity check failed: send_json_to_rust_alloc string does \
Expand Down

0 comments on commit 125b9e3

Please sign in to comment.