Skip to content

Commit 421d1f7

Browse files
committed
update version
1 parent 0e580d0 commit 421d1f7

7 files changed

Lines changed: 161 additions & 39 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ exclude = [
99

1010
[workspace.dependencies]
1111
# local
12-
luars = { path = "crates/luars", version = "0.21.0" }
12+
luars = { path = "crates/luars", version = "0.21.1" }
1313
luars-derive = { path = "crates/luars-derive", version = "0.10.0" }
14-
luars_debugger = { path = "crates/luars_debugger", version = "0.21.0" }
14+
luars_debugger = { path = "crates/luars_debugger", version = "0.21.1" }
1515

1616
wasm-bindgen = "0.2"
1717
ahash = "0.8"

README.md

Lines changed: 105 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,96 @@
55

66
> **Note**: This is an **Lua 5.5** lib through AI-assisted programming.
77
8-
luars is a pure Rust Lua 5.5 runtime and embedding toolkit. This repository contains the core library, derive macros, the interpreter, debugger integration, a WASM target, and several host-facing examples.
8+
luars is a pure Rust Lua 5.5 runtime and embedding toolkit. This repository contains the core runtime, derive macros, the standalone interpreter, debugger integration, a WASM target, benchmark scripts, and host-facing examples.
99

10-
The repository-level documentation is intentionally focused on the high-level `Lua` API. Lower-level `LuaVM` APIs still exist, but the default examples and guides now use the high-level surface first.
10+
The project is shaped around three priorities:
1111

12-
## Quick Start
12+
- performance that is measured against native Lua instead of hand-waved
13+
- safety work that keeps `unsafe` narrowly scoped to representation and truly hot VM paths
14+
- compatibility validated against the upstream Lua test suite, not only custom examples
15+
16+
## Why luars
17+
18+
### Performance
19+
20+
The repository ships benchmark runners and published benchmark snapshots instead of a single cherry-picked number.
21+
22+
Current Windows snapshot on a Ryzen 7 5800X, measured against native Lua 5.5 on the same machine:
23+
24+
| Area | Relative throughput vs native Lua |
25+
|------|-----------------------------------|
26+
| Arithmetic | 111% |
27+
| Locals / register-heavy code | 132% |
28+
| Table library | 118% |
29+
| Coroutines | 152% |
30+
| Errors | 103% |
31+
32+
There is no repository-maintained Linux snapshot from a dedicated physical Linux machine yet, because the current benchmark process does not have a real Linux box behind it. For Linux runs, use the GitHub Actions benchmark workflow as the best continuous reference point.
33+
34+
Observed Linux behavior so far is mixed but fairly consistent: luars is often about 20% to 40% slower than native Lua on broad benchmark coverage, while still beating native Lua in a non-trivial subset of cases.
35+
36+
The current broader script-level snapshot is available here:
37+
38+
- [docs/benchmarks/windows.md](docs/benchmarks/windows.md)
39+
- [docs/benchmarks/macos.md](docs/benchmarks/macos.md)
40+
- GitHub Actions benchmark workflow: https://github.com/CppCXY/lua-rs/actions/workflows/benchmarks.yml
41+
42+
Benchmark commands are part of the repository:
43+
44+
- `./run_benchmarks.ps1` / `./run_benchmarks.sh`
45+
- `./run_lua_benchmarks.ps1` / `./run_lua_benchmarks.sh`
46+
47+
One important caveat for the Windows numbers: the coroutine result is unusually strong partly because native Lua's coroutine implementation on Windows pays heavily for `longjmp`-based control transfer, which triggers unwind-related overhead. luars does not inherit that exact cost model, so the Windows coroutine gap is real in measurement but should not be over-generalized into a platform-independent "always faster" claim.
48+
49+
### Safety And Audit Posture
50+
51+
luars is implemented in Rust, but it does not pretend that "written in Rust" automatically means the whole runtime is safe by default. The project keeps `unsafe` under active review and pushes non-critical paths back to safe Rust when that does not cost hot-path performance.
52+
53+
Recent cleanup work narrowed the unsafe surface in VM helper and dispatch-adjacent code, including helper, concat, call, and metamethod paths. The remaining `unsafe` usage is concentrated in the places where the runtime actually needs it:
54+
55+
- GC/object representation and tagged value layout
56+
- stack/upvalue pointer fast paths in hot interpreter execution
57+
- a small number of performance-sensitive VM internals where safe equivalents were measured to be worse
58+
59+
There are also Miri smoke tests in the repo for low-level runtime sanity checks.
60+
61+
### Compatibility And Test Status
62+
63+
The repository includes the official Lua test suite under `lua_tests/testes`, plus the scripts needed to run it directly.
64+
65+
Current status from a fresh release-mode run in this workspace:
66+
67+
- `./run_lua_tests.ps1 -Profile release -Script all.lua -SkipBuild`
68+
- Result: `final OK !!!`
69+
70+
That means the current tree passes the upstream Lua test suite entrypoint used by this repository, with the expected skips for `testC`-dependent cases that are not active in this environment.
71+
72+
## Crate Features
73+
74+
Core crate feature flags for `luars`:
75+
76+
| Feature | Purpose |
77+
|---------|---------|
78+
| `serde` | Enable `serde` / `serde_json` integration for Lua value conversion and host interop |
79+
| `sandbox` | Enable sandbox-oriented helpers and isolated execution entry points |
80+
| `shared-proto` | Reuse cached compiled proto/constant-string state across loads/VM instances to reduce duplicate compilation and allocation work |
81+
82+
Minimal dependency:
1383

1484
```toml
1585
[dependencies]
16-
luars = "0.18"
86+
luars = "0.21"
1787
```
1888

89+
With optional features:
90+
91+
```toml
92+
[dependencies]
93+
luars = { version = "0.21", features = ["serde", "sandbox", "shared-proto"] }
94+
```
95+
96+
## Quick Start
97+
1998
```rust
2099
use luars::{Lua, SafeOption, Stdlib};
21100

@@ -31,68 +110,65 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
31110
}
32111
```
33112

34-
## Benchmarks
35-
36-
see
37-
- [docs/benchmarks/macos.md](docs/benchmarks/macos.md) for the latest macOS benchmark snapshot
38-
- [docs/benchmarks/windows.md](docs/benchmarks/windows.md) for the latest Windows benchmark snapshot
39-
- I don't have a physical Linux machine, but you can refer to the GitHub Actions VM CI results: https://github.com/CppCXY/lua-rs/actions. If this repository includes a `benchmarks` workflow, view it directly at https://github.com/CppCXY/lua-rs/actions/workflows/benchmarks.yml
40-
41-
## What The High-Level API Covers
113+
## High-Level API Highlights
42114

43115
- Execute chunks with `lua.load(...).exec()`, `eval()`, and `eval_multi()`
44116
- Execute async chunks with `exec_async()`, `eval_async()`, and `eval_multi_async()`
45-
- Call Lua globals with `call_global()` / `call_global1()` and their async variants
46-
- Register Rust functions with `register_function()` and `register_async_function()`
117+
- Call Lua globals with `call_global()` / `call_global1()` and async variants
118+
- Register Rust functions with typed and untyped callback APIs
47119
- Expose Rust types with `register_type()` and `LuaUserData`
48120
- Work with globals and tables through `globals()`, `create_table()`, and `create_table_from()`
49121
- Create scoped borrowed callbacks and userdata through `scope(...)`
50-
- Run isolated chunks through `load_sandboxed()` and `execute_sandboxed()` when the `sandbox` feature is enabled
122+
- Run isolated chunks through sandbox helpers when the `sandbox` feature is enabled
51123

52124
## Repository Layout
53125

54126
| Path | Description |
55127
|------|-------------|
56-
| `crates/luars` | Core library with the compiler, VM, GC, and high-level `Lua` API |
57-
| `crates/luars-derive` | `LuaUserData` and `lua_methods` macros |
58-
| `crates/luars_interpreter` | CLI interpreter and bytecode tools |
128+
| `crates/luars` | Core library: compiler, VM, GC, string/table runtime, and high-level `Lua` API |
129+
| `crates/luars-derive` | `LuaUserData` and related derive/macros |
130+
| `crates/luars_interpreter` | Standalone interpreter, tooling, and benchmark entrypoints |
59131
| `crates/luars_debugger` | Debugger integration |
60132
| `crates/luars_wasm` | WASM bindings |
61-
| `docs/` | High-level embedding guides |
62-
| `examples/` | Host examples built around the high-level API |
133+
| `benchmarks/` | Repository benchmark scripts used for performance tracking |
134+
| `lua_tests/` | Upstream Lua test suite used for compatibility validation |
135+
| `docs/` | Guides, benchmark snapshots, async notes, and user-facing docs |
136+
| `examples/` | Embedding and host integration examples |
63137

64138
## Examples
65139

66140
| Example | Description |
67141
|---------|-------------|
68-
| [examples/luars-example](examples/luars-example) | Minimal high-level API example: globals, userdata, and scope |
69-
| [examples/rules-engine-demo](examples/rules-engine-demo) | Business rules engine with Rust host functions and Lua policy |
70-
| [examples/http-server](examples/http-server) | Async HTTP example using high-level async calls and sandboxed Lua request handlers |
71-
| [examples/rust-bind-bench](examples/rust-bind-bench) | High-level userdata registration benchmark |
142+
| [examples/luars-example](examples/luars-example) | Minimal host embedding example |
143+
| [examples/rules-engine-demo](examples/rules-engine-demo) | Rules engine with Rust host functions and Lua policy |
144+
| [examples/http-server](examples/http-server) | Async HTTP example with sandboxed request execution |
145+
| [examples/rust-bind-bench](examples/rust-bind-bench) | Binding and userdata benchmark example |
72146

73147
## Documentation
74148

75149
| Document | Description |
76150
|----------|-------------|
77151
| [docs/Guide.md](docs/Guide.md) | High-level `Lua` API overview |
78-
| [docs/UserGuide.md](docs/UserGuide.md) | High-level userdata guide |
79-
| [docs/Async.md](docs/Async.md) | High-level async and sandbox status |
152+
| [docs/UserGuide.md](docs/UserGuide.md) | Userdata and embedding guide |
153+
| [docs/Async.md](docs/Async.md) | Async execution and related notes |
80154
| [docs/Different.md](docs/Different.md) | Known differences from C Lua |
81155
| [crates/luars/README.md](crates/luars/README.md) | Crate-level documentation |
82156

83-
## Validate
157+
## Validation Commands
84158

85159
```bash
86160
cargo test
87161
```
88162

89-
Miri smoke tests:
163+
```bash
164+
./run_lua_tests.ps1 -Profile release -Script all.lua
165+
```
90166

91167
```bash
92168
MIRIFLAGS="-Zmiri-disable-stacked-borrows -Zmiri-permissive-provenance" cargo +nightly miri test -p luars --lib miri_ -- --nocapture
93169
```
94170

95-
The Miri profile disables stacked borrows and uses permissive provenance because luars currently relies on tagged-pointer GC/object representations that intentionally compress and reconstruct raw pointers.
171+
The Miri profile disables stacked borrows and uses permissive provenance because luars relies on tagged-pointer and compact GC/object layouts that intentionally reconstruct raw pointers.
96172

97173
## License
98174

crates/luars/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "luars"
3-
version = "0.21.0"
3+
version = "0.21.1"
44
edition = "2024"
55
authors = ["CppCXY"]
66
description = "A library for lua 5.5 runtime implementation in Rust"

crates/luars/src/lua_api/lua.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,17 @@ impl Lua {
579579
self.vm.into_full_error(error)
580580
}
581581

582+
/// Stop the garbage collector. No GC steps will run until `gc_restart` is called.
583+
pub fn gc_stop(&mut self) {
584+
self.vm_mut().gc.gc_stopped = true;
585+
}
586+
587+
/// Restart the garbage collector after it has been stopped.
588+
pub fn gc_restart(&mut self) {
589+
self.vm_mut().gc.gc_stopped = false;
590+
self.vm_mut().gc.set_debt(0);
591+
}
592+
582593
/// Get a mutable reference to the underlying LuaVM for advanced use cases.
583594
///
584595
/// # Safety

crates/luars/src/lua_value/lua_table/native_table.rs

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -401,12 +401,17 @@ impl NativeTable {
401401
}
402402

403403
let mp = self.mainposition_string(key);
404-
let key_ptr = unsafe { key.value.i };
404+
let key_ptr = StringPtr::new(unsafe { key.value.ptr as *mut GcString });
405405

406406
unsafe {
407407
let mut node = mp;
408408
loop {
409-
if (*node).key_tt == LUA_VSHRSTR && (*node).key_data.i == key_ptr {
409+
if (*node).key_tt == LUA_VSHRSTR
410+
&& short_string_ptr_eq(
411+
StringPtr::new((*node).key_data.ptr as *mut GcString),
412+
key_ptr,
413+
)
414+
{
410415
if (*node).val_tt != LUA_VNIL {
411416
(*node).set_value(value);
412417
return ShortStrSetResult::Done {
@@ -521,12 +526,17 @@ impl NativeTable {
521526
}
522527

523528
let mp = self.mainposition_string(key);
524-
let key_ptr = unsafe { key.value.i };
529+
let key_ptr = StringPtr::new(unsafe { key.value.ptr as *mut GcString });
525530

526531
unsafe {
527532
let mut node = mp;
528533
loop {
529-
if (*node).key_tt == LUA_VSHRSTR && (*node).key_data.i == key_ptr {
534+
if (*node).key_tt == LUA_VSHRSTR
535+
&& short_string_ptr_eq(
536+
StringPtr::new((*node).key_data.ptr as *mut GcString),
537+
key_ptr,
538+
)
539+
{
530540
if (*node).val_tt != LUA_VNIL {
531541
(*node).set_value_parts(value, tt);
532542
return ShortStrSetResult::Done {
@@ -2322,4 +2332,29 @@ mod tests {
23222332

23232333
assert_eq!(table.raw_get(&local_key), Some(value));
23242334
}
2335+
2336+
#[cfg(feature = "shared-proto")]
2337+
#[test]
2338+
fn test_shared_short_string_update_with_local_query_uses_existing_slot() {
2339+
let key = "PTYPE";
2340+
let mut shared_interner = StringInterner::new();
2341+
let mut local_interner = StringInterner::new();
2342+
let mut shared_gc = GC::new(SafeOption::default());
2343+
let mut local_gc = GC::new(SafeOption::default());
2344+
2345+
let mut shared_key = shared_interner.intern(key, &mut shared_gc).unwrap();
2346+
let local_key = local_interner.intern(key, &mut local_gc).unwrap();
2347+
2348+
assert!(share_lua_value(&mut shared_key));
2349+
2350+
let mut table = NativeTable::new(0, 4);
2351+
table.raw_set(&shared_key, LuaValue::integer(1));
2352+
2353+
let result = table.pset_shortstr(&local_key, LuaValue::integer(3));
2354+
let (new_key, _) = table.finish_shortstr_set(&local_key, LuaValue::integer(3), result);
2355+
2356+
assert!(!new_key);
2357+
assert_eq!(table.raw_get(&shared_key), Some(LuaValue::integer(3)));
2358+
assert_eq!(table.raw_get(&local_key), Some(LuaValue::integer(3)));
2359+
}
23252360
}

crates/luars_debugger/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "luars_debugger"
3-
version = "0.21.0"
3+
version = "0.21.1"
44
edition = "2024"
55
description = "Built-in debugger for luars, loaded via require('emmy_core')"
66
authors = ["CppCXY"]

0 commit comments

Comments
 (0)