Skip to content

Commit 39cac56

Browse files
committed
Add unwinding support (returning an Error) to Luau Require implementation
1 parent e6e1ef0 commit 39cac56

File tree

5 files changed

+125
-46
lines changed

5 files changed

+125
-46
lines changed

mlua-sys/src/luau/luarequire.rs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@ pub enum luarequire_WriteResult {
2626
#[repr(C)]
2727
pub struct luarequire_Configuration {
2828
// Returns whether requires are permitted from the given chunkname.
29-
pub is_require_allowed:
30-
unsafe extern "C" fn(L: *mut lua_State, ctx: *mut c_void, requirer_chunkname: *const c_char) -> bool,
29+
pub is_require_allowed: unsafe extern "C-unwind" fn(
30+
L: *mut lua_State,
31+
ctx: *mut c_void,
32+
requirer_chunkname: *const c_char,
33+
) -> bool,
3134

3235
// Resets the internal state to point at the requirer module.
33-
pub reset: unsafe extern "C" fn(
36+
pub reset: unsafe extern "C-unwind" fn(
3437
L: *mut lua_State,
3538
ctx: *mut c_void,
3639
requirer_chunkname: *const c_char,
@@ -39,26 +42,27 @@ pub struct luarequire_Configuration {
3942
// Resets the internal state to point at an aliased module, given its exact path from a configuration
4043
// file. This function is only called when an alias's path cannot be resolved relative to its
4144
// configuration file.
42-
pub jump_to_alias: unsafe extern "C" fn(
45+
pub jump_to_alias: unsafe extern "C-unwind" fn(
4346
L: *mut lua_State,
4447
ctx: *mut c_void,
4548
path: *const c_char,
4649
) -> luarequire_NavigateResult,
4750

4851
// Navigates through the context by making mutations to the internal state.
49-
pub to_parent: unsafe extern "C" fn(L: *mut lua_State, ctx: *mut c_void) -> luarequire_NavigateResult,
50-
pub to_child: unsafe extern "C" fn(
52+
pub to_parent:
53+
unsafe extern "C-unwind" fn(L: *mut lua_State, ctx: *mut c_void) -> luarequire_NavigateResult,
54+
pub to_child: unsafe extern "C-unwind" fn(
5155
L: *mut lua_State,
5256
ctx: *mut c_void,
5357
name: *const c_char,
5458
) -> luarequire_NavigateResult,
5559

5660
// Returns whether the context is currently pointing at a module.
57-
pub is_module_present: unsafe extern "C" fn(L: *mut lua_State, ctx: *mut c_void) -> bool,
61+
pub is_module_present: unsafe extern "C-unwind" fn(L: *mut lua_State, ctx: *mut c_void) -> bool,
5862

5963
// Provides a chunkname for the current module. This will be accessible through the debug library. This
6064
// function is only called if is_module_present returns true.
61-
pub get_chunkname: unsafe extern "C" fn(
65+
pub get_chunkname: unsafe extern "C-unwind" fn(
6266
L: *mut lua_State,
6367
ctx: *mut c_void,
6468
buffer: *mut c_char,
@@ -68,7 +72,7 @@ pub struct luarequire_Configuration {
6872

6973
// Provides a loadname that identifies the current module and is passed to load. This function
7074
// is only called if is_module_present returns true.
71-
pub get_loadname: unsafe extern "C" fn(
75+
pub get_loadname: unsafe extern "C-unwind" fn(
7276
L: *mut lua_State,
7377
ctx: *mut c_void,
7478
buffer: *mut c_char,
@@ -78,7 +82,7 @@ pub struct luarequire_Configuration {
7882

7983
// Provides a cache key representing the current module. This function is only called if
8084
// is_module_present returns true.
81-
pub get_cache_key: unsafe extern "C" fn(
85+
pub get_cache_key: unsafe extern "C-unwind" fn(
8286
L: *mut lua_State,
8387
ctx: *mut c_void,
8488
buffer: *mut c_char,
@@ -89,15 +93,15 @@ pub struct luarequire_Configuration {
8993
// Returns whether a configuration file is present in the current context.
9094
// If not, require-by-string will call to_parent until either a configuration file is present or
9195
// NAVIGATE_FAILURE is returned (at root).
92-
pub is_config_present: unsafe extern "C" fn(L: *mut lua_State, ctx: *mut c_void) -> bool,
96+
pub is_config_present: unsafe extern "C-unwind" fn(L: *mut lua_State, ctx: *mut c_void) -> bool,
9397

9498
// Parses the configuration file in the current context for the given alias and returns its
9599
// value or WRITE_FAILURE if not found. This function is only called if is_config_present
96100
// returns true. If this function pointer is set, get_config must not be set. Opting in to this
97101
// function pointer disables parsing configuration files internally and can be used for finer
98102
// control over the configuration file parsing process.
99103
pub get_alias: Option<
100-
unsafe extern "C" fn(
104+
unsafe extern "C-unwind" fn(
101105
L: *mut lua_State,
102106
ctx: *mut c_void,
103107
alias: *const c_char,
@@ -111,7 +115,7 @@ pub struct luarequire_Configuration {
111115
// if is_config_present returns true. If this function pointer is set, get_alias must not be set. Opting
112116
// in to this function pointer enables parsing configuration files internally.
113117
pub get_config: Option<
114-
unsafe extern "C" fn(
118+
unsafe extern "C-unwind" fn(
115119
L: *mut lua_State,
116120
ctx: *mut c_void,
117121
buffer: *mut c_char,
@@ -134,7 +138,7 @@ pub struct luarequire_Configuration {
134138
}
135139

136140
// Populates function pointers in the given luarequire_Configuration.
137-
pub type luarequire_Configuration_init = unsafe extern "C" fn(config: *mut luarequire_Configuration);
141+
pub type luarequire_Configuration_init = unsafe extern "C-unwind" fn(config: *mut luarequire_Configuration);
138142

139143
unsafe extern "C-unwind" {
140144
// Initializes and pushes the require closure onto the stack without registration.

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ pub use crate::{
132132
buffer::Buffer,
133133
chunk::{CompileConstant, Compiler},
134134
function::CoverageInfo,
135-
luau::{NavigateError, Require},
135+
luau::{NavigateError, Require, TextRequirer},
136136
vector::Vector,
137137
};
138138

src/luau/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::function::Function;
88
use crate::state::{callback_error_ext, Lua};
99
use crate::traits::{FromLuaMulti, IntoLua};
1010

11-
pub use require::{NavigateError, Require};
11+
pub use require::{NavigateError, Require, TextRequirer};
1212

1313
// Since Luau has some missing standard functions, we re-implement them here
1414

src/luau/require.rs

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,39 +8,51 @@ use std::path::{Component, Path, PathBuf};
88
use std::result::Result as StdResult;
99
use std::{env, fmt, fs, mem, ptr};
1010

11-
use crate::error::Result;
11+
use crate::error::{Error, Result};
1212
use crate::function::Function;
1313
use crate::state::{callback_error_ext, Lua};
1414
use crate::table::Table;
1515
use crate::types::MaybeSend;
1616

1717
/// An error that can occur during navigation in the Luau `require` system.
18-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18+
#[cfg(any(feature = "luau", doc))]
19+
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
20+
#[derive(Debug, Clone)]
1921
pub enum NavigateError {
2022
Ambiguous,
2123
NotFound,
24+
Other(Error),
2225
}
2326

2427
#[cfg(feature = "luau")]
2528
trait IntoNavigateResult {
26-
fn into_nav_result(self) -> ffi::luarequire_NavigateResult;
29+
fn into_nav_result(self) -> Result<ffi::luarequire_NavigateResult>;
2730
}
2831

2932
#[cfg(feature = "luau")]
3033
impl IntoNavigateResult for StdResult<(), NavigateError> {
31-
fn into_nav_result(self) -> ffi::luarequire_NavigateResult {
34+
fn into_nav_result(self) -> Result<ffi::luarequire_NavigateResult> {
3235
match self {
33-
Ok(()) => ffi::luarequire_NavigateResult::Success,
34-
Err(NavigateError::Ambiguous) => ffi::luarequire_NavigateResult::Ambiguous,
35-
Err(NavigateError::NotFound) => ffi::luarequire_NavigateResult::NotFound,
36+
Ok(()) => Ok(ffi::luarequire_NavigateResult::Success),
37+
Err(NavigateError::Ambiguous) => Ok(ffi::luarequire_NavigateResult::Ambiguous),
38+
Err(NavigateError::NotFound) => Ok(ffi::luarequire_NavigateResult::NotFound),
39+
Err(NavigateError::Other(err)) => Err(err),
3640
}
3741
}
3842
}
3943

44+
impl From<Error> for NavigateError {
45+
fn from(err: Error) -> Self {
46+
NavigateError::Other(err)
47+
}
48+
}
49+
4050
#[cfg(feature = "luau")]
4151
type WriteResult = ffi::luarequire_WriteResult;
4252

4353
/// A trait for handling modules loading and navigation in the Luau `require` system.
54+
#[cfg(any(feature = "luau", doc))]
55+
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
4456
pub trait Require: MaybeSend {
4557
/// Returns `true` if "require" is permitted for the given chunk name.
4658
fn is_require_allowed(&self, chunk_name: &str) -> bool;
@@ -92,15 +104,17 @@ impl fmt::Debug for dyn Require {
92104
}
93105

94106
/// The standard implementation of Luau `require` navigation.
107+
#[doc(hidden)]
95108
#[derive(Default, Debug)]
96-
pub(super) struct TextRequirer {
109+
pub struct TextRequirer {
97110
abs_path: PathBuf,
98111
rel_path: PathBuf,
99112
module_path: PathBuf,
100113
}
101114

102115
impl TextRequirer {
103-
pub(super) fn new() -> Self {
116+
/// Creates a new `TextRequirer` instance.
117+
pub fn new() -> Self {
104118
Self::default()
105119
}
106120

@@ -308,12 +322,12 @@ macro_rules! try_borrow_mut {
308322
}
309323

310324
#[cfg(feature = "luau")]
311-
pub(super) unsafe extern "C" fn init_config(config: *mut ffi::luarequire_Configuration) {
325+
pub(super) unsafe extern "C-unwind" fn init_config(config: *mut ffi::luarequire_Configuration) {
312326
if config.is_null() {
313327
return;
314328
}
315329

316-
unsafe extern "C" fn is_require_allowed(
330+
unsafe extern "C-unwind" fn is_require_allowed(
317331
state: *mut ffi::lua_State,
318332
ctx: *mut c_void,
319333
requirer_chunkname: *const c_char,
@@ -327,50 +341,58 @@ pub(super) unsafe extern "C" fn init_config(config: *mut ffi::luarequire_Configu
327341
this.is_require_allowed(&chunk_name)
328342
}
329343

330-
unsafe extern "C" fn reset(
344+
unsafe extern "C-unwind" fn reset(
331345
state: *mut ffi::lua_State,
332346
ctx: *mut c_void,
333347
requirer_chunkname: *const c_char,
334348
) -> ffi::luarequire_NavigateResult {
335349
let mut this = try_borrow_mut!(state, ctx);
336350
let chunk_name = CStr::from_ptr(requirer_chunkname).to_string_lossy();
337-
this.reset(&chunk_name).into_nav_result()
351+
callback_error_ext(state, ptr::null_mut(), true, move |_, _| {
352+
this.reset(&chunk_name).into_nav_result()
353+
})
338354
}
339355

340-
unsafe extern "C" fn jump_to_alias(
356+
unsafe extern "C-unwind" fn jump_to_alias(
341357
state: *mut ffi::lua_State,
342358
ctx: *mut c_void,
343359
path: *const c_char,
344360
) -> ffi::luarequire_NavigateResult {
345361
let mut this = try_borrow_mut!(state, ctx);
346362
let path = CStr::from_ptr(path).to_string_lossy();
347-
this.jump_to_alias(&path).into_nav_result()
363+
callback_error_ext(state, ptr::null_mut(), true, move |_, _| {
364+
this.jump_to_alias(&path).into_nav_result()
365+
})
348366
}
349367

350-
unsafe extern "C" fn to_parent(
368+
unsafe extern "C-unwind" fn to_parent(
351369
state: *mut ffi::lua_State,
352370
ctx: *mut c_void,
353371
) -> ffi::luarequire_NavigateResult {
354372
let mut this = try_borrow_mut!(state, ctx);
355-
this.to_parent().into_nav_result()
373+
callback_error_ext(state, ptr::null_mut(), true, move |_, _| {
374+
this.to_parent().into_nav_result()
375+
})
356376
}
357377

358-
unsafe extern "C" fn to_child(
378+
unsafe extern "C-unwind" fn to_child(
359379
state: *mut ffi::lua_State,
360380
ctx: *mut c_void,
361381
name: *const c_char,
362382
) -> ffi::luarequire_NavigateResult {
363383
let mut this = try_borrow_mut!(state, ctx);
364384
let name = CStr::from_ptr(name).to_string_lossy();
365-
this.to_child(&name).into_nav_result()
385+
callback_error_ext(state, ptr::null_mut(), true, move |_, _| {
386+
this.to_child(&name).into_nav_result()
387+
})
366388
}
367389

368-
unsafe extern "C" fn is_module_present(state: *mut ffi::lua_State, ctx: *mut c_void) -> bool {
390+
unsafe extern "C-unwind" fn is_module_present(state: *mut ffi::lua_State, ctx: *mut c_void) -> bool {
369391
let this = try_borrow!(state, ctx);
370392
this.has_module()
371393
}
372394

373-
unsafe extern "C" fn get_chunkname(
395+
unsafe extern "C-unwind" fn get_chunkname(
374396
_state: *mut ffi::lua_State,
375397
_ctx: *mut c_void,
376398
buffer: *mut c_char,
@@ -380,7 +402,7 @@ pub(super) unsafe extern "C" fn init_config(config: *mut ffi::luarequire_Configu
380402
write_to_buffer(buffer, buffer_size, size_out, &[])
381403
}
382404

383-
unsafe extern "C" fn get_loadname(
405+
unsafe extern "C-unwind" fn get_loadname(
384406
_state: *mut ffi::lua_State,
385407
_ctx: *mut c_void,
386408
buffer: *mut c_char,
@@ -390,7 +412,7 @@ pub(super) unsafe extern "C" fn init_config(config: *mut ffi::luarequire_Configu
390412
write_to_buffer(buffer, buffer_size, size_out, &[])
391413
}
392414

393-
unsafe extern "C" fn get_cache_key(
415+
unsafe extern "C-unwind" fn get_cache_key(
394416
state: *mut ffi::lua_State,
395417
ctx: *mut c_void,
396418
buffer: *mut c_char,
@@ -402,22 +424,20 @@ pub(super) unsafe extern "C" fn init_config(config: *mut ffi::luarequire_Configu
402424
write_to_buffer(buffer, buffer_size, size_out, cache_key.as_bytes())
403425
}
404426

405-
unsafe extern "C" fn is_config_present(state: *mut ffi::lua_State, ctx: *mut c_void) -> bool {
427+
unsafe extern "C-unwind" fn is_config_present(state: *mut ffi::lua_State, ctx: *mut c_void) -> bool {
406428
let this = try_borrow!(state, ctx);
407429
this.has_config()
408430
}
409431

410-
unsafe extern "C" fn get_config(
432+
unsafe extern "C-unwind" fn get_config(
411433
state: *mut ffi::lua_State,
412434
ctx: *mut c_void,
413435
buffer: *mut c_char,
414436
buffer_size: usize,
415437
size_out: *mut usize,
416438
) -> WriteResult {
417439
let this = try_borrow!(state, ctx);
418-
let Ok(config) = this.config() else {
419-
return WriteResult::Failure;
420-
};
440+
let config = callback_error_ext(state, ptr::null_mut(), true, move |_, _| Ok(this.config()?));
421441
write_to_buffer(buffer, buffer_size, size_out, &config)
422442
}
423443

@@ -429,7 +449,7 @@ pub(super) unsafe extern "C" fn init_config(config: *mut ffi::luarequire_Configu
429449
_loadname: *const c_char,
430450
) -> c_int {
431451
let this = try_borrow!(state, ctx);
432-
callback_error_ext(state, ptr::null_mut(), false, move |extra, _| {
452+
callback_error_ext(state, ptr::null_mut(), true, move |extra, _| {
433453
let rawlua = (*extra).raw_lua();
434454
let loader = this.loader(rawlua.lua())?;
435455
rawlua.push(loader)?;

0 commit comments

Comments
 (0)