Skip to main content

luaur_web/functions/
execute_script.rs

1//! `extern "C" const char* executeScript(const char* source)`
2//! (`CLI/src/Web.cpp:184-208`).
3//!
4//! The wasm entry point: enables every `Luau*` bool fast flag, spins up a fresh
5//! sandboxed Lua state, runs the script via [`run_code`], and returns the result
6//! string (or null when empty). The C++ caches the result in a function-`static`
7//! `std::string` so the returned pointer outlives the call; the Rust analog is a
8//! thread-local `CString` whose pointer is returned.
9
10use crate::functions::run_code::run_code;
11use crate::functions::setup_state::setup_state;
12use core::cell::RefCell;
13use core::ffi::{c_char, CStr};
14use std::ffi::CString;
15
16use luaur_common::set_luau_bool_flags;
17use luaur_vm::functions::lua_close::lua_close;
18use luaur_vm::functions::lua_l_newstate::lua_l_newstate;
19use luaur_vm::functions::lua_l_sandboxthread::lua_l_sandboxthread;
20use luaur_vm::type_aliases::lua_state::lua_State;
21
22thread_local! {
23    /// Mirror of the C++ `static std::string result;` — keeps the returned
24    /// C string alive after the call returns.
25    static RESULT: RefCell<Option<CString>> = const { RefCell::new(None) };
26}
27
28/// # Safety
29/// `source` must be a valid, NUL-terminated C string (the wasm/JS caller's
30/// contract), or null.
31#[cfg_attr(not(test), no_mangle)]
32pub unsafe extern "C" fn execute_script(source: *const c_char) -> *const c_char {
33    // setup flags:
34    //   for (FValue<bool>* flag = FValue<bool>::list; flag; flag = flag->next)
35    //       if (strncmp(flag->name, "Luau", 4) == 0)
36    //           flag->value = true;
37    //
38    // In this port the per-type intrusive `FValue<bool>::list` is never
39    // populated (Rust statics cannot self-register in a ctor, and no startup
40    // `register()` runs), so the C++ name-prefix walk is expressed through the
41    // crate's public flag-enabling analog `set_luau_bool_flags`, which turns on
42    // every non-`Debug` bool FastFlag (the `Luau*` flags dominate that set).
43    set_luau_bool_flags(true);
44
45    // create new state: unique_ptr<lua_State, lua_close> globalState(luaL_newstate(), lua_close);
46    let l: *mut lua_State = lua_l_newstate();
47
48    // setup state
49    setup_state(l);
50
51    // sandbox thread
52    lua_l_sandboxthread(l);
53
54    // run code + collect error
55    let source_str = if source.is_null() {
56        ""
57    } else {
58        core::str::from_utf8_unchecked(CStr::from_ptr(source).to_bytes())
59    };
60    let result = run_code(l, source_str);
61
62    // unique_ptr destructor: lua_close(L)
63    lua_close(l);
64
65    if result.is_empty() {
66        // cache empty (drop any prior held string) and return nullptr.
67        RESULT.with(|r| *r.borrow_mut() = None);
68        return core::ptr::null();
69    }
70
71    RESULT.with(|r| {
72        let cstring = CString::new(result).unwrap_or_default();
73        let ptr = cstring.as_ptr();
74        *r.borrow_mut() = Some(cstring);
75        ptr
76    })
77}