Skip to content

Commit 2970405

Browse files
authored
extern.load - load shared libraries at runtime (#123)
Allow users to write and load plugins that expose a symbol `seal_open_extern`. The Luau state is passed as a `*mut lua_State` to the shared library which can do whatever it wants.
1 parent bf7b9f5 commit 2970405

File tree

17 files changed

+832
-141
lines changed

17 files changed

+832
-141
lines changed

.seal/typedefs/interop/extern.luau

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
2+
--[=[
3+
Load external libraries at runtime.
4+
5+
External libraries (also called plugins), can be used to expand *seal*'s functionality,
6+
add bindings to C and Rust libraries, and allow performance-critical code execution without mluau or Luau VM overhead.
7+
]=]
8+
export type extern = {
9+
--[=[
10+
Loads the native dynamic library at `path`, calling the symbol `seal_open_extern` in that library with a pointer to the Luau state.
11+
12+
It is expected that calling `seal_open_extern` pushes 1 value directly onto the Luau stack. This function returns that value unchanged.
13+
14+
This function does not handle platform-specific behavior; it is expected the caller loads the correct dynamic library for
15+
supported platforms/architectures.
16+
17+
# ⚠️ Safety
18+
19+
This function is extremely unsafe.
20+
21+
External libraries can easily bypass Rust's and mluau's safety guarantees, including but not limited to:
22+
- causing memory safety vulnerabilities,
23+
- incorrectly using the Luau stack (causing hard crashes or UB),
24+
- modifying the Luau state in cursed ways,
25+
- execute arbitrary code.
26+
27+
The caller is responsible for ensuring the library:
28+
29+
- is compatible with the caller's operating system, platform, and architecture,
30+
- is a *seal* extern library, not any other shared/dynamic library,
31+
- contains a *not mangled* function symbol `seal_open_extern` that:
32+
- is in the C ABI (use `"C-unwind"` in Rust),
33+
- takes in a Luau `lua_State` pointer as its first argument,
34+
- returns `1` as an `i32 (c_int)`.
35+
36+
Library/plugin maintainers are responsible for ensuring the library:
37+
38+
- uses the Luau stack correctly,
39+
- does NOT share the passed `lua_State` between multiple OS threads,
40+
- does NOT free memory owned by Luau,
41+
- correctly links to Luau (not Lua) for using the Luau stack,
42+
- should not throw an uncaught foreign exception or Rust panic.
43+
44+
# Errors
45+
46+
Throws an error if:
47+
48+
- The dynamic library cannot be found (usually with a message like "dlopen failed"),
49+
- The dynamic library is missing the symbol `seal_open_extern`,
50+
- The dynamic library could not be loaded for a different reason,
51+
- `seal_open_extern` does not return 1 (c_int),
52+
- `seal_open_extern` returns 1 but does not push exactly 1 value to the Luau stack.
53+
]=]
54+
load: (path: string) -> unknown,
55+
}
56+
57+
return {} :: extern

CONTRIBUTING.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,12 @@ A library is a good candidate for `@std` if:
4242
5. please don't run a formatter over the whole codebase.
4343
1. I don't mind if `wrap_err!`s go off the RHS of the page if that means vertical space is better used for code.
4444
2. On the other hand, let's try to keep non-wrap_err! code and comments to 85-110 colwidth?
45-
6. Documentation goes in `./.seal/typedefs/std/*`.
45+
6. Inline documentation goes in `./.seal/typedefs/std/*`.
4646
1. For documentation, try to stick with the newer docs headers like seen in the `@std/process` API docs.
4747
2. No Moonwave `@attributes`, they make code less readable and we don't use Moonwave.
48-
7. Register the library in `./src/require/mod.rs` and `./.seal/typedefs/init.luau` in 3 places:
48+
7. After you finish writing inline documentation, regenerate markdown documentation by running `seal ./docs/docscripts/reference.luau`. Generated docs aren't going to be perfect, we'll fix them later.
49+
8. Register the library in `./src/require/mod.rs` and `./.seal/typedefs/init.luau` in 3 places:
4950
1. The Big Beautiful Table (BBT) in `./src/require/mod.rs`, which handles when the library is required directly (`@std/mylib`)
5051
2. the Small Beautiful Table in `./src/require/mod.rs`, which handles when the entire `@std` is required at once.
5152
3. in `./.seal/typedefs/init.luau` to provide `require` support when the entire `@std` is required at once.
52-
8. Add tests in `./tests/luau/std/libname/*` or `./tests/luau/std/libname.luau`.
53+
9. Add tests in `./tests/luau/std/libname/*` or `./tests/luau/std/libname.luau`.

0 commit comments

Comments
 (0)