mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0711c614c7 | |||
| 743325f7d6 | |||
| 0b365e92a9 | |||
| 15fb63b2a2 | |||
| 0849d05c83 | |||
| 38c05b850e | |||
| 8d1841f8cf | |||
| e9271d2e32 | |||
| e263220fb3 | |||
| e6d16815d7 | |||
| ae88e8acf8 | |||
| fcab60bac4 | |||
| 208a70f407 | |||
| ca360f9019 | |||
| a7c5a24a7b | |||
| b7c98ad9bb | |||
| 1f3dafa564 | |||
| 1d4a756436 | |||
| 6e7d6c78ed | |||
| 023e4c61d8 | |||
| 92bd06d3c1 | |||
| d8544bf038 | |||
| 7114c03489 | |||
| f4cacc524e | |||
| cc7f7ce7b7 | |||
| 72de602ec3 | |||
| 1573dd1242 | |||
| 39d3201848 | |||
| 4aa6214b45 | |||
| 72824a468a | |||
| c54b90623c | |||
| 5f0e06fb66 | |||
| 181c9d07b7 | |||
| 4e827179d1 | |||
| cc26dcd4ff | |||
| 201e30bc07 | |||
| 4e028d8409 | |||
| 3d1ae981d3 | |||
| 8c93948f2f | |||
| f2b5cc44de | |||
| 27f91dfd1b | |||
| 75ff11f795 | |||
| df6097ab38 | |||
| 65bb6279ee | |||
| 31b88e85bb | |||
| 3be4745190 | |||
| c52deec988 | |||
| 3ab3c997b3 | |||
| 5872ed70f5 | |||
| e7e92b4f6f | |||
| d27693b61a | |||
| c9848d6faf | |||
| 9126bb8ce0 | |||
| 7f1d716a44 | |||
| be56e2205c | |||
| c5aadc68cd | |||
| a5ae2a1fc3 | |||
| 59872da63d | |||
| a24d2151af | |||
| 56c227fd7e | |||
| d5d66abe42 | |||
| a2d8b21964 | |||
| a9604c4946 | |||
| 81ae8e1393 | |||
| a959b98d30 | |||
| efd0856033 | |||
| c91066006f | |||
| a45fe9bb93 | |||
| f1a97e4193 | |||
| 47e6a37323 | |||
| bf0c96908f | |||
| 8817720362 | |||
| 35294359ad | |||
| eb76db59da | |||
| 33bf3ffde7 | |||
| 0f3fdb0539 | |||
| 79d438aaad | |||
| 30cf4bef58 | |||
| 5776c72208 | |||
| 943c3aed58 | |||
| 8fcb6a8416 | |||
| 452dc8be88 | |||
| 63a255bbc9 | |||
| 151adc0e87 | |||
| 7f3ec63ab5 | |||
| f19c6aac3b | |||
| 29af448ad9 | |||
| 497d84828a | |||
| 0e489901a5 | |||
| 88063e756f | |||
| c8436e2b80 | |||
| 613748ec16 | |||
| 2fbd266da6 | |||
| c79b5e9cdb | |||
| 2ace892613 | |||
| c1ffd4e790 | |||
| d9c139b55f | |||
| 0c4206c97d | |||
| a985dc7a37 |
@@ -0,0 +1,68 @@
|
||||
name: Documentation (main)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow only one concurrent deployment
|
||||
concurrency:
|
||||
group: pages
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Documentation
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Build documentation
|
||||
env:
|
||||
RUSTDOCFLAGS: "--cfg docsrs"
|
||||
run: |
|
||||
cargo +nightly doc --no-deps \
|
||||
--features "lua55,vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
|
||||
- name: Create index redirect
|
||||
run: |
|
||||
echo '<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting to mlua documentation</title>
|
||||
<meta http-equiv="refresh" content="0; URL=mlua/index.html">
|
||||
<link rel="canonical" href="mlua/index.html">
|
||||
</head>
|
||||
<body>
|
||||
<p>Redirecting to <a href="mlua/index.html">mlua documentation</a>...</p>
|
||||
</body>
|
||||
</html>' > target/doc/index.html
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v5
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
with:
|
||||
path: target/doc
|
||||
|
||||
deploy:
|
||||
name: Deploy to GitHub Pages
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
@@ -1,3 +1,31 @@
|
||||
## v0.12.0-rc.2 (Jun 06, 2026)
|
||||
|
||||
- Add `#[derive(UserData)]` and `#[mlua::userdata_impl]` macros
|
||||
- Support thread create/resume/yield callbacks for all Lua versions (including Luau)
|
||||
- Support `to_alias_override`/`to_alias_fallback` in `Require` trait (Luau)
|
||||
- Prevent `XRc` overflow when dropping `RawLua` with foreign Lua state
|
||||
- implement `Not` for `StdLib` (#699)
|
||||
- Fix `String::to_pointer` return NULL in Lua <5.4
|
||||
|
||||
## v0.12.0-rc.1 (Apr 21, 2026)
|
||||
|
||||
- Rust 2024 edition
|
||||
- Removed `Error::ToLuaConversionError` variant as it was unused (and not practically useful)
|
||||
- New modules to group data types: `chunk`, `debug`, `error`, `function`, `table`, `string`, `state`, `thread`, `userdata`, `luau`
|
||||
- Support `__todebugstring` metamethod for pretty formatting userdata value (for debugging)
|
||||
- New `MaybeSync` trait that is required for userdata types
|
||||
- Removed lifetime from `BorrowedStr` and `BorrowedBytes`
|
||||
- New `Thread` methods: `is_resumable`, `is_running`, `is_finished`, `is_error`
|
||||
- Added `Thread::state` to get raw Lua state pointer
|
||||
- Luau `TextRequirer` is renamed to `FsRequirer`
|
||||
- GC interface refactor: `Lua::gc_inc/Lua::gc_gen` is replaced with `gc_set_mode`
|
||||
- Added `GcIncParams` and `GcGenParams` for GC tuning
|
||||
- New `UserDataMethods::add_method_once` and `UserDataMethods::add_async_method_once`
|
||||
- Initial Luau integer64 type support
|
||||
- Changed interface of `Function::wrap/wrap_mut/wrap_async` to support any Error type
|
||||
- Changed `AnyUserData::type_name` to return `LuaString` instead
|
||||
- Added `UserDataOwned<T>` wrapper to take ownership of userdata `T` and implements `FromLua`
|
||||
|
||||
## v0.11.6 (Jan 27, 2026)
|
||||
|
||||
- Added Lua 5.5 support (`lua55` feature flag)
|
||||
|
||||
+10
-10
@@ -1,9 +1,9 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.11.6" # remember to update mlua_derive
|
||||
version = "0.12.0-rc.2" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
|
||||
rust-version = "1.85.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.88"
|
||||
edition = "2024"
|
||||
repository = "https://github.com/mlua-rs/mlua"
|
||||
documentation = "https://docs.rs/mlua"
|
||||
readme = "README.md"
|
||||
@@ -42,7 +42,7 @@ async = ["dep:futures-util"]
|
||||
send = ["error-send"]
|
||||
error-send = []
|
||||
serde = ["dep:serde", "dep:erased-serde", "dep:serde-value", "bstr/serde"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
macros = ["mlua_derive/macros", "dep:inventory"]
|
||||
anyhow = ["dep:anyhow", "error-send"]
|
||||
userdata-wrappers = ["parking_lot/send_guard"]
|
||||
|
||||
@@ -50,7 +50,7 @@ userdata-wrappers = ["parking_lot/send_guard"]
|
||||
serialize = ["serde"]
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.11.0", optional = true, path = "mlua_derive" }
|
||||
mlua_derive = { version = "=0.12.0-rc.1", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "1.0", features = ["std"], default-features = false }
|
||||
either = "1.0"
|
||||
num-traits = { version = "0.2.14" }
|
||||
@@ -61,10 +61,10 @@ erased-serde = { version = "0.4", optional = true }
|
||||
serde-value = { version = "0.7", optional = true }
|
||||
parking_lot = { version = "0.12", features = ["arc_lock"] }
|
||||
anyhow = { version = "1.0", optional = true }
|
||||
rustversion = "1.0"
|
||||
inventory = { version = "0.3", optional = true }
|
||||
libc = "0.2"
|
||||
|
||||
ffi = { package = "mlua-sys", version = "0.10.0", path = "mlua-sys" }
|
||||
ffi = { package = "mlua-sys", version = "0.11.0-rc.1", path = "mlua-sys" }
|
||||
|
||||
[dev-dependencies]
|
||||
trybuild = "1.0"
|
||||
@@ -78,10 +78,10 @@ static_assertions = "1.0"
|
||||
hyper = { version = "1.2", features = ["full"] }
|
||||
hyper-util = { version = "0.1.3", features = ["full"] }
|
||||
http-body-util = "0.1.1"
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
reqwest = { version = "0.13", features = ["json"] }
|
||||
tempfile = "3"
|
||||
criterion = { version = "0.7", features = ["async_tokio"] }
|
||||
rustyline = "17.0"
|
||||
criterion = { version = "0.8", features = ["async_tokio"] }
|
||||
rustyline = "18.0"
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
|
||||
[lints.rust]
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
[Benchmarks]: https://github.com/khvzak/script-bench-rs
|
||||
[FAQ]: FAQ.md
|
||||
|
||||
## The main branch is the development version of `mlua`. Please see the [v0.11](https://github.com/mlua-rs/mlua/tree/v0.11) branch for the stable versions of `mlua`.
|
||||
|
||||
`mlua` is a set of bindings to the [Lua](https://www.lua.org) programming language for Rust with a goal of providing a
|
||||
_safe_ (as much as possible), high level, easy to use, practical and flexible API.
|
||||
|
||||
@@ -125,7 +127,7 @@ my_project $ LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static ca
|
||||
Just enable the `vendored` feature and cargo will automatically build and link the specified Lua/LuaJIT version. This is the easiest way to get started with `mlua`.
|
||||
|
||||
### Standalone mode
|
||||
In standalone mode, `mlua` allows adding scripting support to your application with a gently configured Lua runtime to ensure safety and soundness.
|
||||
In standalone mode, `mlua` allows adding scripting support to your application with a properly configured Lua runtime to ensure safety and soundness.
|
||||
|
||||
Add to `Cargo.toml`:
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::task;
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
|
||||
|
||||
use mlua::prelude::*;
|
||||
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
Implements the [`UserData`] trait for a Rust type.
|
||||
|
||||
This derive macro generates an implementation of [`UserData`] that exposes
|
||||
struct fields to Lua and integrates with `#[mlua::userdata_impl]` for
|
||||
registering methods.
|
||||
|
||||
Named fields are exposed as readable and writable fields in Lua by default.
|
||||
Use `#[lua(...)]` on individual fields or methods to control how they are
|
||||
registered.
|
||||
|
||||
```rust,ignore
|
||||
use mlua::{Lua, Result, UserData};
|
||||
|
||||
#[derive(UserData)]
|
||||
struct Rectangle {
|
||||
length: u32,
|
||||
width: u32,
|
||||
}
|
||||
|
||||
#[mlua::userdata_impl]
|
||||
impl Rectangle {
|
||||
#[lua(infallible)]
|
||||
fn new(length: u32, width: u32) -> Self {
|
||||
Self { length, width }
|
||||
}
|
||||
|
||||
#[lua(getter, name = "area", infallible)]
|
||||
fn calculate_area(&self) -> u32 {
|
||||
self.length * self.width
|
||||
}
|
||||
|
||||
fn diagonal(&self) -> Result<f64> {
|
||||
Ok(((self.length.pow(2) + self.width.pow(2)) as f64).sqrt())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Struct field attributes
|
||||
|
||||
Each named field can be annotated with `#[lua(...)]`:
|
||||
|
||||
| Attribute | Description |
|
||||
| -------------- | ----------------------------------------------------- |
|
||||
| `get` | Expose a getter. The field becomes readable from Lua. |
|
||||
| `set` | Expose a setter. The field becomes writable from Lua. |
|
||||
| `skip` | Do not expose this field. |
|
||||
| `name = "..."` | Override the Lua-facing name for the field. |
|
||||
|
||||
If neither `get` nor `set` is specified, both are enabled.
|
||||
|
||||
Fields exposed as readable (via `get` or by default) must implement `Clone`.
|
||||
The generated getter clones the field value when accessed from Lua.
|
||||
|
||||
# Methods registration
|
||||
|
||||
Use `#[mlua::userdata_impl]` on an `impl` block to register methods,
|
||||
metamethods, and constants. All public items in the block are registered
|
||||
automatically.
|
||||
|
||||
## Method detection
|
||||
|
||||
The receiver type determines how a method is registered:
|
||||
|
||||
| Receiver | Registration |
|
||||
| ----------- | ----------------- |
|
||||
| `&self` | `add_method` |
|
||||
| `&mut self` | `add_method_mut` |
|
||||
| `self` | `add_method_once` |
|
||||
| None | `add_function` |
|
||||
|
||||
A first parameter of type `&Lua` (or `&mlua::Lua`) is treated as the
|
||||
Lua state reference and passed automatically.
|
||||
|
||||
## Method and constant attributes
|
||||
|
||||
Each item in the impl block can be annotated with `#[lua(...)]`:
|
||||
|
||||
| Attribute | Applies to | Description |
|
||||
| -------------- | ------------------ | -------------------------------------------------------------------------------------- |
|
||||
| `skip` | Methods, constants | Exclude this item from registration. |
|
||||
| `name = "..."` | Methods, constants | Override the Lua-facing name. |
|
||||
| `infallible` | Methods | Wrap the return value in `Ok(...)`. |
|
||||
| `getter` | Methods | Register as a field getter. Must take `&self` and no Lua-facing arguments. |
|
||||
| `setter` | Methods | Register as a field setter. Must take `&[mut] self` and one value argument. |
|
||||
| `field` | Methods, constants | Register as a static field. Methods must take no receiver and no Lua-facing arguments. |
|
||||
| `meta` | Methods, constants | Register as a metamethod. May be combined with `field` for meta static fields. |
|
||||
|
||||
At most one of `getter`, `setter`, `field` may be specified on a method.
|
||||
|
||||
## Constants
|
||||
|
||||
Constants in an `#[mlua::userdata_impl]` block are registered as static
|
||||
fields:
|
||||
|
||||
```rust,ignore
|
||||
#[mlua::userdata_impl]
|
||||
impl MyType {
|
||||
const VERSION: &str = "1.0";
|
||||
const COUNT: u32 = 42;
|
||||
}
|
||||
```
|
||||
|
||||
Use `#[lua(meta)]` on a constant to register it as a meta static field.
|
||||
|
||||
## Metamethods
|
||||
|
||||
Annotate a method with `#[lua(meta)]` to register it as a Lua metamethod.
|
||||
The metamethod name is inferred from the function name when it starts with
|
||||
`__`. Use `name = "..."` to specify the name explicitly.
|
||||
|
||||
```rust,ignore
|
||||
#[mlua::userdata_impl]
|
||||
impl MyType {
|
||||
#[lua(meta, infallible)]
|
||||
fn __add(&self, other: &Self) -> Self { ... }
|
||||
|
||||
#[lua(meta, name = "__call", infallible)]
|
||||
fn construct(lua: &Lua, value: u32) -> Self { ... }
|
||||
}
|
||||
```
|
||||
|
||||
## Reference parameters
|
||||
|
||||
Reference parameters in method signatures are automatically mapped to
|
||||
the appropriate callback wrapper types:
|
||||
|
||||
| Parameter type | Callback type |
|
||||
| -------------- | ------------------- |
|
||||
| `&str` | `BorrowedStr` |
|
||||
| `&[u8]` | `BorrowedBytes` |
|
||||
| `&T` | `UserDataRef<T>` |
|
||||
| `&mut T` | `UserDataRefMut<T>` |
|
||||
|
||||
## Async methods
|
||||
|
||||
Async methods are supported and registered via the corresponding async
|
||||
variants (`add_async_method`, `add_async_method_mut`, etc.).
|
||||
|
||||
# Limitations
|
||||
|
||||
Generics are not supported. Wrap a generic type in a concrete newtype
|
||||
instead.
|
||||
|
||||
Union types cannot derive `UserData`.
|
||||
|
||||
Enum types are accepted but generate no field registrations. All method
|
||||
registration must be done via `#[mlua::userdata_impl]`.
|
||||
|
||||
[`UserData`]: crate::UserData
|
||||
@@ -0,0 +1,52 @@
|
||||
Create a type that implements [`AsChunk`] and can capture Rust variables.
|
||||
|
||||
This macro allows to write Lua code directly in Rust code.
|
||||
|
||||
Rust variables can be referenced from Lua using `$` prefix, as shown in the example below.
|
||||
User's Rust types needs to implement [`UserData`] or [`IntoLua`] traits.
|
||||
|
||||
Captured variables are **moved** into the chunk.
|
||||
|
||||
```rust
|
||||
use mlua::{Lua, Result, chunk};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let name = "Rustacean";
|
||||
lua.load(chunk! {
|
||||
print("hello, " .. $name)
|
||||
}).exec()
|
||||
}
|
||||
```
|
||||
|
||||
## Syntax issues
|
||||
|
||||
Since the Rust tokenizer will tokenize Lua code, this imposes some restrictions.
|
||||
The main thing to remember is:
|
||||
|
||||
- Use double quoted strings (`""`) instead of single quoted strings (`''`).
|
||||
|
||||
(Single quoted strings only work if they contain a single character, since in Rust,
|
||||
`'a'` is a character literal).
|
||||
|
||||
- Using Lua comments `--` is not desirable in **stable** Rust and can have bad side effects.
|
||||
|
||||
This is because procedural macros have Line/Column information available only in
|
||||
**nightly** Rust. Instead, Lua chunks represented as a big single line of code in stable Rust.
|
||||
|
||||
As workaround, Rust comments `//` can be used.
|
||||
|
||||
Other minor limitations:
|
||||
|
||||
- Certain escape codes in string literals don't work. (Specifically: `\a`, `\b`, `\f`, `\v`,
|
||||
`\123` (octal escape codes), `\u`, and `\U`).
|
||||
|
||||
These are accepted: : `\\`, `\n`, `\t`, `\r`, `\xAB` (hex escape codes), and `\0`.
|
||||
|
||||
- The `//` (floor division) operator is unusable, as its start a comment.
|
||||
|
||||
Everything else should work.
|
||||
|
||||
[`AsChunk`]: crate::chunk::AsChunk
|
||||
[`UserData`]: crate::UserData
|
||||
[`IntoLua`]: crate::IntoLua
|
||||
@@ -0,0 +1,41 @@
|
||||
Registers Lua module entrypoint.
|
||||
|
||||
You can register multiple entrypoints as required.
|
||||
|
||||
```rust,ignore
|
||||
use mlua::{Lua, Result, Table};
|
||||
|
||||
#[mlua::lua_module]
|
||||
fn my_module(lua: &Lua) -> Result<Table> {
|
||||
let exports = lua.create_table()?;
|
||||
exports.set("hello", "world")?;
|
||||
Ok(exports)
|
||||
}
|
||||
```
|
||||
|
||||
Internally in the code above the compiler defines C function `luaopen_my_module`.
|
||||
|
||||
You can also pass options to the attribute:
|
||||
|
||||
* name - name of the module, defaults to the name of the function
|
||||
|
||||
```rust,ignore
|
||||
#[mlua::lua_module(name = "alt_module")]
|
||||
fn my_module(lua: &Lua) -> Result<Table> {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
* skip_memory_check - skip memory allocation checks for some operations.
|
||||
|
||||
In module mode, mlua runs in an unknown environment and cannot tell whether there are any memory
|
||||
limits or not. As a result, some operations that require memory allocation run in protected
|
||||
mode. Setting this attribute will improve performance of such operations with risk of having
|
||||
uncaught exceptions and memory leaks.
|
||||
|
||||
```rust,ignore
|
||||
#[mlua::lua_module(skip_memory_check)]
|
||||
fn my_module(lua: &Lua) -> Result<Table> {
|
||||
...
|
||||
}
|
||||
```
|
||||
@@ -1,6 +1,6 @@
|
||||
## mlua v0.10 release notes
|
||||
|
||||
The v0.10 version of mlua has goal to improve the user experience while keeping the same performance and safety guarantees.
|
||||
The v0.10 version of mlua has a goal to improve the user experience while keeping the same performance and safety guarantees.
|
||||
This document highlights the most notable features. For a full list of changes, see the [CHANGELOG].
|
||||
|
||||
[CHANGELOG]: https://github.com/mlua-rs/mlua/blob/main/CHANGELOG.md
|
||||
@@ -40,7 +40,7 @@ assert_eq!(lua.globals().get::<i32>("i")?, 20);
|
||||
|
||||
Under the hood, to synchronize access to the Lua state, mlua uses [`ReentrantMutex`] which can be recursively locked by a single thread. Only one thread can execute Lua code at a time, but it's possible to share Lua values between threads.
|
||||
|
||||
This has some performance penalties (about 10-20%) compared to the lock free mode. This flag is disabled by default and does not supported in module mode.
|
||||
This has some performance penalties (about 10-20%) compared to the lock free mode. This flag is disabled by default and is not supported in module mode.
|
||||
|
||||
[`ReentrantMutex`]: https://docs.rs/parking_lot/latest/parking_lot/type.ReentrantMutex.html
|
||||
|
||||
@@ -144,7 +144,7 @@ The following `Scope` methods were changed:
|
||||
Instead, scope has comprehensive support for borrowed userdata: `create_any_userdata_ref`, `create_any_userdata_ref_mut`, `create_userdata_ref`, `create_userdata_ref_mut`.
|
||||
|
||||
`UserDataRef` and `UserDataRefMut` are no longer acceptable for scoped userdata access as they require owned underlying data.
|
||||
In mlua v0.9 this can cause read-after-free bug in some edge cases.
|
||||
In mlua v0.9 this could cause a read-after-free bug in some edge cases.
|
||||
|
||||
To temporarily borrow underlying data, the `AnyUserData::borrow_scoped` and `AnyUserData::borrow_mut_scoped` methods were introduced:
|
||||
|
||||
|
||||
@@ -152,9 +152,9 @@ It will automatically trigger JIT compilation for new Lua chunks. To disable it,
|
||||
|
||||
#### 1. Better error reporting
|
||||
|
||||
When calling a Rust function from Lua and passing wrong arguments, previous mlua versions reported a error message without any context or reference to the particular argument.
|
||||
When calling a Rust function from Lua and passing wrong arguments, previous mlua versions reported an error message without any context or reference to the particular argument.
|
||||
|
||||
In v0.9 it reports a error message with the argument index and expected type:
|
||||
In v0.9 it reports an error message with the argument index and expected type:
|
||||
|
||||
```rust
|
||||
let func = lua.create_function(|_, _a: i32| Ok(()))?;
|
||||
@@ -327,7 +327,7 @@ Under the hood a new function `luaopen_alt_module` will be created for the Lua m
|
||||
|
||||
- `skip_memory_check` - skip memory allocation checks for some operations.
|
||||
|
||||
In module mode, mlua runs in unknown environment and cannot say are there any memory limits or not. As result, some operations that require memory allocation runs in
|
||||
In module mode, mlua runs in an unknown environment and cannot tell whether there are any memory limits or not. As a result, some operations that require memory allocation run in
|
||||
protected mode. Setting this attribute will improve performance of such operations with risk of having uncaught exceptions and memory leaks.
|
||||
|
||||
#### Improved Windows target
|
||||
|
||||
@@ -5,7 +5,7 @@ use hyper::body::Incoming;
|
||||
use hyper_util::client::legacy::Client as HyperClient;
|
||||
use hyper_util::rt::TokioExecutor;
|
||||
|
||||
use mlua::{chunk, ExternalResult, Lua, Result, UserData, UserDataMethods};
|
||||
use mlua::{ExternalResult, Lua, Result, UserData, UserDataMethods, chunk};
|
||||
|
||||
struct BodyReader(Incoming);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use mlua::{chunk, ExternalResult, Lua, LuaSerdeExt, Result, Value};
|
||||
use mlua::{ExternalResult, Lua, LuaSerdeExt, Result, Value, chunk};
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<()> {
|
||||
|
||||
@@ -11,7 +11,7 @@ use hyper::{Request, Response};
|
||||
use hyper_util::rt::TokioIo;
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
use mlua::{chunk, Error as LuaError, Function, Lua, String as LuaString, Table, UserData, UserDataMethods};
|
||||
use mlua::{Error as LuaError, Function, Lua, String as LuaString, Table, UserData, UserDataMethods, chunk};
|
||||
|
||||
/// Wrapper around incoming request that implements UserData
|
||||
struct LuaRequest(SocketAddr, Request<Incoming>);
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::net::SocketAddr;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
|
||||
use mlua::{chunk, BString, Function, Lua, UserData, UserDataMethods};
|
||||
use mlua::{BString, Function, Lua, UserData, UserDataMethods, chunk};
|
||||
|
||||
struct LuaTcpStream(TcpStream);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::f32;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use mlua::{chunk, FromLua, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Value, Variadic};
|
||||
use mlua::{FromLua, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Value, Variadic, chunk};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// You can create a new Lua state with `Lua::new()`. This loads the default Lua std library
|
||||
|
||||
+28
-27
@@ -1,45 +1,46 @@
|
||||
use mlua::{chunk, Lua, MetaMethod, Result, UserData};
|
||||
use mlua::{Lua, Result, UserData, chunk};
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, UserData)]
|
||||
struct Rectangle {
|
||||
length: u32,
|
||||
width: u32,
|
||||
}
|
||||
|
||||
impl UserData for Rectangle {
|
||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("length", |_, this| Ok(this.length));
|
||||
fields.add_field_method_set("length", |_, this, val| {
|
||||
this.length = val;
|
||||
Ok(())
|
||||
});
|
||||
fields.add_field_method_get("width", |_, this| Ok(this.width));
|
||||
fields.add_field_method_set("width", |_, this, val| {
|
||||
this.width = val;
|
||||
Ok(())
|
||||
});
|
||||
#[mlua::userdata_impl]
|
||||
impl Rectangle {
|
||||
const NAME: &str = "Rectangle";
|
||||
|
||||
#[lua(infallible)]
|
||||
fn new(length: u32, width: u32) -> Self {
|
||||
Self { length, width }
|
||||
}
|
||||
|
||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("area", |_, this, ()| Ok(this.length * this.width));
|
||||
methods.add_method("diagonal", |_, this, ()| {
|
||||
Ok((this.length.pow(2) as f64 + this.width.pow(2) as f64).sqrt())
|
||||
});
|
||||
#[lua(getter, name = "area", infallible)]
|
||||
fn calculate_area(&self) -> u32 {
|
||||
self.length * self.width
|
||||
}
|
||||
|
||||
// Constructor
|
||||
methods.add_meta_function(MetaMethod::Call, |_, ()| Ok(Rectangle::default()));
|
||||
fn diagonal(&self) -> Result<f64> {
|
||||
Ok((self.length.pow(2) as f64 + self.width.pow(2) as f64).sqrt())
|
||||
}
|
||||
|
||||
// Constructor via `__call` metamethod
|
||||
#[lua(meta, infallible)]
|
||||
fn __call(length: u32, width: u32) -> Self {
|
||||
Rectangle::new(length, width)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let rectangle = Rectangle::default();
|
||||
lua.globals().set("Rectangle", lua.create_proxy::<Rectangle>()?)?;
|
||||
lua.load(chunk! {
|
||||
local rect = $rectangle()
|
||||
rect.width = 10
|
||||
rect.length = 5
|
||||
assert(rect:area() == 50)
|
||||
assert(rect:diagonal() - 11.1803 < 0.0001)
|
||||
local rect = Rectangle(10, 5)
|
||||
rect.width = rect.width + 5
|
||||
rect.length = rect.length + 5
|
||||
assert(rect.NAME == "Rectangle")
|
||||
assert(rect.area == 150)
|
||||
assert(math.floor(rect:diagonal()) == 18)
|
||||
})
|
||||
.exec()
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0-rc.1"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
rust-version = "1.85"
|
||||
rust-version = "1.88"
|
||||
edition = "2024"
|
||||
repository = "https://github.com/mlua-rs/mlua"
|
||||
documentation = "https://docs.rs/mlua-sys"
|
||||
@@ -41,9 +41,9 @@ libc = "0.2"
|
||||
cc = "1.0"
|
||||
cfg-if = "1.0"
|
||||
pkg-config = "0.3.17"
|
||||
lua-src = { version = ">= 550.0.0, < 550.1.0", optional = true }
|
||||
luajit-src = { version = ">= 210.6.0, < 210.7.0", optional = true }
|
||||
luau0-src = { version = "0.18.0", optional = true }
|
||||
lua-src = { version = ">= 550.1.0, < 550.2.0", optional = true }
|
||||
luajit-src = { version = ">= 210.7.0, < 210.8.0", optional = true }
|
||||
luau0-src = { version = "0.20.0", optional = true }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(raw_dylib)'] }
|
||||
|
||||
@@ -38,8 +38,10 @@ unsafe extern "C-unwind" {
|
||||
|
||||
#[link_name = "luaL_checkinteger"]
|
||||
pub fn luaL_checkinteger_(L: *mut lua_State, narg: c_int) -> c_int;
|
||||
pub fn luaL_checkinteger64(L: *mut lua_State, narg: c_int) -> i64;
|
||||
#[link_name = "luaL_optinteger"]
|
||||
pub fn luaL_optinteger_(L: *mut lua_State, narg: c_int, def: c_int) -> c_int;
|
||||
pub fn luaL_optinteger64(L: *mut lua_State, narg: c_int, def: i64) -> i64;
|
||||
pub fn luaL_checkunsigned(L: *mut lua_State, narg: c_int) -> lua_Unsigned;
|
||||
pub fn luaL_optunsigned(L: *mut lua_State, narg: c_int, def: lua_Unsigned) -> lua_Unsigned;
|
||||
|
||||
|
||||
@@ -65,14 +65,15 @@ pub const LUA_TBOOLEAN: c_int = 1;
|
||||
|
||||
pub const LUA_TLIGHTUSERDATA: c_int = 2;
|
||||
pub const LUA_TNUMBER: c_int = 3;
|
||||
pub const LUA_TVECTOR: c_int = 4;
|
||||
pub const LUA_TINTEGER: c_int = 4;
|
||||
pub const LUA_TVECTOR: c_int = 5;
|
||||
|
||||
pub const LUA_TSTRING: c_int = 5;
|
||||
pub const LUA_TTABLE: c_int = 6;
|
||||
pub const LUA_TFUNCTION: c_int = 7;
|
||||
pub const LUA_TUSERDATA: c_int = 8;
|
||||
pub const LUA_TTHREAD: c_int = 9;
|
||||
pub const LUA_TBUFFER: c_int = 10;
|
||||
pub const LUA_TSTRING: c_int = 6;
|
||||
pub const LUA_TTABLE: c_int = 7;
|
||||
pub const LUA_TFUNCTION: c_int = 8;
|
||||
pub const LUA_TUSERDATA: c_int = 9;
|
||||
pub const LUA_TTHREAD: c_int = 10;
|
||||
pub const LUA_TBUFFER: c_int = 11;
|
||||
|
||||
/// Guaranteed number of Lua stack slots available to a C function.
|
||||
pub const LUA_MINSTACK: c_int = 20;
|
||||
@@ -153,6 +154,7 @@ unsafe extern "C-unwind" {
|
||||
pub fn lua_tounsignedx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Unsigned;
|
||||
pub fn lua_tovector(L: *mut lua_State, idx: c_int) -> *const c_float;
|
||||
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_tointeger64(L: *mut lua_State, idx: c_int, isinteger: *mut c_int) -> i64;
|
||||
pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||
pub fn lua_tostringatom(L: *mut lua_State, idx: c_int, atom: *mut c_int) -> *const c_char;
|
||||
pub fn lua_tolstringatom(
|
||||
@@ -182,6 +184,7 @@ unsafe extern "C-unwind" {
|
||||
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
|
||||
#[link_name = "lua_pushinteger"]
|
||||
pub fn lua_pushinteger_(L: *mut lua_State, n: c_int);
|
||||
pub fn lua_pushinteger64(L: *mut lua_State, n: i64);
|
||||
pub fn lua_pushunsigned(L: *mut lua_State, n: lua_Unsigned);
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
pub fn lua_pushvector(L: *mut lua_State, x: c_float, y: c_float, z: c_float);
|
||||
@@ -412,6 +415,11 @@ pub unsafe fn lua_isboolean(L: *mut lua_State, n: c_int) -> c_int {
|
||||
(lua_type(L, n) == LUA_TBOOLEAN) as c_int
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_isinteger64(L: *mut lua_State, n: c_int) -> c_int {
|
||||
(lua_type(L, n) == LUA_TINTEGER) as c_int
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_isvector(L: *mut lua_State, n: c_int) -> c_int {
|
||||
(lua_type(L, n) == LUA_TVECTOR) as c_int
|
||||
@@ -501,6 +509,12 @@ pub type lua_Coverage = unsafe extern "C-unwind" fn(
|
||||
size: usize,
|
||||
);
|
||||
|
||||
pub type lua_CounterFunction =
|
||||
unsafe extern "C-unwind" fn(context: *mut c_void, function: *const c_char, linedefined: c_int);
|
||||
|
||||
pub type lua_CounterValue =
|
||||
unsafe extern "C-unwind" fn(context: *mut c_void, kind: c_int, line: c_int, hits: u64);
|
||||
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn lua_stackdepth(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_getinfo(L: *mut lua_State, level: c_int, what: *const c_char, ar: *mut lua_Debug) -> c_int;
|
||||
@@ -515,6 +529,14 @@ unsafe extern "C-unwind" {
|
||||
|
||||
pub fn lua_getcoverage(L: *mut lua_State, funcindex: c_int, context: *mut c_void, callback: lua_Coverage);
|
||||
|
||||
pub fn lua_getcounters(
|
||||
L: *mut lua_State,
|
||||
funcindex: c_int,
|
||||
context: *mut c_void,
|
||||
functionvisit: lua_CounterFunction,
|
||||
countervisit: lua_CounterValue,
|
||||
);
|
||||
|
||||
pub fn lua_debugtrace(L: *mut lua_State) -> *const c_char;
|
||||
}
|
||||
|
||||
@@ -551,8 +573,8 @@ pub struct lua_Callbacks {
|
||||
|
||||
/// gets called when L is created (LP == parent) or destroyed (LP == NULL)
|
||||
pub userthread: Option<unsafe extern "C-unwind" fn(LP: *mut lua_State, L: *mut lua_State)>,
|
||||
/// gets called when a string is created; returned atom can be retrieved via tostringatom
|
||||
pub useratom: Option<unsafe extern "C-unwind" fn(s: *const c_char, l: usize) -> i16>,
|
||||
/// gets called when a string is created to assign an atom id
|
||||
pub useratom: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, s: *const c_char, l: usize) -> i16>,
|
||||
|
||||
/// gets called when BREAK instruction is encountered
|
||||
pub debugbreak: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
|
||||
|
||||
@@ -80,6 +80,7 @@ unsafe extern "C" {
|
||||
pub fn luau_set_compile_constant_nil(cons: *mut lua_CompileConstant);
|
||||
pub fn luau_set_compile_constant_boolean(cons: *mut lua_CompileConstant, b: c_int);
|
||||
pub fn luau_set_compile_constant_number(cons: *mut lua_CompileConstant, n: f64);
|
||||
pub fn luau_set_compile_constant_integer64(cons: *mut lua_CompileConstant, l: i64);
|
||||
pub fn luau_set_compile_constant_vector(cons: *mut lua_CompileConstant, x: f32, y: f32, z: f32, w: f32);
|
||||
pub fn luau_set_compile_constant_string(cons: *mut lua_CompileConstant, s: *const c_char, l: usize);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ pub const LUA_UTF8LIBNAME: *const c_char = cstr!("utf8");
|
||||
pub const LUA_MATHLIBNAME: *const c_char = cstr!("math");
|
||||
pub const LUA_DBLIBNAME: *const c_char = cstr!("debug");
|
||||
pub const LUA_VECLIBNAME: *const c_char = cstr!("vector");
|
||||
pub const LUA_INTLIBNAME: *const c_char = cstr!("integer");
|
||||
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaopen_base(L: *mut lua_State) -> c_int;
|
||||
@@ -27,6 +28,7 @@ unsafe extern "C-unwind" {
|
||||
pub fn luaopen_math(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_vector(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_integer(L: *mut lua_State) -> c_int;
|
||||
|
||||
// open all builtin libraries
|
||||
pub fn luaL_openlibs(L: *mut lua_State);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.11.0"
|
||||
version = "0.12.0-rc.1"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.88"
|
||||
edition = "2024"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
repository = "https://github.com/mlua-rs/mlua"
|
||||
keywords = ["lua", "mlua"]
|
||||
@@ -12,7 +13,7 @@ license = "MIT"
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
macros = ["proc-macro-error2", "itertools", "regex", "once_cell"]
|
||||
macros = ["proc-macro-error2", "itertools"]
|
||||
|
||||
[dependencies]
|
||||
quote = "1.0"
|
||||
@@ -20,5 +21,3 @@ proc-macro2 = { version = "1.0", features = ["span-locations"] }
|
||||
proc-macro-error2 = { version = "2.0.1", optional = true }
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
itertools = { version = "0.14", optional = true }
|
||||
regex = { version = "1.4", optional = true }
|
||||
once_cell = { version = "1.0", optional = true }
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
use proc_macro::{TokenStream, TokenTree};
|
||||
|
||||
use crate::token::{Pos, Token, Tokens};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Capture {
|
||||
key: Token,
|
||||
rust: TokenTree,
|
||||
}
|
||||
|
||||
impl Capture {
|
||||
fn new(key: Token, rust: TokenTree) -> Self {
|
||||
Self { key, rust }
|
||||
}
|
||||
|
||||
/// Token string inside `chunk!`
|
||||
pub(crate) fn key(&self) -> &Token {
|
||||
&self.key
|
||||
}
|
||||
|
||||
/// As rust variable, e.g. `x`
|
||||
pub(crate) fn as_rust(&self) -> &TokenTree {
|
||||
&self.rust
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Captures(Vec<Capture>);
|
||||
|
||||
impl Captures {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
}
|
||||
|
||||
pub(crate) fn add(&mut self, token: &Token) -> Capture {
|
||||
let tt = token.tree();
|
||||
let key = token.clone();
|
||||
|
||||
match self.0.iter().find(|arg| arg.key() == &key) {
|
||||
Some(arg) => arg.clone(),
|
||||
None => {
|
||||
let arg = Capture::new(key, tt.clone());
|
||||
self.0.push(arg.clone());
|
||||
arg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn captures(&self) -> &[Capture] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Chunk {
|
||||
source: String,
|
||||
caps: Captures,
|
||||
}
|
||||
|
||||
impl Chunk {
|
||||
pub(crate) fn new(tokens: TokenStream) -> Self {
|
||||
let tokens = Tokens::retokenize(tokens);
|
||||
|
||||
let mut source = String::new();
|
||||
let mut caps = Captures::new();
|
||||
|
||||
let mut pos: Option<Pos> = None;
|
||||
for t in tokens {
|
||||
if t.is_cap() {
|
||||
caps.add(&t);
|
||||
}
|
||||
|
||||
let (line, col) = (t.start().line, t.start().column);
|
||||
let (prev_line, prev_col) = pos
|
||||
.take()
|
||||
.map(|lc| (lc.line, lc.column))
|
||||
.unwrap_or_else(|| (line, col));
|
||||
|
||||
#[allow(clippy::comparison_chain)]
|
||||
if line > prev_line {
|
||||
source.push('\n');
|
||||
} else if line == prev_line {
|
||||
for _ in 0..col.saturating_sub(prev_col) {
|
||||
source.push(' ');
|
||||
}
|
||||
}
|
||||
source.push_str(&t.to_string());
|
||||
|
||||
pos = Some(t.end());
|
||||
}
|
||||
|
||||
Self {
|
||||
source: source.trim_end().to_string(),
|
||||
caps,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn source(&self) -> &str {
|
||||
&self.source
|
||||
}
|
||||
|
||||
pub(crate) fn captures(&self) -> &[Capture] {
|
||||
self.caps.captures()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
use std::ops::Deref;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{ToTokens, quote};
|
||||
|
||||
use self::token::{Pos, Token, Tokens};
|
||||
|
||||
mod token;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Capture(Token);
|
||||
|
||||
impl Deref for Capture {
|
||||
type Target = Token;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Capture {
|
||||
fn new(token: &Token) -> Self {
|
||||
Self(token.clone())
|
||||
}
|
||||
|
||||
pub(crate) fn name(&self) -> String {
|
||||
self.0.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Capture {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let ts: TokenStream = self.0.tree().clone().into();
|
||||
tokens.extend(TokenStream2::from(ts));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Captures(Vec<Capture>);
|
||||
|
||||
impl Captures {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
}
|
||||
|
||||
pub(crate) fn add(&mut self, token: &Token) {
|
||||
if self.0.iter().any(|arg| &**arg == token) {
|
||||
return;
|
||||
}
|
||||
self.0.push(Capture::new(token));
|
||||
}
|
||||
|
||||
pub(crate) fn captures(&self) -> &[Capture] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Chunk {
|
||||
source: String,
|
||||
caps: Captures,
|
||||
}
|
||||
|
||||
impl Chunk {
|
||||
pub(crate) fn new(tokens: TokenStream) -> Self {
|
||||
let tokens = Tokens::retokenize(tokens);
|
||||
|
||||
let mut source = String::new();
|
||||
let mut caps = Captures::new();
|
||||
|
||||
let mut prev_end: Option<Pos> = None;
|
||||
for t in tokens {
|
||||
if t.is_cap() {
|
||||
caps.add(&t);
|
||||
}
|
||||
|
||||
let (line, col) = (t.start().line, t.start().column);
|
||||
if let Some(prev) = prev_end {
|
||||
if line > prev.line {
|
||||
source.push('\n');
|
||||
source.push_str(&" ".repeat(col.saturating_sub(1)));
|
||||
} else if line == prev.line {
|
||||
source.push_str(&" ".repeat(col.saturating_sub(prev.column)));
|
||||
}
|
||||
} else {
|
||||
source.push_str(&" ".repeat(col.saturating_sub(1)));
|
||||
}
|
||||
source.push_str(&t.to_string());
|
||||
|
||||
prev_end = Some(t.end());
|
||||
}
|
||||
|
||||
Self {
|
||||
source: source.trim_end().to_string(),
|
||||
caps,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn captures(&self) -> &[Capture] {
|
||||
self.caps.captures()
|
||||
}
|
||||
|
||||
pub(crate) fn expand(&self) -> TokenStream2 {
|
||||
let source = &self.source;
|
||||
|
||||
let caps_len = self.captures().len();
|
||||
let caps = self.captures().iter().map(|cap| {
|
||||
let cap_name = cap.name();
|
||||
quote! { env.raw_set(#cap_name, #cap)?; }
|
||||
});
|
||||
|
||||
quote! {{
|
||||
use mlua::{AsChunk, ChunkMode, Lua, Result, Table};
|
||||
use ::std::borrow::Cow;
|
||||
use ::std::cell::Cell;
|
||||
use ::std::io::Result as IoResult;
|
||||
|
||||
struct InnerChunk<F: FnOnce(&Lua) -> Result<Table>>(Cell<Option<F>>);
|
||||
|
||||
impl<F> AsChunk for InnerChunk<F>
|
||||
where
|
||||
F: FnOnce(&Lua) -> Result<Table>,
|
||||
{
|
||||
fn environment(&self, lua: &Lua) -> Result<Option<Table>> {
|
||||
if #caps_len > 0 {
|
||||
if let Some(make_env) = self.0.take() {
|
||||
return make_env(lua).map(Some);
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn mode(&self) -> Option<ChunkMode> {
|
||||
Some(ChunkMode::Text)
|
||||
}
|
||||
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Borrowed((#source).as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
let make_env = move |lua: &Lua| -> Result<Table> {
|
||||
let globals = lua.globals();
|
||||
let env = lua.create_table()?;
|
||||
let meta = lua.create_table()?;
|
||||
meta.raw_set("__index", &globals)?;
|
||||
meta.raw_set("__newindex", &globals)?;
|
||||
|
||||
// Add captured variables
|
||||
#(#caps)*
|
||||
|
||||
env.set_metatable(Some(meta))?;
|
||||
Ok(env)
|
||||
};
|
||||
|
||||
InnerChunk(Cell::new(Some(make_env)))
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,8 @@ use std::fmt::{self, Display, Formatter};
|
||||
use std::vec::IntoIter;
|
||||
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
use proc_macro::{Delimiter, Span, TokenStream, TokenTree};
|
||||
use proc_macro2::Span as Span2;
|
||||
use regex::Regex;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) struct Pos {
|
||||
@@ -39,46 +37,16 @@ fn span_pos(span: &Span) -> (Pos, Pos) {
|
||||
let start = span2.start();
|
||||
let end = span2.end();
|
||||
|
||||
// In stable, line/column information is not provided
|
||||
// and set to 0 (line is 1-indexed)
|
||||
// Rust 1.88 stabilized Span APIs, so this branch must be unreachable
|
||||
if start.line == 0 || end.line == 0 {
|
||||
return fallback_span_pos(span);
|
||||
proc_macro_error2::abort_call_site!(
|
||||
"cannot retrieve span location information; mlua requires nightly Rust or stable >= 1.88"
|
||||
);
|
||||
}
|
||||
|
||||
(Pos::new(start.line, start.column), Pos::new(end.line, end.column))
|
||||
}
|
||||
|
||||
fn parse_pos(span: &Span) -> Option<(usize, usize)> {
|
||||
// Workaround to somehow retrieve location information in span in stable rust :(
|
||||
|
||||
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"bytes\(([0-9]+)\.\.([0-9]+)\)").unwrap());
|
||||
|
||||
match RE.captures(&format!("{span:?}")) {
|
||||
Some(caps) => match (caps.get(1), caps.get(2)) {
|
||||
(Some(start), Some(end)) => Some((
|
||||
match start.as_str().parse() {
|
||||
Ok(v) => v,
|
||||
_ => return None,
|
||||
},
|
||||
match end.as_str().parse() {
|
||||
Ok(v) => v,
|
||||
_ => return None,
|
||||
},
|
||||
)),
|
||||
_ => None,
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn fallback_span_pos(span: &Span) -> (Pos, Pos) {
|
||||
let (start, end) = match parse_pos(span) {
|
||||
Some(v) => v,
|
||||
None => proc_macro_error2::abort_call_site!("Cannot retrieve span information; please use nightly"),
|
||||
};
|
||||
(Pos::new(1, start), Pos::new(1, end))
|
||||
}
|
||||
|
||||
/// Attribute of token.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum TokenAttr {
|
||||
@@ -108,8 +76,9 @@ impl Eq for Token {}
|
||||
impl Token {
|
||||
fn new(tree: TokenTree) -> Self {
|
||||
let (start, end) = span_pos(&tree.span());
|
||||
let source = tree.span().source_text().unwrap_or_else(|| tree.to_string());
|
||||
Self {
|
||||
source: tree.to_string(),
|
||||
source,
|
||||
start,
|
||||
end,
|
||||
tree,
|
||||
@@ -168,14 +137,17 @@ impl Tokens {
|
||||
Tokens(
|
||||
tt.into_iter()
|
||||
.flat_map(Tokens::from)
|
||||
.peekable()
|
||||
.batching(|iter| {
|
||||
// Find variable tokens
|
||||
// Find variable tokens: `$` + `ident` => `$ident`
|
||||
let t = iter.next()?;
|
||||
if t.is("$") {
|
||||
// `$` + `ident` => `$ident`
|
||||
let t = iter.next().expect("$ must trail an identifier");
|
||||
Some(t.attr(TokenAttr::Cap))
|
||||
if let Some(next) = iter.next()
|
||||
&& matches!(next.tree, TokenTree::Ident(_))
|
||||
{
|
||||
Some(next.attr(TokenAttr::Cap))
|
||||
} else {
|
||||
proc_macro_error2::abort!(t.tree.span(), "`$` must be followed by an identifier");
|
||||
}
|
||||
} else {
|
||||
Some(t)
|
||||
}
|
||||
+13
-13
@@ -1,6 +1,6 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
use syn::{DeriveInput, parse_macro_input};
|
||||
|
||||
pub fn from_lua(input: TokenStream) -> TokenStream {
|
||||
let DeriveInput { ident, generics, .. } = parse_macro_input!(input as DeriveInput);
|
||||
@@ -13,19 +13,19 @@ pub fn from_lua(input: TokenStream) -> TokenStream {
|
||||
};
|
||||
|
||||
quote! {
|
||||
impl #impl_generics ::mlua::FromLua for #ident #ty_generics #where_clause {
|
||||
#[inline]
|
||||
fn from_lua(value: ::mlua::Value, _: &::mlua::Lua) -> ::mlua::Result<Self> {
|
||||
match value {
|
||||
::mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
|
||||
_ => Err(::mlua::Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: #ident_str.to_string(),
|
||||
message: None,
|
||||
}),
|
||||
}
|
||||
impl #impl_generics ::mlua::FromLua for #ident #ty_generics #where_clause {
|
||||
#[inline]
|
||||
fn from_lua(value: ::mlua::Value, _: &::mlua::Lua) -> ::mlua::Result<Self> {
|
||||
match value {
|
||||
::mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
|
||||
_ => Err(::mlua::Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: #ident_str.to_string(),
|
||||
message: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
+27
-131
@@ -1,148 +1,30 @@
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use quote::quote;
|
||||
use syn::meta::ParseNestedMeta;
|
||||
use syn::{parse_macro_input, ItemFn, LitStr, Result};
|
||||
|
||||
mod module;
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
use {
|
||||
crate::chunk::Chunk, proc_macro::TokenTree, proc_macro2::TokenStream as TokenStream2,
|
||||
proc_macro_error2::proc_macro_error,
|
||||
};
|
||||
use {crate::chunk::Chunk, proc_macro_error2::proc_macro_error};
|
||||
|
||||
#[derive(Default)]
|
||||
struct ModuleAttributes {
|
||||
name: Option<Ident>,
|
||||
skip_memory_check: bool,
|
||||
}
|
||||
|
||||
impl ModuleAttributes {
|
||||
fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> {
|
||||
if meta.path.is_ident("name") {
|
||||
match meta.value() {
|
||||
Ok(value) => {
|
||||
self.name = Some(value.parse::<LitStr>()?.parse()?);
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(meta.error("`name` attribute must have a value"));
|
||||
}
|
||||
}
|
||||
} else if meta.path.is_ident("skip_memory_check") {
|
||||
if meta.value().is_ok() {
|
||||
return Err(meta.error("`skip_memory_check` attribute have no values"));
|
||||
}
|
||||
self.skip_memory_check = true;
|
||||
} else {
|
||||
return Err(meta.error("unsupported module attribute"));
|
||||
#[cfg(feature = "macros")]
|
||||
macro_rules! try_compile {
|
||||
($expr:expr) => {
|
||||
match $expr {
|
||||
Ok(val) => val,
|
||||
Err(err) => return err.to_compile_error().into(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let mut args = ModuleAttributes::default();
|
||||
if !attr.is_empty() {
|
||||
let args_parser = syn::meta::parser(|meta| args.parse(meta));
|
||||
parse_macro_input!(attr with args_parser);
|
||||
}
|
||||
|
||||
let func = parse_macro_input!(item as ItemFn);
|
||||
let func_name = &func.sig.ident;
|
||||
let module_name = args.name.unwrap_or_else(|| func_name.clone());
|
||||
let ext_entrypoint_name = Ident::new(&format!("luaopen_{module_name}"), Span::call_site());
|
||||
let skip_memory_check = if args.skip_memory_check {
|
||||
quote! { lua.skip_memory_check(true); }
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
let wrapped = quote! {
|
||||
mlua::require_module_feature!();
|
||||
|
||||
#func
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C-unwind" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> ::std::os::raw::c_int {
|
||||
mlua::Lua::entrypoint1(state, move |lua| {
|
||||
#skip_memory_check
|
||||
#func_name(lua)
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
wrapped.into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
fn to_ident(tt: &TokenTree) -> TokenStream2 {
|
||||
let s: TokenStream = tt.clone().into();
|
||||
s.into()
|
||||
module::lua_module(attr, item)
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
#[proc_macro]
|
||||
#[proc_macro_error]
|
||||
pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
let chunk = Chunk::new(input);
|
||||
|
||||
let source = chunk.source();
|
||||
|
||||
let caps_len = chunk.captures().len();
|
||||
let caps = chunk.captures().iter().map(|cap| {
|
||||
let cap_name = cap.as_rust().to_string();
|
||||
let cap = to_ident(cap.as_rust());
|
||||
quote! { env.raw_set(#cap_name, #cap)?; }
|
||||
});
|
||||
|
||||
let wrapped_code = quote! {{
|
||||
use mlua::{AsChunk, ChunkMode, Lua, Result, Table};
|
||||
use ::std::borrow::Cow;
|
||||
use ::std::cell::Cell;
|
||||
use ::std::io::Result as IoResult;
|
||||
|
||||
struct InnerChunk<F: FnOnce(&Lua) -> Result<Table>>(Cell<Option<F>>);
|
||||
|
||||
impl<F> AsChunk for InnerChunk<F>
|
||||
where
|
||||
F: FnOnce(&Lua) -> Result<Table>,
|
||||
{
|
||||
fn environment(&self, lua: &Lua) -> Result<Option<Table>> {
|
||||
if #caps_len > 0 {
|
||||
if let Some(make_env) = self.0.take() {
|
||||
return make_env(lua).map(Some);
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn mode(&self) -> Option<ChunkMode> {
|
||||
Some(ChunkMode::Text)
|
||||
}
|
||||
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Borrowed((#source).as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
let make_env = move |lua: &Lua| -> Result<Table> {
|
||||
let globals = lua.globals();
|
||||
let env = lua.create_table()?;
|
||||
let meta = lua.create_table()?;
|
||||
meta.raw_set("__index", &globals)?;
|
||||
meta.raw_set("__newindex", &globals)?;
|
||||
|
||||
// Add captured variables
|
||||
#(#caps)*
|
||||
|
||||
env.set_metatable(Some(meta))?;
|
||||
Ok(env)
|
||||
};
|
||||
|
||||
InnerChunk(Cell::new(Some(make_env)))
|
||||
}};
|
||||
|
||||
wrapped_code.into()
|
||||
Chunk::new(input).expand().into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
@@ -151,9 +33,23 @@ pub fn from_lua(input: TokenStream) -> TokenStream {
|
||||
from_lua::from_lua(input)
|
||||
}
|
||||
|
||||
/// Derive macro for implementing `UserData` for a Rust type.
|
||||
#[cfg(feature = "macros")]
|
||||
#[proc_macro_derive(UserData, attributes(lua))]
|
||||
pub fn userdata(item: TokenStream) -> TokenStream {
|
||||
userdata::userdata_type(item)
|
||||
}
|
||||
|
||||
/// Attribute macro for exposing impl block methods to Lua userdata.
|
||||
#[cfg(feature = "macros")]
|
||||
#[proc_macro_attribute]
|
||||
pub fn userdata_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
userdata::userdata_impl::userdata_impl(attr, item)
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
mod chunk;
|
||||
#[cfg(feature = "macros")]
|
||||
mod from_lua;
|
||||
#[cfg(feature = "macros")]
|
||||
mod token;
|
||||
mod userdata;
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use quote::quote;
|
||||
use syn::meta::ParseNestedMeta;
|
||||
use syn::{ItemFn, LitStr, Result, parse_macro_input};
|
||||
|
||||
#[derive(Default)]
|
||||
struct ModuleAttributes {
|
||||
name: Option<Ident>,
|
||||
skip_memory_check: bool,
|
||||
}
|
||||
|
||||
impl ModuleAttributes {
|
||||
fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> {
|
||||
if meta.path.is_ident("name") {
|
||||
match meta.value() {
|
||||
Ok(value) => {
|
||||
self.name = Some(value.parse::<LitStr>()?.parse()?);
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(meta.error("`name` attribute must have a value"));
|
||||
}
|
||||
}
|
||||
} else if meta.path.is_ident("skip_memory_check") {
|
||||
if meta.value().is_ok() {
|
||||
return Err(meta.error("`skip_memory_check` attribute have no values"));
|
||||
}
|
||||
self.skip_memory_check = true;
|
||||
} else {
|
||||
return Err(meta.error("unsupported module attribute"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let mut args = ModuleAttributes::default();
|
||||
if !attr.is_empty() {
|
||||
let args_parser = syn::meta::parser(|meta| args.parse(meta));
|
||||
parse_macro_input!(attr with args_parser);
|
||||
}
|
||||
|
||||
let func = parse_macro_input!(item as ItemFn);
|
||||
let func_name = &func.sig.ident;
|
||||
let module_name = args.name.unwrap_or_else(|| func_name.clone());
|
||||
let ext_entrypoint_name = Ident::new(&format!("luaopen_{module_name}"), Span::call_site());
|
||||
let skip_memory_check = if args.skip_memory_check {
|
||||
quote! { lua.skip_memory_check(true); }
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
let wrapped = quote! {
|
||||
mlua::require_module_feature!();
|
||||
|
||||
#func
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C-unwind" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> ::std::os::raw::c_int {
|
||||
mlua::Lua::entrypoint1(state, move |lua| {
|
||||
#skip_memory_check
|
||||
#func_name(lua)
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
wrapped.into()
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
use proc_macro2::Span;
|
||||
use syn::meta::ParseNestedMeta;
|
||||
use syn::{Ident, LitStr, Result};
|
||||
|
||||
/// Parsed `#[lua(...)]` attribute.
|
||||
///
|
||||
/// Some flags are context-dependent:
|
||||
/// - Struct fields: `get`, `set`, `name`, `skip`
|
||||
/// - Impl methods: `getter`, `setter`, `field`, `meta`, `infallible`, `name`, `skip`
|
||||
#[derive(Default)]
|
||||
pub(crate) struct LuaAttr {
|
||||
pub(crate) span: Option<Span>,
|
||||
pub(crate) name: Option<String>,
|
||||
pub(crate) infallible: bool,
|
||||
pub(crate) skip: bool,
|
||||
|
||||
// Struct field context flags
|
||||
pub(crate) get: bool,
|
||||
pub(crate) set: bool,
|
||||
|
||||
// Impl method context flags
|
||||
pub(crate) getter: bool,
|
||||
pub(crate) setter: bool,
|
||||
pub(crate) field: bool,
|
||||
pub(crate) meta: bool,
|
||||
}
|
||||
|
||||
impl LuaAttr {
|
||||
pub(crate) fn parse_inner(&mut self, meta: ParseNestedMeta) -> Result<()> {
|
||||
match &meta.path {
|
||||
path if path.is_ident("skip") => {
|
||||
if meta.value().is_ok() {
|
||||
return Err(meta.error("`skip` does not take a value"));
|
||||
}
|
||||
self.skip = true;
|
||||
}
|
||||
path if path.is_ident("infallible") => {
|
||||
if meta.value().is_ok() {
|
||||
return Err(meta.error("`infallible` does not take a value"));
|
||||
}
|
||||
self.infallible = true;
|
||||
}
|
||||
path if path.is_ident("get") => self.get = true,
|
||||
path if path.is_ident("set") => self.set = true,
|
||||
path if path.is_ident("getter") => self.getter = true,
|
||||
path if path.is_ident("setter") => self.setter = true,
|
||||
path if path.is_ident("field") => self.field = true,
|
||||
path if path.is_ident("meta") => self.meta = true,
|
||||
path if path.is_ident("name") => {
|
||||
let value = meta.value()?;
|
||||
let lit: LitStr = value.parse()?;
|
||||
self.name = Some(lit.value());
|
||||
}
|
||||
_ => {
|
||||
return Err(meta.error(
|
||||
"unsupported lua attribute, expected: ".to_string()
|
||||
+ "`skip`, `infallible`, `get`, `set`, `getter`, `setter`, `field`, `meta`, `name`",
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the effective Lua name.
|
||||
pub(crate) fn name(&self, ident: &Ident) -> String {
|
||||
self.name.clone().unwrap_or_else(|| ident.to_string())
|
||||
}
|
||||
|
||||
/// Returns the span to use for error reporting.
|
||||
pub(crate) fn span(&self) -> Span {
|
||||
self.span.unwrap_or_else(Span::call_site)
|
||||
}
|
||||
|
||||
/// Returns the effective Lua metamethod name.
|
||||
///
|
||||
/// If `name` is set via attribute, use it. Otherwise, if the function name
|
||||
/// starts with `__`, use that. Returns an error if neither is available.
|
||||
pub(crate) fn effective_meta_name(&self, fn_ident: &Ident) -> Result<String> {
|
||||
if let Some(ref name) = self.name {
|
||||
return Ok(name.clone());
|
||||
}
|
||||
let fn_name = fn_ident.to_string();
|
||||
if fn_name.starts_with("__") {
|
||||
return Ok(fn_name);
|
||||
}
|
||||
Err(syn::Error::new(
|
||||
fn_ident.span(),
|
||||
format!(
|
||||
"could not infer metamethod name from `{fn_name}`, either add `name = \"...\"` to `#[lua(meta, ...)]` or prefix the function with `__`"
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
mod attr;
|
||||
pub(crate) mod userdata_impl;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{Attribute, Data, DeriveInput, Error, Fields, FieldsNamed, Meta, parse_macro_input};
|
||||
|
||||
use self::attr::LuaAttr;
|
||||
|
||||
/// Wrap registration tokens with any `#[cfg]`/`#[cfg_attr]` attributes from the original item.
|
||||
pub(crate) fn with_cfg(tokens: proc_macro2::TokenStream, attrs: &[Attribute]) -> proc_macro2::TokenStream {
|
||||
let cfgs: Vec<_> = (attrs.iter())
|
||||
.filter(|attr| attr.path().is_ident("cfg") || attr.path().is_ident("cfg_attr"))
|
||||
.collect();
|
||||
if cfgs.is_empty() {
|
||||
return tokens;
|
||||
}
|
||||
quote! {
|
||||
#(#cfgs)*
|
||||
#tokens
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse all `#[lua(...)]` attributes on a field, merging them into one `LuaAttr`.
|
||||
fn parse_field_lua_attr(attrs: &[Attribute]) -> syn::Result<LuaAttr> {
|
||||
let mut lua_attr = LuaAttr::default();
|
||||
for attr in attrs {
|
||||
if !attr.path().is_ident("lua") {
|
||||
continue;
|
||||
}
|
||||
match &attr.meta {
|
||||
Meta::List(_) => {
|
||||
lua_attr.span = Some(attr.span());
|
||||
attr.parse_nested_meta(|meta| lua_attr.parse_inner(meta))?;
|
||||
validate_field_lua_attr(&lua_attr)?;
|
||||
}
|
||||
Meta::Path(_) => {}
|
||||
Meta::NameValue(_) => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
attr,
|
||||
"`#[lua = \"...\"]` is not supported: use `#[lua(attr = \"...\")]`",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(lua_attr)
|
||||
}
|
||||
|
||||
fn validate_field_lua_attr(attr: &LuaAttr) -> syn::Result<()> {
|
||||
for (set, name) in [
|
||||
(attr.getter, "getter"),
|
||||
(attr.setter, "setter"),
|
||||
(attr.field, "field"),
|
||||
(attr.meta, "meta"),
|
||||
(attr.infallible, "infallible"),
|
||||
] {
|
||||
if set {
|
||||
return Err(syn::Error::new(
|
||||
attr.span(),
|
||||
format!("`{name}` is not valid for struct fields"),
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn userdata_type(item: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(item as DeriveInput);
|
||||
let type_name = &input.ident;
|
||||
|
||||
let named_fields: Option<&FieldsNamed> = match &input.data {
|
||||
Data::Struct(data) => match &data.fields {
|
||||
Fields::Named(fields) => Some(fields),
|
||||
Fields::Unnamed(_) | Fields::Unit => None,
|
||||
},
|
||||
Data::Enum(_) => None,
|
||||
Data::Union(_) => {
|
||||
return Error::new_spanned(&input, "`#[derive(UserData)]` cannot be applied to unions")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
};
|
||||
|
||||
// Check for generic parameters (not supported)
|
||||
let has_generics = !input.generics.params.is_empty();
|
||||
if has_generics {
|
||||
return Error::new_spanned(
|
||||
&input.generics,
|
||||
"`#[derive(UserData)]` does not support generic type parameters. Wrap the generic type in a concrete newtype instead."
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let mut field_registrations = Vec::new();
|
||||
if let Some(fields) = &named_fields {
|
||||
for field in &fields.named {
|
||||
let field_name = field.ident.as_ref().unwrap();
|
||||
|
||||
let lua_attr = try_compile!(parse_field_lua_attr(&field.attrs));
|
||||
if lua_attr.skip {
|
||||
continue;
|
||||
}
|
||||
|
||||
let lua_name = lua_attr.name.unwrap_or_else(|| field_name.to_string());
|
||||
|
||||
// Assume get/set by default (unless explicitly specified)
|
||||
let (has_get, has_set) = if lua_attr.get || lua_attr.set {
|
||||
(lua_attr.get, lua_attr.set)
|
||||
} else {
|
||||
(true, true)
|
||||
};
|
||||
|
||||
if has_get {
|
||||
let tokens = quote! {
|
||||
registry.add_field_method_get(#lua_name, |_lua, this| Ok(this.#field_name.clone()));
|
||||
};
|
||||
field_registrations.push(with_cfg(tokens, &field.attrs));
|
||||
}
|
||||
if has_set {
|
||||
let tokens = quote! {
|
||||
registry.add_field_method_set(#lua_name, |_lua, this, val| {
|
||||
this.#field_name = val;
|
||||
Ok(())
|
||||
});
|
||||
};
|
||||
field_registrations.push(with_cfg(tokens, &field.attrs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let registration_type_name = format_ident!("__MluaUserDataRegistration_{type_name}");
|
||||
let register_fields_fn_name = format_ident!("__mlua_register_{type_name}_fields");
|
||||
|
||||
let output = quote! {
|
||||
#[doc(hidden)]
|
||||
#[allow(non_camel_case_types)]
|
||||
struct #registration_type_name {
|
||||
register: fn(&mut ::mlua::userdata::UserDataRegistry<#type_name>),
|
||||
}
|
||||
|
||||
::mlua::__inventory::collect!(#registration_type_name);
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn #register_fields_fn_name(registry: &mut ::mlua::userdata::UserDataRegistry<#type_name>) {
|
||||
use ::mlua::userdata::UserDataFields as _;
|
||||
#(#field_registrations)*
|
||||
}
|
||||
|
||||
::mlua::__inventory::submit! {
|
||||
#registration_type_name { register: #register_fields_fn_name }
|
||||
}
|
||||
|
||||
impl ::mlua::userdata::UserData for #type_name {
|
||||
fn register(registry: &mut ::mlua::userdata::UserDataRegistry<Self>) {
|
||||
for item in ::mlua::__inventory::iter::<#registration_type_name> {
|
||||
(item.register)(registry);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
output.into()
|
||||
}
|
||||
@@ -0,0 +1,730 @@
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{
|
||||
Attribute, FnArg, Ident, ImplItem, ItemImpl, Meta, Signature, Type, parse_macro_input, parse_quote,
|
||||
};
|
||||
|
||||
use super::attr::LuaAttr;
|
||||
use super::with_cfg;
|
||||
|
||||
/// `&T` reference types that mlua provides as wrapper types via `FromLua`.
|
||||
static BORROW_WRAPPERS: &[(&str, &str)] = &[
|
||||
("str", "::mlua::string::BorrowedStr"),
|
||||
("[u8]", "::mlua::string::BorrowedBytes"),
|
||||
];
|
||||
|
||||
enum SelfKind {
|
||||
Ref(RefKind),
|
||||
Owned,
|
||||
None,
|
||||
}
|
||||
|
||||
enum RefKind {
|
||||
Ref,
|
||||
Mut,
|
||||
}
|
||||
|
||||
struct ArgInfo {
|
||||
ident: Ident,
|
||||
userdata_ref: Option<RefKind>,
|
||||
callback_type: Type,
|
||||
}
|
||||
|
||||
struct MethodInfo {
|
||||
self_kind: SelfKind,
|
||||
has_lua: bool,
|
||||
args: Vec<ArgInfo>,
|
||||
}
|
||||
|
||||
/// Extract the inner type from a reference type.
|
||||
fn ref_inner_type(ty: &Type) -> Type {
|
||||
match ty {
|
||||
Type::Reference(ref_ty) => (*ref_ty.elem).clone(),
|
||||
_ => ty.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the type is `&Lua` or `&mlua::Lua`.
|
||||
fn is_lua_ref(ty: &Type) -> bool {
|
||||
let Type::Reference(ref_ty) = ty else { return false };
|
||||
match &*ref_ty.elem {
|
||||
Type::Path(p) if p.path.segments.len() == 1 => p.path.segments[0].ident == "Lua",
|
||||
Type::Path(p) if p.path.segments.len() == 2 => {
|
||||
p.path.segments[0].ident == "mlua" && p.path.segments[1].ident == "Lua"
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Classify a `&[mut] T` parameter, returning the callback wrapper type.
|
||||
///
|
||||
/// Known borrow types come from the mapping table `BORROW_WRAPPERS`.
|
||||
/// Everything else gets `UserDataRef[Mut]<T>`.
|
||||
fn classify_ref_type(ty: &Type) -> Option<Type> {
|
||||
let Type::Reference(ref_ty) = ty else { return None };
|
||||
|
||||
// Check known borrow wrappers:
|
||||
// - For `&T` check the path name
|
||||
// - For `&[T]` unpack the slice and format the element as `[T]` for lookup
|
||||
if ref_ty.mutability.is_none() {
|
||||
let lookup_name: Option<String> = match &*ref_ty.elem {
|
||||
Type::Path(path) => path.path.segments.last().map(|seg| seg.ident.to_string()),
|
||||
Type::Slice(slice) => {
|
||||
if let Type::Path(path) = &*slice.elem {
|
||||
path.path.segments.last().map(|seg| format!("[{}]", seg.ident))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
if let Some(ref name) = lookup_name {
|
||||
for &(inner, wrapper) in BORROW_WRAPPERS {
|
||||
if name == inner {
|
||||
let wrapper = syn::parse_str(wrapper).expect("invalid wrapper type");
|
||||
return Some(wrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Mutable references to slices are not supported.
|
||||
if matches!(&*ref_ty.elem, Type::Slice(_)) && ref_ty.mutability.is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let inner = ref_inner_type(ty);
|
||||
if ref_ty.mutability.is_none() {
|
||||
Some(parse_quote! { ::mlua::userdata::UserDataRef<#inner> })
|
||||
} else {
|
||||
Some(parse_quote! { ::mlua::userdata::UserDataRefMut<#inner> })
|
||||
}
|
||||
}
|
||||
|
||||
/// Analyze method signature.
|
||||
///
|
||||
/// Determine `self` kind and collect the callback arguments.
|
||||
/// Auto-detects `&Lua` as the first non-self parameter.
|
||||
fn analyze_self_and_args(sig: &Signature) -> syn::Result<MethodInfo> {
|
||||
let mut self_kind = SelfKind::None;
|
||||
let mut has_lua = false;
|
||||
let mut args = Vec::new();
|
||||
let mut check_first_typed = true;
|
||||
|
||||
for param in &sig.inputs {
|
||||
match param {
|
||||
FnArg::Receiver(recv) if recv.reference.is_some() && recv.mutability.is_some() => {
|
||||
self_kind = SelfKind::Ref(RefKind::Mut);
|
||||
}
|
||||
FnArg::Receiver(recv) if recv.reference.is_some() => {
|
||||
self_kind = SelfKind::Ref(RefKind::Ref);
|
||||
}
|
||||
FnArg::Receiver(_) => {
|
||||
self_kind = SelfKind::Owned;
|
||||
}
|
||||
FnArg::Typed(typed) => {
|
||||
if check_first_typed && is_lua_ref(&typed.ty) {
|
||||
has_lua = true;
|
||||
check_first_typed = false;
|
||||
continue;
|
||||
}
|
||||
check_first_typed = false;
|
||||
if let syn::Pat::Ident(pat_ident) = &*typed.pat {
|
||||
let arg_type = &*typed.ty;
|
||||
let ref_kind = match arg_type {
|
||||
Type::Reference(r) if r.mutability.is_some() => Some(RefKind::Mut),
|
||||
Type::Reference(_) => Some(RefKind::Ref),
|
||||
_ => None,
|
||||
};
|
||||
let callback_type = match &ref_kind {
|
||||
Some(_) => match classify_ref_type(arg_type) {
|
||||
Some(ty) => ty,
|
||||
None => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
arg_type,
|
||||
"this reference type is not supported as a callback parameter",
|
||||
));
|
||||
}
|
||||
},
|
||||
None => arg_type.clone(),
|
||||
};
|
||||
args.push(ArgInfo {
|
||||
ident: pat_ident.ident.clone(),
|
||||
userdata_ref: ref_kind,
|
||||
callback_type,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(MethodInfo {
|
||||
self_kind,
|
||||
has_lua,
|
||||
args,
|
||||
})
|
||||
}
|
||||
|
||||
fn strip_item_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
|
||||
(attrs.iter())
|
||||
.filter(|attr| !attr.path().is_ident("lua"))
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn parse_lua_attr(attrs: &[Attribute]) -> syn::Result<LuaAttr> {
|
||||
let mut lua_attr = LuaAttr::default();
|
||||
for attr in attrs {
|
||||
if !attr.path().is_ident("lua") {
|
||||
continue;
|
||||
}
|
||||
match &attr.meta {
|
||||
Meta::List(_) => {
|
||||
lua_attr.span = Some(attr.span());
|
||||
attr.parse_nested_meta(|meta| lua_attr.parse_inner(meta))?;
|
||||
validate_lua_attr(&lua_attr)?;
|
||||
}
|
||||
Meta::Path(_) => {}
|
||||
Meta::NameValue(_) => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
attr,
|
||||
"`#[lua = \"...\"]` is not supported: use `#[lua(attr = \"...\")]`",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(lua_attr)
|
||||
}
|
||||
|
||||
fn validate_lua_attr(attr: &LuaAttr) -> syn::Result<()> {
|
||||
for (set, name) in [(attr.get, "get"), (attr.set, "set")] {
|
||||
if set {
|
||||
return Err(syn::Error::new(
|
||||
attr.span(),
|
||||
format!("`{name}` is not valid for methods"),
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn userdata_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
if !attr.is_empty() {
|
||||
return syn::Error::new_spanned(
|
||||
proc_macro2::TokenStream::from(attr),
|
||||
"`#[userdata_impl]` does not accept arguments",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let mut input = parse_macro_input!(item as ItemImpl);
|
||||
|
||||
let type_path = match &*input.self_ty {
|
||||
Type::Path(type_path) => &type_path.path,
|
||||
_ => {
|
||||
return syn::Error::new_spanned(&input.self_ty, "`#[userdata_impl]` requires a simple path type")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
};
|
||||
let type_name = (type_path.segments)
|
||||
.last()
|
||||
.map(|seg| seg.ident.clone())
|
||||
.ok_or_else(|| syn::Error::new_spanned(&input.self_ty, "cannot determine type name"));
|
||||
let type_name = try_compile!(type_name);
|
||||
|
||||
static COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
let unique_suffix = COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
let register_fn_name = format_ident!("__mlua_register_{type_name}_{unique_suffix}");
|
||||
let registration_type_name = format_ident!("__MluaUserDataRegistration_{type_name}");
|
||||
|
||||
let mut registration_calls = Vec::new();
|
||||
for item in &input.items {
|
||||
match item {
|
||||
ImplItem::Const(const_item) => {
|
||||
let lua_attr = try_compile!(parse_lua_attr(&const_item.attrs));
|
||||
if lua_attr.skip {
|
||||
continue;
|
||||
}
|
||||
if lua_attr.getter || lua_attr.setter {
|
||||
return syn::Error::new(
|
||||
lua_attr.span(),
|
||||
"const items do not support `getter` or `setter`",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
let const_name = &const_item.ident;
|
||||
let lua_name = lua_attr.name(const_name);
|
||||
if lua_attr.meta {
|
||||
let tokens = quote! {
|
||||
registry.add_meta_field(#lua_name, #type_path::#const_name);
|
||||
};
|
||||
registration_calls.push(with_cfg(tokens, &const_item.attrs));
|
||||
} else {
|
||||
let tokens = quote! {
|
||||
registry.add_field(#lua_name, #type_path::#const_name);
|
||||
};
|
||||
registration_calls.push(with_cfg(tokens, &const_item.attrs));
|
||||
}
|
||||
}
|
||||
ImplItem::Fn(method) => {
|
||||
let lua_attr = try_compile!(parse_lua_attr(&method.attrs));
|
||||
if lua_attr.skip {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validate mutually exclusive role flags.
|
||||
// `getter`, `setter`, `field` are exclusive.
|
||||
// `meta` on its own means a metamethod.
|
||||
// `meta` combined with `field` means a meta static field.
|
||||
// `meta` with `getter` or `setter` is invalid.
|
||||
let primary = [lua_attr.getter, lua_attr.setter, lua_attr.field];
|
||||
let primary_count = primary.iter().filter(|&&x| x).count();
|
||||
if primary_count > 1 {
|
||||
return syn::Error::new(
|
||||
lua_attr.span(),
|
||||
"at most one of `getter`, `setter`, `field` can be specified",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
if lua_attr.meta && primary_count == 1 && !lua_attr.field {
|
||||
return syn::Error::new(lua_attr.span(), "`meta` can only be combined with `field`")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let fn_name = &method.sig.ident;
|
||||
let info = try_compile!(analyze_self_and_args(&method.sig));
|
||||
let is_async = method.sig.asyncness.is_some();
|
||||
|
||||
if lua_attr.getter {
|
||||
if is_async {
|
||||
return syn::Error::new_spanned(&method.sig, "async field getter is not supported")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
if !matches!(info.self_kind, SelfKind::Ref(RefKind::Ref)) {
|
||||
return syn::Error::new_spanned(&method.sig, "field getter must take `&self`")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
if !info.args.is_empty() {
|
||||
return syn::Error::new_spanned(
|
||||
&method.sig,
|
||||
"field getter must not take additional arguments",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
let tokens = gen_field_getter(type_path, fn_name, &lua_attr, &info);
|
||||
registration_calls.push(with_cfg(tokens, &method.attrs));
|
||||
continue;
|
||||
}
|
||||
if lua_attr.setter {
|
||||
if is_async {
|
||||
return syn::Error::new_spanned(&method.sig, "async field setter is not supported")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
if !matches!(info.self_kind, SelfKind::Ref(_)) {
|
||||
return syn::Error::new_spanned(&method.sig, "field setter must take `&[mut] self`")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
if info.args.len() != 1 {
|
||||
return syn::Error::new_spanned(
|
||||
&method.sig,
|
||||
"field setter must take exactly one value argument",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
let tokens = gen_field_setter(type_path, fn_name, &lua_attr, &info);
|
||||
registration_calls.push(with_cfg(tokens, &method.attrs));
|
||||
continue;
|
||||
}
|
||||
if lua_attr.field {
|
||||
if is_async {
|
||||
return syn::Error::new_spanned(&method.sig, "async field function is not supported")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
if !matches!(info.self_kind, SelfKind::None) {
|
||||
return syn::Error::new_spanned(&method.sig, "field function must not take `self`")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
if !info.args.is_empty() {
|
||||
return syn::Error::new_spanned(
|
||||
&method.sig,
|
||||
"field function must not take arguments",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
let lua_name = lua_attr.name(fn_name);
|
||||
if lua_attr.meta {
|
||||
let tokens = quote! {
|
||||
registry.add_meta_field(#lua_name, #type_path::#fn_name());
|
||||
};
|
||||
registration_calls.push(with_cfg(tokens, &method.attrs));
|
||||
} else {
|
||||
let tokens = quote! {
|
||||
registry.add_field(#lua_name, #type_path::#fn_name());
|
||||
};
|
||||
registration_calls.push(with_cfg(tokens, &method.attrs));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if lua_attr.meta {
|
||||
if matches!(info.self_kind, SelfKind::Owned) {
|
||||
return syn::Error::new_spanned(
|
||||
&method.sig,
|
||||
"meta methods cannot take `self`, use `&[mut] self` instead",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
if is_async {
|
||||
let tokens = gen_async_meta(type_path, fn_name, &lua_attr, &info);
|
||||
registration_calls.push(with_cfg(tokens, &method.attrs));
|
||||
} else {
|
||||
let tokens = gen_meta(type_path, fn_name, &lua_attr, &info);
|
||||
registration_calls.push(with_cfg(tokens, &method.attrs));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if is_async {
|
||||
let tokens = gen_async_regular_method(type_path, fn_name, &lua_attr, &info);
|
||||
registration_calls.push(with_cfg(tokens, &method.attrs));
|
||||
} else {
|
||||
let tokens = gen_regular_method(type_path, fn_name, &lua_attr, &info);
|
||||
registration_calls.push(with_cfg(tokens, &method.attrs));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
for item in &mut input.items {
|
||||
match item {
|
||||
ImplItem::Const(c) => c.attrs = strip_item_attrs(&c.attrs),
|
||||
ImplItem::Fn(m) => m.attrs = strip_item_attrs(&m.attrs),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
input.attrs = strip_item_attrs(&input.attrs);
|
||||
|
||||
let output = quote! {
|
||||
#[allow(non_snake_case)]
|
||||
fn #register_fn_name(registry: &mut ::mlua::userdata::UserDataRegistry<#type_path>) {
|
||||
use ::mlua::userdata::{UserDataFields as _, UserDataMethods as _};
|
||||
#(#registration_calls)*
|
||||
}
|
||||
|
||||
::mlua::__inventory::submit! {
|
||||
#registration_type_name { register: #register_fn_name }
|
||||
}
|
||||
|
||||
#input
|
||||
};
|
||||
|
||||
output.into()
|
||||
}
|
||||
|
||||
/// Generate the closure argument destructuring pattern.
|
||||
fn gen_closure_destructure(info: &MethodInfo) -> TokenStream2 {
|
||||
if info.args.is_empty() {
|
||||
return quote! { () };
|
||||
}
|
||||
let idents: Vec<_> = (info.args)
|
||||
.iter()
|
||||
.map(|a| {
|
||||
let ident = &a.ident;
|
||||
if matches!(a.userdata_ref, Some(RefKind::Mut)) {
|
||||
quote! { mut #ident }
|
||||
} else {
|
||||
quote! { #ident }
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let types: Vec<_> = info.args.iter().map(|a| &a.callback_type).collect();
|
||||
quote! { (#(#idents),*): (#(#types),*) }
|
||||
}
|
||||
|
||||
/// Generate call arguments for invoking the original method.
|
||||
fn gen_call_args(info: &MethodInfo) -> TokenStream2 {
|
||||
let mut call_args: Vec<TokenStream2> = Vec::new();
|
||||
|
||||
match info.self_kind {
|
||||
SelfKind::None => {}
|
||||
_ => call_args.push(quote! { this }),
|
||||
}
|
||||
|
||||
if info.has_lua {
|
||||
call_args.push(quote! { lua });
|
||||
}
|
||||
|
||||
for arg in &info.args {
|
||||
let ident = &arg.ident;
|
||||
match arg.userdata_ref {
|
||||
Some(RefKind::Ref) => call_args.push(quote! { &*#ident }),
|
||||
Some(RefKind::Mut) => call_args.push(quote! { &mut *#ident }),
|
||||
None => call_args.push(quote! { #ident }),
|
||||
}
|
||||
}
|
||||
|
||||
quote! { #(#call_args),* }
|
||||
}
|
||||
|
||||
/// Generate call arguments for invoking the original async method.
|
||||
fn gen_async_call_args(info: &MethodInfo) -> TokenStream2 {
|
||||
let mut call_args: Vec<TokenStream2> = Vec::new();
|
||||
|
||||
match info.self_kind {
|
||||
SelfKind::None => {}
|
||||
SelfKind::Ref(RefKind::Ref) => call_args.push(quote! { &this }),
|
||||
SelfKind::Ref(RefKind::Mut) => call_args.push(quote! { &mut this }),
|
||||
SelfKind::Owned => call_args.push(quote! { this }),
|
||||
}
|
||||
|
||||
if info.has_lua {
|
||||
call_args.push(quote! { lua });
|
||||
}
|
||||
|
||||
for arg in &info.args {
|
||||
let ident = &arg.ident;
|
||||
match arg.userdata_ref {
|
||||
Some(RefKind::Ref) => call_args.push(quote! { &*#ident }),
|
||||
Some(RefKind::Mut) => call_args.push(quote! { &mut *#ident }),
|
||||
None => call_args.push(quote! { #ident }),
|
||||
}
|
||||
}
|
||||
|
||||
quote! { #(#call_args),* }
|
||||
}
|
||||
|
||||
/// Generate the closure params for the registration callback.
|
||||
fn gen_closure_params(info: &MethodInfo) -> TokenStream2 {
|
||||
let destructure = gen_closure_destructure(info);
|
||||
match info.self_kind {
|
||||
SelfKind::None => quote! { |lua, #destructure| },
|
||||
_ => quote! { |lua, this, #destructure| },
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the closure params for an async registration callback.
|
||||
fn gen_async_closure_params(info: &MethodInfo) -> TokenStream2 {
|
||||
let destructure = gen_closure_destructure(info);
|
||||
match info.self_kind {
|
||||
SelfKind::None => quote! { |lua, #destructure| },
|
||||
SelfKind::Ref(RefKind::Mut) => quote! { |lua, mut this, #destructure| },
|
||||
_ => quote! { |lua, this, #destructure| },
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_field_getter(
|
||||
type_path: &syn::Path,
|
||||
fn_name: &Ident,
|
||||
lua_attr: &LuaAttr,
|
||||
info: &MethodInfo,
|
||||
) -> TokenStream2 {
|
||||
let lua_name = lua_attr.name(fn_name);
|
||||
let call_args = gen_call_args(info);
|
||||
|
||||
if lua_attr.infallible {
|
||||
return quote! {
|
||||
registry.add_field_method_get(#lua_name, |lua, this| {
|
||||
let _ = lua; // silence unused variable warning
|
||||
Ok(#type_path::#fn_name(#call_args))
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
quote! {
|
||||
registry.add_field_method_get(#lua_name, |lua, this| {
|
||||
let _ = lua; // silence unused variable warning
|
||||
#type_path::#fn_name(#call_args)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_field_setter(
|
||||
type_path: &syn::Path,
|
||||
fn_name: &Ident,
|
||||
lua_attr: &LuaAttr,
|
||||
info: &MethodInfo,
|
||||
) -> TokenStream2 {
|
||||
let lua_name = lua_attr.name(fn_name);
|
||||
let call_args = gen_call_args(info);
|
||||
|
||||
if lua_attr.infallible {
|
||||
let val_ident = info.args.first().map(|a| &a.ident);
|
||||
return quote! {
|
||||
registry.add_field_method_set(#lua_name, |lua, this, #val_ident| {
|
||||
let _ = lua; // silence unused variable warning
|
||||
Ok(#type_path::#fn_name(#call_args))
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
let val_ident = info.args.first().map(|a| &a.ident);
|
||||
quote! {
|
||||
registry.add_field_method_set(#lua_name, |lua, this, #val_ident| {
|
||||
let _ = lua; // silence unused variable warning
|
||||
#type_path::#fn_name(#call_args)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_meta(type_path: &syn::Path, fn_name: &Ident, lua_attr: &LuaAttr, info: &MethodInfo) -> TokenStream2 {
|
||||
let meta_name = match lua_attr.effective_meta_name(fn_name) {
|
||||
Ok(name) => name,
|
||||
Err(err) => return err.to_compile_error(),
|
||||
};
|
||||
let closure_params = if matches!(info.self_kind, SelfKind::None) {
|
||||
// Lua always passes `self` to the stack arg, just ignore it.
|
||||
if info.args.is_empty() {
|
||||
quote! { |lua, _this: ::mlua::AnyUserData| }
|
||||
} else {
|
||||
let idents: Vec<_> = info.args.iter().map(|a| &a.ident).collect();
|
||||
let types: Vec<_> = info.args.iter().map(|a| &a.callback_type).collect();
|
||||
quote! { |lua, (_this, #(#idents),*): (::mlua::AnyUserData, #(#types),*) | }
|
||||
}
|
||||
} else {
|
||||
gen_closure_params(info)
|
||||
};
|
||||
let call_args = gen_call_args(info);
|
||||
let fn_path = quote! { #type_path::#fn_name };
|
||||
|
||||
let body = if lua_attr.infallible {
|
||||
quote! { Ok(#fn_path(#call_args)) }
|
||||
} else {
|
||||
quote! { #fn_path(#call_args) }
|
||||
};
|
||||
match info.self_kind {
|
||||
SelfKind::None => quote! {
|
||||
registry.add_meta_function(#meta_name, #closure_params { #body });
|
||||
},
|
||||
SelfKind::Ref(RefKind::Mut) => quote! {
|
||||
registry.add_meta_method_mut(#meta_name, #closure_params { #body });
|
||||
},
|
||||
_ => quote! {
|
||||
registry.add_meta_method(#meta_name, #closure_params { #body });
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_regular_method(
|
||||
type_path: &syn::Path,
|
||||
fn_name: &Ident,
|
||||
lua_attr: &LuaAttr,
|
||||
info: &MethodInfo,
|
||||
) -> TokenStream2 {
|
||||
let fn_path = quote! { #type_path::#fn_name };
|
||||
let closure_params = gen_closure_params(info);
|
||||
let call_args = gen_call_args(info);
|
||||
let lua_name = lua_attr.name(fn_name);
|
||||
|
||||
let body = if lua_attr.infallible {
|
||||
quote! { Ok(#fn_path(#call_args)) }
|
||||
} else {
|
||||
quote! { #fn_path(#call_args) }
|
||||
};
|
||||
match info.self_kind {
|
||||
SelfKind::Ref(RefKind::Ref) => quote! {
|
||||
registry.add_method(#lua_name, #closure_params { #body });
|
||||
},
|
||||
SelfKind::Ref(RefKind::Mut) => quote! {
|
||||
registry.add_method_mut(#lua_name, #closure_params { #body });
|
||||
},
|
||||
SelfKind::Owned => quote! {
|
||||
registry.add_method_once(#lua_name, #closure_params { #body });
|
||||
},
|
||||
SelfKind::None => quote! {
|
||||
registry.add_function(#lua_name, #closure_params { #body });
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_async_regular_method(
|
||||
type_path: &syn::Path,
|
||||
fn_name: &Ident,
|
||||
lua_attr: &LuaAttr,
|
||||
info: &MethodInfo,
|
||||
) -> TokenStream2 {
|
||||
let fn_path = quote! { #type_path::#fn_name };
|
||||
let closure_params = gen_async_closure_params(info);
|
||||
let call_args = gen_async_call_args(info);
|
||||
let lua_name = lua_attr.name(fn_name);
|
||||
|
||||
let body = if lua_attr.infallible {
|
||||
quote! { async move { Ok(#fn_path(#call_args).await) } }
|
||||
} else {
|
||||
quote! { async move { #fn_path(#call_args).await } }
|
||||
};
|
||||
match info.self_kind {
|
||||
SelfKind::Ref(RefKind::Ref) => quote! {
|
||||
registry.add_async_method(#lua_name, #closure_params #body);
|
||||
},
|
||||
SelfKind::Ref(RefKind::Mut) => quote! {
|
||||
registry.add_async_method_mut(#lua_name, #closure_params #body);
|
||||
},
|
||||
SelfKind::Owned => quote! {
|
||||
registry.add_async_method_once(#lua_name, #closure_params #body);
|
||||
},
|
||||
SelfKind::None => quote! {
|
||||
registry.add_async_function(#lua_name, #closure_params #body);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_async_meta(
|
||||
type_path: &syn::Path,
|
||||
fn_name: &Ident,
|
||||
lua_attr: &LuaAttr,
|
||||
info: &MethodInfo,
|
||||
) -> TokenStream2 {
|
||||
let meta_name = match lua_attr.effective_meta_name(fn_name) {
|
||||
Ok(name) => name,
|
||||
Err(err) => return err.to_compile_error(),
|
||||
};
|
||||
let closure_params = if matches!(info.self_kind, SelfKind::None) {
|
||||
if info.args.is_empty() {
|
||||
quote! { |lua, _this: ::mlua::AnyUserData| }
|
||||
} else {
|
||||
let idents: Vec<_> = info.args.iter().map(|a| &a.ident).collect();
|
||||
let types: Vec<_> = info.args.iter().map(|a| &a.callback_type).collect();
|
||||
quote! { |lua, (_this, #(#idents),*): (::mlua::AnyUserData, #(#types),*) | }
|
||||
}
|
||||
} else {
|
||||
gen_async_closure_params(info)
|
||||
};
|
||||
let call_args = gen_async_call_args(info);
|
||||
let fn_path = quote! { #type_path::#fn_name };
|
||||
|
||||
let body = if lua_attr.infallible {
|
||||
quote! { async move { Ok(#fn_path(#call_args).await) } }
|
||||
} else {
|
||||
quote! { async move { #fn_path(#call_args).await } }
|
||||
};
|
||||
match info.self_kind {
|
||||
SelfKind::None => quote! {
|
||||
registry.add_async_meta_function(#meta_name, #closure_params #body);
|
||||
},
|
||||
SelfKind::Ref(RefKind::Mut) => quote! {
|
||||
registry.add_async_meta_method_mut(#meta_name, #closure_params #body);
|
||||
},
|
||||
_ => quote! {
|
||||
registry.add_async_meta_method(#meta_name, #closure_params #body);
|
||||
},
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -59,7 +59,7 @@ impl Buffer {
|
||||
/// Returns an adaptor implementing [`io::Read`], [`io::Write`] and [`io::Seek`] over the
|
||||
/// buffer.
|
||||
///
|
||||
/// Buffer operations are infallible, none of the read/write functions will return a Err.
|
||||
/// Buffer operations are infallible, none of the read/write functions will return an Err.
|
||||
pub fn cursor(self) -> impl io::Read + io::Write + io::Seek {
|
||||
BufferCursor(self, 0)
|
||||
}
|
||||
@@ -97,7 +97,7 @@ struct BufferCursor(Buffer, usize);
|
||||
|
||||
impl io::Read for BufferCursor {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let lua = self.0 .0.lua.lock();
|
||||
let lua = self.0.0.lua.lock();
|
||||
let data = self.0.as_slice(&lua);
|
||||
if self.1 == data.len() {
|
||||
return Ok(0);
|
||||
@@ -111,7 +111,7 @@ impl io::Read for BufferCursor {
|
||||
|
||||
impl io::Write for BufferCursor {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let lua = self.0 .0.lua.lock();
|
||||
let lua = self.0.0.lua.lock();
|
||||
let data = self.0.as_slice_mut(&lua);
|
||||
if self.1 == data.len() {
|
||||
return Ok(0);
|
||||
@@ -129,7 +129,7 @@ impl io::Write for BufferCursor {
|
||||
|
||||
impl io::Seek for BufferCursor {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
||||
let lua = self.0 .0.lua.lock();
|
||||
let lua = self.0.0.lua.lock();
|
||||
let data = self.0.as_slice(&lua);
|
||||
let new_offset = match pos {
|
||||
io::SeekFrom::Start(offset) => offset as i64,
|
||||
|
||||
+75
-72
@@ -1,10 +1,16 @@
|
||||
//! Lua chunk loading and execution.
|
||||
//!
|
||||
//! This module provides types for loading Lua source code or bytecode into a [`Chunk`],
|
||||
//! configuring how it is compiled and executed, and converting it into a callable [`Function`].
|
||||
//!
|
||||
//! Chunks can be loaded from strings, byte slices, or files via the [`AsChunk`] trait.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CString;
|
||||
use std::io::Result as IoResult;
|
||||
use std::panic::Location;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
@@ -20,7 +26,7 @@ pub trait AsChunk {
|
||||
/// Returns optional chunk name
|
||||
///
|
||||
/// See [`Chunk::set_name`] for possible name prefixes.
|
||||
fn name(&self) -> Option<StdString> {
|
||||
fn name(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -52,13 +58,13 @@ impl AsChunk for &str {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk for StdString {
|
||||
impl AsChunk for String {
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Owned(self.clone().into_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk for &StdString {
|
||||
impl AsChunk for &String {
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||
where
|
||||
Self: 'a,
|
||||
@@ -92,7 +98,7 @@ impl AsChunk for &Vec<u8> {
|
||||
}
|
||||
|
||||
impl AsChunk for &Path {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
fn name(&self) -> Option<String> {
|
||||
Some(format!("@{}", self.display()))
|
||||
}
|
||||
|
||||
@@ -102,7 +108,7 @@ impl AsChunk for &Path {
|
||||
}
|
||||
|
||||
impl AsChunk for PathBuf {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
fn name(&self) -> Option<String> {
|
||||
Some(format!("@{}", self.display()))
|
||||
}
|
||||
|
||||
@@ -112,7 +118,7 @@ impl AsChunk for PathBuf {
|
||||
}
|
||||
|
||||
impl<C: AsChunk + ?Sized> AsChunk for Box<C> {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
fn name(&self) -> Option<String> {
|
||||
(**self).name()
|
||||
}
|
||||
|
||||
@@ -136,7 +142,7 @@ impl<C: AsChunk + ?Sized> AsChunk for Box<C> {
|
||||
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
|
||||
pub struct Chunk<'a> {
|
||||
pub(crate) lua: WeakLua,
|
||||
pub(crate) name: StdString,
|
||||
pub(crate) name: String,
|
||||
pub(crate) env: Result<Option<Table>>,
|
||||
pub(crate) mode: Option<ChunkMode>,
|
||||
pub(crate) source: IoResult<Cow<'a, [u8]>>,
|
||||
@@ -154,13 +160,14 @@ pub enum ChunkMode {
|
||||
/// Represents a constant value that can be used by Luau compiler.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum CompileConstant {
|
||||
Nil,
|
||||
Boolean(bool),
|
||||
Number(crate::Number),
|
||||
Vector(crate::Vector),
|
||||
String(StdString),
|
||||
String(String),
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -192,7 +199,7 @@ impl From<&str> for CompileConstant {
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
type LibraryMemberConstantMap = HashMap<(StdString, StdString), CompileConstant>;
|
||||
type LibraryMemberConstantMap = HashMap<(String, String), CompileConstant>;
|
||||
|
||||
/// Luau compiler
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -203,14 +210,14 @@ pub struct Compiler {
|
||||
debug_level: u8,
|
||||
type_info_level: u8,
|
||||
coverage_level: u8,
|
||||
vector_lib: Option<StdString>,
|
||||
vector_ctor: Option<StdString>,
|
||||
vector_type: Option<StdString>,
|
||||
mutable_globals: Vec<StdString>,
|
||||
userdata_types: Vec<StdString>,
|
||||
libraries_with_known_members: Vec<StdString>,
|
||||
vector_lib: Option<String>,
|
||||
vector_ctor: Option<String>,
|
||||
vector_type: Option<String>,
|
||||
mutable_globals: Vec<String>,
|
||||
userdata_types: Vec<String>,
|
||||
libraries_with_known_members: Vec<String>,
|
||||
library_constants: Option<LibraryMemberConstantMap>,
|
||||
disabled_builtins: Vec<StdString>,
|
||||
disabled_builtins: Vec<String>,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -294,7 +301,7 @@ impl Compiler {
|
||||
/// To set the library and method name, use the `lib.ctor` format.
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn set_vector_ctor(mut self, ctor: impl Into<StdString>) -> Self {
|
||||
pub fn set_vector_ctor(mut self, ctor: impl Into<String>) -> Self {
|
||||
let ctor = ctor.into();
|
||||
let lib_ctor = ctor.split_once('.');
|
||||
self.vector_lib = lib_ctor.as_ref().map(|&(lib, _)| lib.to_owned());
|
||||
@@ -307,7 +314,7 @@ impl Compiler {
|
||||
/// Sets alternative vector type name for type tables, in addition to default type `vector`.
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn set_vector_type(mut self, r#type: impl Into<StdString>) -> Self {
|
||||
pub fn set_vector_type(mut self, r#type: impl Into<String>) -> Self {
|
||||
self.vector_type = Some(r#type.into());
|
||||
self
|
||||
}
|
||||
@@ -316,7 +323,7 @@ impl Compiler {
|
||||
///
|
||||
/// It disables the import optimization for fields accessed through it.
|
||||
#[must_use]
|
||||
pub fn add_mutable_global(mut self, global: impl Into<StdString>) -> Self {
|
||||
pub fn add_mutable_global(mut self, global: impl Into<String>) -> Self {
|
||||
self.mutable_globals.push(global.into());
|
||||
self
|
||||
}
|
||||
@@ -325,21 +332,21 @@ impl Compiler {
|
||||
///
|
||||
/// It disables the import optimization for fields accessed through these.
|
||||
#[must_use]
|
||||
pub fn set_mutable_globals<S: Into<StdString>>(mut self, globals: impl IntoIterator<Item = S>) -> Self {
|
||||
pub fn set_mutable_globals<S: Into<String>>(mut self, globals: impl IntoIterator<Item = S>) -> Self {
|
||||
self.mutable_globals = globals.into_iter().map(|s| s.into()).collect();
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a userdata type to the list that will be included in the type information.
|
||||
#[must_use]
|
||||
pub fn add_userdata_type(mut self, r#type: impl Into<StdString>) -> Self {
|
||||
pub fn add_userdata_type(mut self, r#type: impl Into<String>) -> Self {
|
||||
self.userdata_types.push(r#type.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a list of userdata types that will be included in the type information.
|
||||
#[must_use]
|
||||
pub fn set_userdata_types<S: Into<StdString>>(mut self, types: impl IntoIterator<Item = S>) -> Self {
|
||||
pub fn set_userdata_types<S: Into<String>>(mut self, types: impl IntoIterator<Item = S>) -> Self {
|
||||
self.userdata_types = types.into_iter().map(|s| s.into()).collect();
|
||||
self
|
||||
}
|
||||
@@ -366,24 +373,21 @@ impl Compiler {
|
||||
self.libraries_with_known_members.push(lib.clone());
|
||||
}
|
||||
self.library_constants
|
||||
.get_or_insert_with(HashMap::new)
|
||||
.get_or_insert_default()
|
||||
.insert((lib, member), r#const.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a builtin that should be disabled.
|
||||
#[must_use]
|
||||
pub fn add_disabled_builtin(mut self, builtin: impl Into<StdString>) -> Self {
|
||||
pub fn add_disabled_builtin(mut self, builtin: impl Into<String>) -> Self {
|
||||
self.disabled_builtins.push(builtin.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a list of builtins that should be disabled.
|
||||
#[must_use]
|
||||
pub fn set_disabled_builtins<S: Into<StdString>>(
|
||||
mut self,
|
||||
builtins: impl IntoIterator<Item = S>,
|
||||
) -> Self {
|
||||
pub fn set_disabled_builtins<S: Into<String>>(mut self, builtins: impl IntoIterator<Item = S>) -> Self {
|
||||
self.disabled_builtins = builtins.into_iter().map(|s| s.into()).collect();
|
||||
self
|
||||
}
|
||||
@@ -477,11 +481,11 @@ impl Compiler {
|
||||
options.mutableGlobals = mutable_globals_ptr;
|
||||
options.userdataTypes = userdata_types_ptr;
|
||||
options.librariesWithKnownMembers = libraries_with_known_members_ptr;
|
||||
if let Some(map) = self.library_constants.as_ref() {
|
||||
if !self.libraries_with_known_members.is_empty() {
|
||||
LIBRARY_MEMBER_CONSTANT_MAP.with_borrow_mut(|gmap| *gmap = map.clone());
|
||||
options.libraryMemberConstantCallback = Some(library_member_constant_callback);
|
||||
}
|
||||
if let Some(map) = self.library_constants.as_ref()
|
||||
&& !self.libraries_with_known_members.is_empty()
|
||||
{
|
||||
LIBRARY_MEMBER_CONSTANT_MAP.with_borrow_mut(|gmap| *gmap = map.clone());
|
||||
options.libraryMemberConstantCallback = Some(library_member_constant_callback);
|
||||
}
|
||||
options.disabledBuiltins = disabled_builtins_ptr;
|
||||
ffi::luau_compile(source.as_ref(), options)
|
||||
@@ -490,7 +494,7 @@ impl Compiler {
|
||||
if bytecode.first() == Some(&0) {
|
||||
// The rest of the bytecode is the error message starting with `:`
|
||||
// See https://github.com/luau-lang/luau/blob/0.640/Compiler/src/Compiler.cpp#L4336
|
||||
let message = StdString::from_utf8_lossy(&bytecode[2..]).into_owned();
|
||||
let message = String::from_utf8_lossy(&bytecode[2..]).into_owned();
|
||||
return Err(Error::SyntaxError {
|
||||
incomplete_input: message.ends_with("<eof>"),
|
||||
message,
|
||||
@@ -513,7 +517,7 @@ impl Chunk<'_> {
|
||||
/// - `@` - file path (when truncation is needed, the end of the file path is kept, as this is
|
||||
/// more useful for identifying the file)
|
||||
/// - `=` - custom chunk name (when truncation is needed, the beginning of the name is kept)
|
||||
pub fn set_name(mut self, name: impl Into<StdString>) -> Self {
|
||||
pub fn set_name(mut self, name: impl Into<String>) -> Self {
|
||||
self.name = name.into();
|
||||
self
|
||||
}
|
||||
@@ -662,19 +666,19 @@ impl Chunk<'_> {
|
||||
///
|
||||
/// It does nothing if the chunk is already binary or invalid.
|
||||
fn compile(&mut self) {
|
||||
if let Ok(ref source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Text {
|
||||
#[cfg(feature = "luau")]
|
||||
if let Ok(data) = self.compiler.get_or_insert_with(Default::default).compile(source) {
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
}
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if let Ok(func) = self.lua.lock().load_chunk(None, None, None, source.as_ref()) {
|
||||
let data = func.dump(false);
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
}
|
||||
if let Ok(ref source) = self.source
|
||||
&& self.detect_mode() == ChunkMode::Text
|
||||
{
|
||||
#[cfg(feature = "luau")]
|
||||
if let Ok(data) = self.compiler.get_or_insert_default().compile(source) {
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
}
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if let Ok(func) = self.lua.lock().load_chunk(None, None, None, source.as_ref()) {
|
||||
let data = func.dump(false);
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -687,33 +691,33 @@ impl Chunk<'_> {
|
||||
|
||||
// Try to fetch compiled chunk from cache
|
||||
let mut text_source = None;
|
||||
if let Ok(ref source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Text {
|
||||
let lua = self.lua.lock();
|
||||
if let Some(cache) = lua.priv_app_data_ref::<ChunksCache>() {
|
||||
if let Some(data) = cache.0.get(source.as_ref()) {
|
||||
self.source = Ok(Cow::Owned(data.clone()));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
return self;
|
||||
}
|
||||
}
|
||||
text_source = Some(source.as_ref().to_vec());
|
||||
if let Ok(ref source) = self.source
|
||||
&& self.detect_mode() == ChunkMode::Text
|
||||
{
|
||||
let lua = self.lua.lock();
|
||||
if let Some(cache) = lua.priv_app_data_ref::<ChunksCache>()
|
||||
&& let Some(data) = cache.0.get(source.as_ref())
|
||||
{
|
||||
self.source = Ok(Cow::Owned(data.clone()));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
return self;
|
||||
}
|
||||
text_source = Some(source.as_ref().to_vec());
|
||||
}
|
||||
|
||||
// Compile and cache the chunk
|
||||
if let Some(text_source) = text_source {
|
||||
self.compile();
|
||||
if let Ok(ref binary_source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Binary {
|
||||
let lua = self.lua.lock();
|
||||
if let Some(mut cache) = lua.priv_app_data_mut::<ChunksCache>() {
|
||||
cache.0.insert(text_source, binary_source.to_vec());
|
||||
} else {
|
||||
let mut cache = ChunksCache(HashMap::new());
|
||||
cache.0.insert(text_source, binary_source.to_vec());
|
||||
lua.set_priv_app_data(cache);
|
||||
};
|
||||
if let Ok(ref binary_source) = self.source
|
||||
&& self.detect_mode() == ChunkMode::Binary
|
||||
{
|
||||
let lua = self.lua.lock();
|
||||
if let Some(mut cache) = lua.priv_app_data_mut::<ChunksCache>() {
|
||||
cache.0.insert(text_source, binary_source.to_vec());
|
||||
} else {
|
||||
let mut cache = ChunksCache(HashMap::new());
|
||||
cache.0.insert(text_source, binary_source.to_vec());
|
||||
lua.set_priv_app_data(cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -761,7 +765,7 @@ impl Chunk<'_> {
|
||||
ChunkMode::Text
|
||||
}
|
||||
|
||||
fn convert_name(name: StdString) -> Result<CString> {
|
||||
fn convert_name(name: String) -> Result<CString> {
|
||||
CString::new(name).map_err(|err| Error::runtime(format!("invalid name: {err}")))
|
||||
}
|
||||
|
||||
@@ -783,7 +787,6 @@ impl Chunk<'_> {
|
||||
///
|
||||
/// The resulted `IntoLua` implementation will convert the chunk into a Lua function without
|
||||
/// executing it.
|
||||
#[doc(hidden)]
|
||||
#[track_caller]
|
||||
pub fn wrap(chunk: impl AsChunk) -> impl IntoLua {
|
||||
WrappedChunk {
|
||||
|
||||
+154
-231
@@ -4,20 +4,19 @@ use std::ffi::{CStr, CString, OsStr, OsString};
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
use std::os::raw::c_int;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::string::String as StdString;
|
||||
use std::{mem, slice, str};
|
||||
use std::{slice, str};
|
||||
|
||||
use bstr::{BStr, BString, ByteSlice, ByteVec};
|
||||
use bstr::{BStr, BString, ByteVec};
|
||||
use num_traits::cast;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::string::{BorrowedBytes, BorrowedStr, String};
|
||||
use crate::string::{BorrowedBytes, BorrowedStr, LuaString};
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::traits::{FromLua, IntoLua, ShortTypeName as _};
|
||||
use crate::types::{Either, LightUserData, MaybeSend, RegistryKey};
|
||||
use crate::types::{Either, LightUserData, MaybeSend, MaybeSync, RegistryKey};
|
||||
use crate::userdata::{AnyUserData, UserData};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
@@ -47,14 +46,14 @@ impl FromLua for Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for String {
|
||||
impl IntoLua for LuaString {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for &String {
|
||||
impl IntoLua for &LuaString {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.clone()))
|
||||
@@ -67,16 +66,12 @@ impl IntoLua for &String {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for String {
|
||||
impl FromLua for LuaString {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<String> {
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<LuaString> {
|
||||
let ty = value.type_name();
|
||||
lua.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "string".to_string(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
})
|
||||
.ok_or_else(|| Error::from_lua_conversion(ty, "string", "expected string or number".to_string()))
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
@@ -84,98 +79,86 @@ impl FromLua for String {
|
||||
let type_id = ffi::lua_type(state, idx);
|
||||
if type_id == ffi::LUA_TSTRING {
|
||||
ffi::lua_xpush(state, lua.ref_thread(), idx);
|
||||
return Ok(String(lua.pop_ref_thread()));
|
||||
return Ok(LuaString(lua.pop_ref_thread()));
|
||||
}
|
||||
// Fallback to default
|
||||
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for BorrowedStr<'_> {
|
||||
impl IntoLua for BorrowedStr {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.borrow.into_owned()))
|
||||
Ok(Value::String(LuaString(self.vref)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_ref(&self.borrow.0);
|
||||
lua.push_ref(&self.vref);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for &BorrowedStr<'_> {
|
||||
impl IntoLua for &BorrowedStr {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.borrow.clone().into_owned()))
|
||||
Ok(Value::String(LuaString(self.vref.clone())))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_ref(&self.borrow.0);
|
||||
lua.push_ref(&self.vref);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for BorrowedStr<'_> {
|
||||
impl FromLua for BorrowedStr {
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let s = String::from_lua(value, lua)?;
|
||||
let BorrowedStr { buf, _lua, .. } = BorrowedStr::try_from(&s)?;
|
||||
let buf = unsafe { mem::transmute::<&str, &'static str>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
let s = LuaString::from_lua(value, lua)?;
|
||||
BorrowedStr::try_from(&s)
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let s = String::from_stack(idx, lua)?;
|
||||
let BorrowedStr { buf, _lua, .. } = BorrowedStr::try_from(&s)?;
|
||||
let buf = unsafe { mem::transmute::<&str, &'static str>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
let s = LuaString::from_stack(idx, lua)?;
|
||||
BorrowedStr::try_from(&s)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for BorrowedBytes<'_> {
|
||||
impl IntoLua for BorrowedBytes {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.borrow.into_owned()))
|
||||
Ok(Value::String(LuaString(self.vref)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_ref(&self.borrow.0);
|
||||
lua.push_ref(&self.vref);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for &BorrowedBytes<'_> {
|
||||
impl IntoLua for &BorrowedBytes {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.borrow.clone().into_owned()))
|
||||
Ok(Value::String(LuaString(self.vref.clone())))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_ref(&self.borrow.0);
|
||||
lua.push_ref(&self.vref);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for BorrowedBytes<'_> {
|
||||
impl FromLua for BorrowedBytes {
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let s = String::from_lua(value, lua)?;
|
||||
let BorrowedBytes { buf, _lua, .. } = BorrowedBytes::from(&s);
|
||||
let buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
let s = LuaString::from_lua(value, lua)?;
|
||||
Ok(BorrowedBytes::from(&s))
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let s = String::from_stack(idx, lua)?;
|
||||
let BorrowedBytes { buf, _lua, .. } = BorrowedBytes::from(&s);
|
||||
let buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
let s = LuaString::from_stack(idx, lua)?;
|
||||
Ok(BorrowedBytes::from(&s))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,11 +187,7 @@ impl FromLua for Table {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Table> {
|
||||
match value {
|
||||
Value::Table(table) => Ok(table),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "table".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(value.type_name(), "table", None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,11 +217,7 @@ impl FromLua for Function {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Function> {
|
||||
match value {
|
||||
Value::Function(table) => Ok(table),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "function".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(value.type_name(), "function", None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,11 +247,7 @@ impl FromLua for Thread {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Thread> {
|
||||
match value {
|
||||
Value::Thread(t) => Ok(t),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "thread".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(value.type_name(), "thread", None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,16 +277,12 @@ impl FromLua for AnyUserData {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<AnyUserData> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(ud),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "userdata".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(value.type_name(), "userdata", None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UserData + MaybeSend + 'static> IntoLua for T {
|
||||
impl<T: UserData + MaybeSend + MaybeSync + 'static> IntoLua for T {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
Ok(Value::UserData(lua.create_userdata(self)?))
|
||||
@@ -428,11 +395,11 @@ impl FromLua for LightUserData {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::LightUserData(ud) => Ok(ud),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "lightuserdata".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
"lightuserdata",
|
||||
None,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -451,11 +418,7 @@ impl FromLua for crate::Vector {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::Vector(v) => Ok(v),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "vector".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(value.type_name(), "vector", None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -488,16 +451,12 @@ impl FromLua for crate::Buffer {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::Buffer(buf) => Ok(buf),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "buffer".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(value.type_name(), "buffer", None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for StdString {
|
||||
impl IntoLua for String {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
#[cfg(feature = "lua55")]
|
||||
@@ -519,16 +478,14 @@ impl IntoLua for StdString {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for StdString {
|
||||
impl FromLua for String {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
Ok(lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
.ok_or_else(|| {
|
||||
Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
|
||||
})?
|
||||
.to_str()?
|
||||
.to_owned())
|
||||
@@ -544,11 +501,7 @@ impl FromLua for StdString {
|
||||
let bytes = slice::from_raw_parts(data as *const u8, size);
|
||||
return str::from_utf8(bytes)
|
||||
.map(|s| s.to_owned())
|
||||
.map_err(|e| Error::FromLuaConversionError {
|
||||
from: "string",
|
||||
to: Self::type_name(),
|
||||
message: Some(e.to_string()),
|
||||
});
|
||||
.map_err(|e| Error::from_lua_conversion("string", Self::type_name(), e.to_string()));
|
||||
}
|
||||
// Fallback to default
|
||||
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
|
||||
@@ -570,7 +523,10 @@ impl IntoLua for &str {
|
||||
impl IntoLua for Cow<'_, str> {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
||||
match self {
|
||||
Cow::Borrowed(s) => s.into_lua(lua),
|
||||
Cow::Owned(s) => s.into_lua(lua),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,10 +543,8 @@ impl FromLua for Box<str> {
|
||||
let ty = value.type_name();
|
||||
Ok(lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
.ok_or_else(|| {
|
||||
Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
|
||||
})?
|
||||
.to_str()?
|
||||
.to_owned()
|
||||
@@ -614,21 +568,12 @@ impl FromLua for CString {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
let string = lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
})?;
|
||||
|
||||
let string = lua.coerce_string(value)?.ok_or_else(|| {
|
||||
Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
|
||||
})?;
|
||||
match CStr::from_bytes_with_nul(&string.as_bytes_with_nul()) {
|
||||
Ok(s) => Ok(s.into()),
|
||||
Err(_) => Err(Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("invalid C-style string".to_string()),
|
||||
}),
|
||||
Err(err) => Err(Error::from_lua_conversion(ty, Self::type_name(), err.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -643,7 +588,10 @@ impl IntoLua for &CStr {
|
||||
impl IntoLua for Cow<'_, CStr> {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(lua.create_string(self.to_bytes())?))
|
||||
match self {
|
||||
Cow::Borrowed(s) => s.into_lua(lua),
|
||||
Cow::Owned(s) => s.into_lua(lua),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -668,10 +616,8 @@ impl FromLua for BString {
|
||||
Value::Buffer(buf) => Ok(buf.to_vec().into()),
|
||||
_ => Ok((*lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
.ok_or_else(|| {
|
||||
Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
|
||||
})?
|
||||
.as_bytes())
|
||||
.into()),
|
||||
@@ -722,23 +668,22 @@ impl FromLua for OsString {
|
||||
let bs = BString::from_lua(value, lua)?;
|
||||
Vec::from(bs)
|
||||
.into_os_string()
|
||||
.map_err(|err| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "OsString".into(),
|
||||
message: Some(err.to_string()),
|
||||
})
|
||||
.map_err(|err| Error::from_lua_conversion(ty, "OsString", err.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for &OsStr {
|
||||
#[cfg(unix)]
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
let s = <[u8]>::from_os_str(self).ok_or_else(|| Error::ToLuaConversionError {
|
||||
from: "OsStr".into(),
|
||||
to: "string",
|
||||
message: Some("invalid utf-8 encoding".into()),
|
||||
})?;
|
||||
Ok(Value::String(lua.create_string(s)?))
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
self.display().to_string().into_lua(lua)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -776,34 +721,25 @@ impl FromLua for char {
|
||||
fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
match value {
|
||||
Value::Integer(i) => {
|
||||
cast(i)
|
||||
.and_then(char::from_u32)
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "char".to_string(),
|
||||
message: Some("integer out of range when converting to char".to_string()),
|
||||
})
|
||||
}
|
||||
Value::Integer(i) => cast(i).and_then(char::from_u32).ok_or_else(|| {
|
||||
let msg = "integer out of range when converting to char";
|
||||
Error::from_lua_conversion(ty, "char", msg.to_string())
|
||||
}),
|
||||
Value::String(s) => {
|
||||
let str = s.to_str()?;
|
||||
let mut str_iter = str.chars();
|
||||
match (str_iter.next(), str_iter.next()) {
|
||||
(Some(char), None) => Ok(char),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "char".to_string(),
|
||||
message: Some(
|
||||
"expected string to have exactly one char when converting to char".to_string(),
|
||||
),
|
||||
}),
|
||||
_ => {
|
||||
let msg = "expected string to have exactly one char when converting to char";
|
||||
Err(Error::from_lua_conversion(ty, "char", msg.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or integer".to_string()),
|
||||
}),
|
||||
_ => {
|
||||
let msg = "expected string or integer";
|
||||
Err(Error::from_lua_conversion(ty, Self::type_name(), msg.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -854,24 +790,14 @@ macro_rules! lua_convert_int {
|
||||
if let Some(i) = lua.coerce_integer(value.clone())? {
|
||||
cast(i)
|
||||
} else {
|
||||
cast(
|
||||
lua.coerce_number(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: stringify!($x).to_string(),
|
||||
message: Some(
|
||||
"expected number or string coercible to number".to_string(),
|
||||
),
|
||||
})?,
|
||||
)
|
||||
cast(lua.coerce_number(value)?.ok_or_else(|| {
|
||||
let msg = "expected number or string coercible to number";
|
||||
Error::from_lua_conversion(ty, stringify!($x), msg.to_string())
|
||||
})?)
|
||||
}
|
||||
}
|
||||
})
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: stringify!($x).to_string(),
|
||||
message: Some("out of range".to_owned()),
|
||||
})
|
||||
.ok_or_else(|| Error::from_lua_conversion(ty, stringify!($x), "out of range".to_string()))
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
@@ -881,13 +807,18 @@ macro_rules! lua_convert_int {
|
||||
let mut ok = 0;
|
||||
let i = ffi::lua_tointegerx(state, idx, &mut ok);
|
||||
if ok != 0 {
|
||||
return cast(i).ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: "integer",
|
||||
to: stringify!($x).to_string(),
|
||||
message: Some("out of range".to_owned()),
|
||||
return cast(i).ok_or_else(|| {
|
||||
Error::from_lua_conversion("integer", stringify!($x), "out of range".to_string())
|
||||
});
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
if type_id == ffi::LUA_TINTEGER {
|
||||
let i = ffi::lua_tointeger64(state, idx, std::ptr::null_mut());
|
||||
return cast(i).ok_or_else(|| {
|
||||
Error::from_lua_conversion("integer", stringify!($x), "out of range".to_string())
|
||||
});
|
||||
}
|
||||
// Fallback to default
|
||||
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
|
||||
}
|
||||
@@ -921,13 +852,10 @@ macro_rules! lua_convert_float {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
lua.coerce_number(value)?
|
||||
.map(|n| n as $x)
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: stringify!($x).to_string(),
|
||||
message: Some("expected number or string coercible to number".to_string()),
|
||||
})
|
||||
lua.coerce_number(value)?.map(|n| n as $x).ok_or_else(|| {
|
||||
let msg = "expected number or string coercible to number";
|
||||
Error::from_lua_conversion(ty, stringify!($x), msg.to_string())
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
@@ -987,18 +915,16 @@ where
|
||||
},
|
||||
Value::Table(table) => {
|
||||
let vec = table.sequence_values().collect::<Result<Vec<_>>>()?;
|
||||
vec.try_into()
|
||||
.map_err(|vec: Vec<T>| Error::FromLuaConversionError {
|
||||
from: "table",
|
||||
to: Self::type_name(),
|
||||
message: Some(format!("expected table of length {N}, got {}", vec.len())),
|
||||
})
|
||||
vec.try_into().map_err(|vec: Vec<T>| {
|
||||
let msg = format!("expected table of length {N}, got {}", vec.len());
|
||||
Error::from_lua_conversion("table", Self::type_name(), msg)
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
let msg = format!("expected table of length {N}");
|
||||
let err = Error::from_lua_conversion(value.type_name(), Self::type_name(), msg.to_string());
|
||||
Err(err)
|
||||
}
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1029,11 +955,11 @@ impl<T: FromLua> FromLua for Vec<T> {
|
||||
fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::Table(table) => table.sequence_values().collect(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
Self::type_name(),
|
||||
"expected table".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1048,14 +974,13 @@ impl<K: Eq + Hash + IntoLua, V: IntoLua, S: BuildHasher> IntoLua for HashMap<K,
|
||||
impl<K: Eq + Hash + FromLua, V: FromLua, S: BuildHasher + Default> FromLua for HashMap<K, V, S> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table.pairs().collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
match value {
|
||||
Value::Table(table) => table.pairs().collect(),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
Self::type_name(),
|
||||
"expected table".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1070,14 +995,13 @@ impl<K: Ord + IntoLua, V: IntoLua> IntoLua for BTreeMap<K, V> {
|
||||
impl<K: Ord + FromLua, V: FromLua> FromLua for BTreeMap<K, V> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table.pairs().collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
match value {
|
||||
Value::Table(table) => table.pairs().collect(),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
Self::type_name(),
|
||||
"expected table".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1097,11 +1021,11 @@ impl<T: Eq + Hash + FromLua, S: BuildHasher + Default> FromLua for HashSet<T, S>
|
||||
match value {
|
||||
Value::Table(table) if table.raw_len() > 0 => table.sequence_values().collect(),
|
||||
Value::Table(table) => table.pairs::<T, Value>().map(|res| res.map(|(k, _)| k)).collect(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
Self::type_name(),
|
||||
"expected table".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1121,11 +1045,11 @@ impl<T: Ord + FromLua> FromLua for BTreeSet<T> {
|
||||
match value {
|
||||
Value::Table(table) if table.raw_len() > 0 => table.sequence_values().collect(),
|
||||
Value::Table(table) => table.pairs::<T, Value>().map(|res| res.map(|(k, _)| k)).collect(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
Self::type_name(),
|
||||
"expected table".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1195,11 +1119,11 @@ impl<L: FromLua, R: FromLua> FromLua for Either<L, R> {
|
||||
// Try the right type
|
||||
Err(_) => match R::from_lua(value, lua).map(Either::Right) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(_) => Err(Error::FromLuaConversionError {
|
||||
from: value_type_name,
|
||||
to: Self::type_name(),
|
||||
message: None,
|
||||
}),
|
||||
Err(_) => Err(Error::from_lua_conversion(
|
||||
value_type_name,
|
||||
Self::type_name(),
|
||||
None,
|
||||
)),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1211,13 +1135,12 @@ impl<L: FromLua, R: FromLua> FromLua for Either<L, R> {
|
||||
Err(_) => match R::from_stack(idx, lua).map(Either::Right) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(_) => {
|
||||
let value_type_name =
|
||||
CStr::from_ptr(ffi::lua_typename(lua.state(), ffi::lua_type(lua.state(), idx)));
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value_type_name.to_str().unwrap(),
|
||||
to: Self::type_name(),
|
||||
message: None,
|
||||
})
|
||||
let state = lua.state();
|
||||
let from_type_name = CStr::from_ptr(ffi::lua_typename(state, ffi::lua_type(state, idx)))
|
||||
.to_str()
|
||||
.unwrap_or("unknown");
|
||||
let err = Error::from_lua_conversion(from_type_name, Self::type_name(), None);
|
||||
Err(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
+32
-49
@@ -1,3 +1,9 @@
|
||||
//! Lua debugging interface.
|
||||
//!
|
||||
//! This module provides access to the Lua debug interface, allowing inspection of the call stack,
|
||||
//! and function information. The main types are [`struct@Debug`] for accessing debug information
|
||||
//! and [`HookTriggers`] for configuring debug hooks.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
@@ -5,7 +11,7 @@ use ffi::{lua_Debug, lua_State};
|
||||
|
||||
use crate::function::Function;
|
||||
use crate::state::RawLua;
|
||||
use crate::util::{assert_stack, linenumber_to_usize, ptr_to_lossy_str, ptr_to_str, StackGuard};
|
||||
use crate::util::{StackGuard, assert_stack, linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
|
||||
|
||||
/// Contains information about currently executing Lua code.
|
||||
///
|
||||
@@ -133,12 +139,6 @@ impl<'a> Debug<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[deprecated(note = "Use `current_line` instead")]
|
||||
pub fn curr_line(&self) -> i32 {
|
||||
self.current_line().map(|n| n as i32).unwrap_or(-1)
|
||||
}
|
||||
|
||||
/// Corresponds to the `l` "what" mask. Returns the current line.
|
||||
pub fn current_line(&self) -> Option<usize> {
|
||||
unsafe {
|
||||
@@ -190,15 +190,15 @@ impl<'a> Debug<'a> {
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let stack = DebugStack {
|
||||
num_ups: (*self.ar).nups as _,
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
num_upvalues: (*self.ar).nups as _,
|
||||
#[cfg(not(any(feature = "lua51", feature = "luajit")))]
|
||||
num_params: (*self.ar).nparams as _,
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[cfg(not(any(feature = "lua51", feature = "luajit")))]
|
||||
is_vararg: (*self.ar).isvararg != 0,
|
||||
};
|
||||
#[cfg(feature = "luau")]
|
||||
let stack = DebugStack {
|
||||
num_ups: (*self.ar).nupvals,
|
||||
num_upvalues: (*self.ar).nupvals,
|
||||
num_params: (*self.ar).nparams,
|
||||
is_vararg: (*self.ar).isvararg != 0,
|
||||
};
|
||||
@@ -208,6 +208,8 @@ impl<'a> Debug<'a> {
|
||||
}
|
||||
|
||||
/// Represents a specific event that triggered the hook.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum DebugEvent {
|
||||
Call,
|
||||
@@ -218,6 +220,9 @@ pub enum DebugEvent {
|
||||
Unknown(c_int),
|
||||
}
|
||||
|
||||
/// Contains the name information of a function in the call stack.
|
||||
///
|
||||
/// Returned by the [`Debug::names`] method.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DebugNames<'a> {
|
||||
/// A (reasonable) name of the function (`None` if the name cannot be found).
|
||||
@@ -228,6 +233,9 @@ pub struct DebugNames<'a> {
|
||||
pub name_what: Option<&'static str>,
|
||||
}
|
||||
|
||||
/// Contains the source information of a function in the call stack.
|
||||
///
|
||||
/// Returned by the [`Debug::source`] method.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DebugSource<'a> {
|
||||
/// Source of the chunk that created the function.
|
||||
@@ -243,47 +251,20 @@ pub struct DebugSource<'a> {
|
||||
pub what: &'static str,
|
||||
}
|
||||
|
||||
/// Contains stack information about a function in the call stack.
|
||||
///
|
||||
/// Returned by the [`Debug::stack`] method.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct DebugStack {
|
||||
/// Number of upvalues.
|
||||
pub num_ups: u8,
|
||||
/// Number of parameters.
|
||||
#[cfg(any(
|
||||
feature = "lua55",
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luau"
|
||||
))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(
|
||||
feature = "lua55",
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luau"
|
||||
)))
|
||||
)]
|
||||
/// The number of upvalues of the function.
|
||||
pub num_upvalues: u8,
|
||||
/// The number of parameters of the function (always 0 for C).
|
||||
#[cfg(any(not(any(feature = "lua51", feature = "luajit")), doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(any(feature = "lua51", feature = "luajit")))))]
|
||||
pub num_params: u8,
|
||||
/// Whether the function is a vararg function.
|
||||
#[cfg(any(
|
||||
feature = "lua55",
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luau"
|
||||
))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(
|
||||
feature = "lua55",
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luau"
|
||||
)))
|
||||
)]
|
||||
/// Whether the function is a variadic function (always true for C).
|
||||
#[cfg(any(not(any(feature = "lua51", feature = "luajit")), doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(any(feature = "lua51", feature = "luajit")))))]
|
||||
pub is_vararg: bool,
|
||||
}
|
||||
|
||||
@@ -361,6 +342,7 @@ impl HookTriggers {
|
||||
}
|
||||
|
||||
// Compute the mask to pass to `lua_sethook`.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) const fn mask(&self) -> c_int {
|
||||
let mut mask: c_int = 0;
|
||||
if self.on_calls {
|
||||
@@ -380,6 +362,7 @@ impl HookTriggers {
|
||||
|
||||
// Returns the `count` parameter to pass to `lua_sethook`, if applicable. Otherwise, zero is
|
||||
// returned.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) const fn count(&self) -> c_int {
|
||||
match self.every_nth_instruction {
|
||||
Some(n) => n as c_int,
|
||||
|
||||
+43
-47
@@ -1,10 +1,14 @@
|
||||
//! Lua error handling.
|
||||
//!
|
||||
//! This module provides the [`Error`] type returned by all fallible `mlua` operations, together
|
||||
//! with extension traits for adapting Rust errors for use within Lua.
|
||||
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::io::Error as IoError;
|
||||
use std::net::AddrParseError;
|
||||
use std::result::Result as StdResult;
|
||||
use std::str::Utf8Error;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::private::Sealed;
|
||||
@@ -22,7 +26,7 @@ pub enum Error {
|
||||
/// Syntax error while parsing Lua source code.
|
||||
SyntaxError {
|
||||
/// The error message as returned by Lua.
|
||||
message: StdString,
|
||||
message: String,
|
||||
/// `true` if the error can likely be fixed by appending more input to the source code.
|
||||
///
|
||||
/// This is useful for implementing REPLs as they can query the user for more input if this
|
||||
@@ -34,20 +38,20 @@ pub enum Error {
|
||||
/// The Lua VM returns this error when a builtin operation is performed on incompatible types.
|
||||
/// Among other things, this includes invoking operators on wrong types (such as calling or
|
||||
/// indexing a `nil` value).
|
||||
RuntimeError(StdString),
|
||||
RuntimeError(String),
|
||||
/// Lua memory error, aka `LUA_ERRMEM`
|
||||
///
|
||||
/// The Lua VM returns this error when the allocator does not return the requested memory, aka
|
||||
/// it is an out-of-memory error.
|
||||
MemoryError(StdString),
|
||||
MemoryError(String),
|
||||
/// Lua garbage collector error, aka `LUA_ERRGCMM`.
|
||||
///
|
||||
/// The Lua VM returns this error when there is an error running a `__gc` metamethod.
|
||||
#[cfg(any(feature = "lua53", feature = "lua52", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua53", feature = "lua52"))))]
|
||||
GarbageCollectorError(StdString),
|
||||
GarbageCollectorError(String),
|
||||
/// Potentially unsafe action in safe mode.
|
||||
SafetyError(StdString),
|
||||
SafetyError(String),
|
||||
/// Memory control is not available.
|
||||
///
|
||||
/// This error can only happen when Lua state was not created by us and does not have the
|
||||
@@ -80,23 +84,14 @@ pub enum Error {
|
||||
/// (which is stored in the corresponding field).
|
||||
BadArgument {
|
||||
/// Function that was called.
|
||||
to: Option<StdString>,
|
||||
to: Option<String>,
|
||||
/// Argument position (usually starts from 1).
|
||||
pos: usize,
|
||||
/// Argument name.
|
||||
name: Option<StdString>,
|
||||
name: Option<String>,
|
||||
/// Underlying error returned when converting argument to a Lua value.
|
||||
cause: Arc<Error>,
|
||||
},
|
||||
/// A Rust value could not be converted to a Lua value.
|
||||
ToLuaConversionError {
|
||||
/// Name of the Rust type that could not be converted.
|
||||
from: String,
|
||||
/// Name of the Lua type that could not be created.
|
||||
to: &'static str,
|
||||
/// A message indicating why the conversion failed in more detail.
|
||||
message: Option<StdString>,
|
||||
},
|
||||
/// A Lua value could not be converted to the expected Rust type.
|
||||
FromLuaConversionError {
|
||||
/// Name of the Lua type that could not be converted.
|
||||
@@ -104,7 +99,7 @@ pub enum Error {
|
||||
/// Name of the Rust type that could not be created.
|
||||
to: String,
|
||||
/// A string containing more detailed error information.
|
||||
message: Option<StdString>,
|
||||
message: Option<String>,
|
||||
},
|
||||
/// [`Thread::resume`] was called on an unresumable coroutine.
|
||||
///
|
||||
@@ -154,17 +149,17 @@ pub enum Error {
|
||||
/// A [`MetaMethod`] operation is restricted (typically for `__gc` or `__metatable`).
|
||||
///
|
||||
/// [`MetaMethod`]: crate::MetaMethod
|
||||
MetaMethodRestricted(StdString),
|
||||
MetaMethodRestricted(String),
|
||||
/// A [`MetaMethod`] (eg. `__index` or `__newindex`) has invalid type.
|
||||
///
|
||||
/// [`MetaMethod`]: crate::MetaMethod
|
||||
MetaMethodTypeError {
|
||||
/// Name of the metamethod.
|
||||
method: StdString,
|
||||
method: String,
|
||||
/// Passed value type.
|
||||
type_name: &'static str,
|
||||
/// A string containing more detailed error information.
|
||||
message: Option<StdString>,
|
||||
message: Option<String>,
|
||||
},
|
||||
/// A [`RegistryKey`] produced from a different Lua state was used.
|
||||
///
|
||||
@@ -173,7 +168,7 @@ pub enum Error {
|
||||
/// A Rust callback returned `Err`, raising the contained `Error` as a Lua error.
|
||||
CallbackError {
|
||||
/// Lua call stack backtrace.
|
||||
traceback: StdString,
|
||||
traceback: String,
|
||||
/// Original error returned by the Rust code.
|
||||
cause: Arc<Error>,
|
||||
},
|
||||
@@ -185,11 +180,11 @@ pub enum Error {
|
||||
/// Serialization error.
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
SerializeError(StdString),
|
||||
SerializeError(String),
|
||||
/// Deserialization error.
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
DeserializeError(StdString),
|
||||
DeserializeError(String),
|
||||
/// A custom error.
|
||||
///
|
||||
/// This can be used for returning user-defined errors from callbacks.
|
||||
@@ -201,7 +196,7 @@ pub enum Error {
|
||||
/// An error with additional context.
|
||||
WithContext {
|
||||
/// A string containing additional context.
|
||||
context: StdString,
|
||||
context: String,
|
||||
/// Underlying error.
|
||||
cause: Arc<Error>,
|
||||
},
|
||||
@@ -225,7 +220,7 @@ impl fmt::Display for Error {
|
||||
}
|
||||
Error::SafetyError(msg) => {
|
||||
write!(fmt, "safety error: {msg}")
|
||||
},
|
||||
}
|
||||
Error::MemoryControlNotAvailable => {
|
||||
write!(fmt, "memory control is not available")
|
||||
}
|
||||
@@ -238,10 +233,7 @@ impl fmt::Display for Error {
|
||||
fmt,
|
||||
"out of Lua stack, too many arguments to a Lua function or too many return values from a callback"
|
||||
),
|
||||
Error::BindError => write!(
|
||||
fmt,
|
||||
"too many arguments to Function::bind"
|
||||
),
|
||||
Error::BindError => write!(fmt, "too many arguments to Function::bind"),
|
||||
Error::BadArgument { to, pos, name, cause } => {
|
||||
if let Some(name) = name {
|
||||
write!(fmt, "bad argument `{name}`")?;
|
||||
@@ -252,13 +244,6 @@ impl fmt::Display for Error {
|
||||
write!(fmt, " to `{to}`")?;
|
||||
}
|
||||
write!(fmt, ": {cause}")
|
||||
},
|
||||
Error::ToLuaConversionError { from, to, message } => {
|
||||
write!(fmt, "error converting {from} to Lua {to}")?;
|
||||
match message {
|
||||
None => Ok(()),
|
||||
Some(message) => write!(fmt, " ({message})"),
|
||||
}
|
||||
}
|
||||
Error::FromLuaConversionError { from, to, message } => {
|
||||
write!(fmt, "error converting Lua {from} to {to}")?;
|
||||
@@ -273,7 +258,11 @@ impl fmt::Display for Error {
|
||||
Error::UserDataBorrowError => write!(fmt, "error borrowing userdata"),
|
||||
Error::UserDataBorrowMutError => write!(fmt, "error mutably borrowing userdata"),
|
||||
Error::MetaMethodRestricted(method) => write!(fmt, "metamethod {method} is restricted"),
|
||||
Error::MetaMethodTypeError { method, type_name, message } => {
|
||||
Error::MetaMethodTypeError {
|
||||
method,
|
||||
type_name,
|
||||
message,
|
||||
} => {
|
||||
write!(fmt, "metamethod {method} has unsupported type {type_name}")?;
|
||||
match message {
|
||||
None => Ok(()),
|
||||
@@ -286,7 +275,11 @@ impl fmt::Display for Error {
|
||||
Error::CallbackError { cause, traceback } => {
|
||||
// Trace errors down to the root
|
||||
let (mut cause, mut full_traceback) = (cause, None);
|
||||
while let Error::CallbackError { cause: cause2, traceback: traceback2 } = &**cause {
|
||||
while let Error::CallbackError {
|
||||
cause: cause2,
|
||||
traceback: traceback2,
|
||||
} = &**cause
|
||||
{
|
||||
cause = cause2;
|
||||
full_traceback = Some(traceback2);
|
||||
}
|
||||
@@ -297,7 +290,7 @@ impl fmt::Display for Error {
|
||||
// Try to find local traceback within the full traceback
|
||||
if let Some(pos) = full_traceback.find(traceback) {
|
||||
write!(fmt, "{}", &full_traceback[..pos])?;
|
||||
writeln!(fmt, ">{}", &full_traceback[pos..].trim_end())?;
|
||||
writeln!(fmt, ">{}", full_traceback[pos..].trim_end())?;
|
||||
} else {
|
||||
writeln!(fmt, "{}", full_traceback.trim_end())?;
|
||||
}
|
||||
@@ -312,11 +305,11 @@ impl fmt::Display for Error {
|
||||
#[cfg(feature = "serde")]
|
||||
Error::SerializeError(err) => {
|
||||
write!(fmt, "serialize error: {err}")
|
||||
},
|
||||
}
|
||||
#[cfg(feature = "serde")]
|
||||
Error::DeserializeError(err) => {
|
||||
write!(fmt, "deserialize error: {err}")
|
||||
},
|
||||
}
|
||||
Error::ExternalError(err) => err.fmt(fmt),
|
||||
Error::WithContext { context, cause } => {
|
||||
writeln!(fmt, "{context}")?;
|
||||
@@ -352,7 +345,11 @@ impl Error {
|
||||
/// Wraps an external error object.
|
||||
#[inline]
|
||||
pub fn external<T: Into<Box<DynStdError>>>(err: T) -> Self {
|
||||
Error::ExternalError(err.into().into())
|
||||
let boxed = err.into();
|
||||
match boxed.downcast::<Self>() {
|
||||
Ok(err) => *err,
|
||||
Err(boxed) => Error::ExternalError(boxed.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to downcast the external error object to a concrete type by reference.
|
||||
@@ -394,6 +391,7 @@ impl Error {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn from_lua_conversion(
|
||||
from: &'static str,
|
||||
to: impl ToString,
|
||||
@@ -561,10 +559,8 @@ impl<'a> Iterator for Chain<'a> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "error-send"))]
|
||||
static_assertions::assert_not_impl_any!(Error: Send, Sync);
|
||||
static_assertions::assert_not_impl_any!(super::Error: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(Error: Send, Sync);
|
||||
static_assertions::assert_impl_all!(super::Error: Send, Sync);
|
||||
}
|
||||
|
||||
+186
-15
@@ -1,24 +1,99 @@
|
||||
//! Lua function handling.
|
||||
//!
|
||||
//! This module provides types for working with Lua functions from Rust, including
|
||||
//! both Lua-defined functions and native Rust callbacks.
|
||||
//!
|
||||
//! # Calling Functions
|
||||
//!
|
||||
//! Use [`Function::call`] to invoke a Lua function synchronously:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Function, Lua, Result};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//!
|
||||
//! // Get a built-in function
|
||||
//! let print: Function = lua.globals().get("print")?;
|
||||
//! print.call::<()>("Hello from Rust!")?;
|
||||
//!
|
||||
//! // Call a function that returns values
|
||||
//! let tonumber: Function = lua.globals().get("tonumber")?;
|
||||
//! let n: i32 = tonumber.call("42")?;
|
||||
//! assert_eq!(n, 42);
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! For asynchronous execution, use `Function::call_async` (requires `async` feature):
|
||||
//!
|
||||
//! ```ignore
|
||||
//! let result: String = my_async_func.call_async(args).await?;
|
||||
//! ```
|
||||
//!
|
||||
//! # Creating Functions
|
||||
//!
|
||||
//! Functions can be created from Rust closures using [`Lua::create_function`]:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Lua, Result};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//!
|
||||
//! let greet = lua.create_function(|_, name: String| {
|
||||
//! Ok(format!("Hello, {}!", name))
|
||||
//! })?;
|
||||
//!
|
||||
//! lua.globals().set("greet", greet)?;
|
||||
//! let result: String = lua.load(r#"greet("World")"#).eval()?;
|
||||
//! assert_eq!(result, "Hello, World!");
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! For simpler cases, use [`Function::wrap`] or [`Function::wrap_raw`] to convert a Rust function
|
||||
//! directly:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Function, Lua, Result};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//!
|
||||
//! fn add(a: i32, b: i32) -> i32 { a + b }
|
||||
//!
|
||||
//! lua.globals().set("add", Function::wrap_raw(add))?;
|
||||
//! let sum: i32 = lua.load("add(2, 3)").eval()?;
|
||||
//! assert_eq!(sum, 5);
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Function Environments
|
||||
//!
|
||||
//! Lua functions have an associated environment table that determines how global
|
||||
//! variables are resolved. Use [`Function::environment`] and [`Function::set_environment`]
|
||||
//! to inspect or modify this environment.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::result::Result as StdResult;
|
||||
use std::{mem, ptr, slice};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::error::{Error, ExternalError, ExternalResult, Result};
|
||||
use crate::state::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut};
|
||||
use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::types::{Callback, LuaType, MaybeSend, ValueRef};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str, StackGuard,
|
||||
StackGuard, assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str,
|
||||
};
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::thread::AsyncThread,
|
||||
crate::traits::LuaNativeAsyncFn,
|
||||
crate::types::AsyncCallback,
|
||||
std::future::{self, Future},
|
||||
std::pin::{pin, Pin},
|
||||
std::pin::{Pin, pin},
|
||||
std::task::{Context, Poll},
|
||||
};
|
||||
|
||||
@@ -165,7 +240,7 @@ impl Function {
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`AsyncThread`]: crate::AsyncThread
|
||||
/// [`AsyncThread`]: crate::thread::AsyncThread
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn call_async<R>(&self, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
@@ -555,30 +630,32 @@ impl Function {
|
||||
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`]
|
||||
/// trait.
|
||||
#[inline]
|
||||
pub fn wrap<F, A, R>(func: F) -> impl IntoLua
|
||||
pub fn wrap<F, A, R, E>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFn<A, Output = Result<R>> + MaybeSend + 'static,
|
||||
F: LuaNativeFn<A, Output = StdResult<R, E>> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
E: ExternalError,
|
||||
{
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func.call(args)?.push_into_stack_multi(lua)
|
||||
func.call(args).into_lua_err()?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
|
||||
pub fn wrap_mut<F, A, R>(func: F) -> impl IntoLua
|
||||
pub fn wrap_mut<F, A, R, E>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFnMut<A, Output = Result<R>> + MaybeSend + 'static,
|
||||
F: LuaNativeFnMut<A, Output = StdResult<R, E>> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
E: ExternalError,
|
||||
{
|
||||
let func = RefCell::new(func);
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func.call(args)?.push_into_stack_multi(lua)
|
||||
func.call(args).into_lua_err()?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -591,6 +668,7 @@ impl Function {
|
||||
pub fn wrap_raw<F, A>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFn<A> + MaybeSend + 'static,
|
||||
F::Output: IntoLuaMulti,
|
||||
A: FromLuaMulti,
|
||||
{
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
@@ -607,6 +685,7 @@ impl Function {
|
||||
pub fn wrap_raw_mut<F, A>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFnMut<A> + MaybeSend + 'static,
|
||||
F::Output: IntoLuaMulti,
|
||||
A: FromLuaMulti,
|
||||
{
|
||||
let func = RefCell::new(func);
|
||||
@@ -621,11 +700,12 @@ impl Function {
|
||||
/// trait.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn wrap_async<F, A, R>(func: F) -> impl IntoLua
|
||||
pub fn wrap_async<F, A, R, E>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeAsyncFn<A, Output = Result<R>> + MaybeSend + 'static,
|
||||
F: LuaNativeAsyncFn<A, Output = StdResult<R, E>> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
E: ExternalError,
|
||||
{
|
||||
WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
|
||||
let args = match A::from_stack_args(nargs, 1, None, rawlua) {
|
||||
@@ -634,7 +714,7 @@ impl Function {
|
||||
};
|
||||
let lua = rawlua.lua();
|
||||
let fut = func.call(args);
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
|
||||
Box::pin(async move { fut.await.into_lua_err()?.push_into_stack_multi(lua.raw_lua()) })
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -648,6 +728,7 @@ impl Function {
|
||||
pub fn wrap_raw_async<F, A>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeAsyncFn<A> + MaybeSend + 'static,
|
||||
F::Output: IntoLuaMulti,
|
||||
A: FromLuaMulti,
|
||||
{
|
||||
WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
|
||||
@@ -681,7 +762,9 @@ impl LuaType for Function {
|
||||
const TYPE_ID: c_int = ffi::LUA_TFUNCTION;
|
||||
}
|
||||
|
||||
/// Future for asynchronous function calls.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct AsyncCallFuture<R: FromLuaMulti>(Result<AsyncThread<R>>);
|
||||
|
||||
@@ -705,6 +788,94 @@ impl<R: FromLuaMulti> Future for AsyncCallFuture<R> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for types that can be used as Lua functions.
|
||||
pub trait LuaNativeFn<A: FromLuaMulti> {
|
||||
type Output;
|
||||
|
||||
fn call(&self, args: A) -> Self::Output;
|
||||
}
|
||||
|
||||
/// A trait for types with mutable state that can be used as Lua functions.
|
||||
pub trait LuaNativeFnMut<A: FromLuaMulti> {
|
||||
type Output;
|
||||
|
||||
fn call(&mut self, args: A) -> Self::Output;
|
||||
}
|
||||
|
||||
/// A trait for types that returns a future and can be used as Lua functions.
|
||||
#[cfg(feature = "async")]
|
||||
pub trait LuaNativeAsyncFn<A: FromLuaMulti> {
|
||||
type Output;
|
||||
|
||||
fn call(&self, args: A) -> impl Future<Output = Self::Output> + MaybeSend + 'static;
|
||||
}
|
||||
|
||||
macro_rules! impl_lua_native_fn {
|
||||
($($A:ident),*) => {
|
||||
impl<FN, $($A,)* R> LuaNativeFn<($($A,)*)> for FN
|
||||
where
|
||||
FN: Fn($($A,)*) -> R + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&self, args: ($($A,)*)) -> Self::Output {
|
||||
let ($($A,)*) = args;
|
||||
self($($A,)*)
|
||||
}
|
||||
}
|
||||
|
||||
impl<FN, $($A,)* R> LuaNativeFnMut<($($A,)*)> for FN
|
||||
where
|
||||
FN: FnMut($($A,)*) -> R + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&mut self, args: ($($A,)*)) -> Self::Output {
|
||||
let ($($A,)*) = args;
|
||||
self($($A,)*)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<FN, $($A,)* Fut, R> LuaNativeAsyncFn<($($A,)*)> for FN
|
||||
where
|
||||
FN: Fn($($A,)*) -> Fut + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
Fut: Future<Output = R> + MaybeSend + 'static,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&self, args: ($($A,)*)) -> impl Future<Output = Self::Output> + MaybeSend + 'static {
|
||||
let ($($A,)*) = args;
|
||||
self($($A,)*)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_lua_native_fn!();
|
||||
impl_lua_native_fn!(A);
|
||||
impl_lua_native_fn!(A, B);
|
||||
impl_lua_native_fn!(A, B, C);
|
||||
impl_lua_native_fn!(A, B, C, D);
|
||||
impl_lua_native_fn!(A, B, C, D, E);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
+72
-132
@@ -61,91 +61,108 @@
|
||||
//! [`Future`]: std::future::Future
|
||||
//! [`serde::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
|
||||
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||
//! [`AsyncThread`]: crate::thread::AsyncThread
|
||||
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(not(send), allow(clippy::arc_with_non_send_sync))]
|
||||
#![allow(clippy::ptr_eq)]
|
||||
#![allow(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
mod buffer;
|
||||
mod chunk;
|
||||
mod conversion;
|
||||
mod debug;
|
||||
mod error;
|
||||
mod function;
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
mod luau;
|
||||
mod memory;
|
||||
mod multi;
|
||||
mod scope;
|
||||
mod state;
|
||||
mod stdlib;
|
||||
mod string;
|
||||
mod table;
|
||||
mod thread;
|
||||
mod traits;
|
||||
mod types;
|
||||
mod userdata;
|
||||
mod util;
|
||||
mod value;
|
||||
mod vector;
|
||||
|
||||
pub mod chunk;
|
||||
pub mod debug;
|
||||
pub mod error;
|
||||
pub mod function;
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub mod luau;
|
||||
pub mod prelude;
|
||||
pub mod state;
|
||||
pub mod string;
|
||||
pub mod table;
|
||||
pub mod thread;
|
||||
pub mod userdata;
|
||||
|
||||
pub use bstr::BString;
|
||||
pub use ffi::{self, lua_CFunction, lua_State};
|
||||
#[cfg(feature = "macros")]
|
||||
#[doc(hidden)]
|
||||
pub use inventory as __inventory;
|
||||
|
||||
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
|
||||
pub use crate::debug::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
|
||||
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
|
||||
pub use crate::function::{Function, FunctionInfo};
|
||||
#[doc(inline)]
|
||||
pub use crate::error::{Error, Result};
|
||||
#[doc(inline)]
|
||||
pub use crate::function::Function;
|
||||
pub use crate::multi::{MultiValue, Variadic};
|
||||
pub use crate::scope::Scope;
|
||||
pub use crate::state::{GCMode, Lua, LuaOptions, WeakLua};
|
||||
#[doc(inline)]
|
||||
pub use crate::state::{Lua, LuaOptions, WeakLua};
|
||||
pub use crate::stdlib::StdLib;
|
||||
pub use crate::string::{BorrowedBytes, BorrowedStr, String};
|
||||
pub use crate::table::{Table, TablePairs, TableSequence};
|
||||
pub use crate::thread::{Thread, ThreadStatus};
|
||||
pub use crate::traits::{
|
||||
FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut, ObjectLike,
|
||||
};
|
||||
#[doc(inline)]
|
||||
pub use crate::string::{BorrowedBytes, BorrowedStr, LuaString};
|
||||
#[doc(inline)]
|
||||
pub use crate::table::Table;
|
||||
#[doc(inline)]
|
||||
pub use crate::thread::Thread;
|
||||
#[doc(inline)]
|
||||
pub use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
|
||||
pub use crate::types::{
|
||||
AppDataRef, AppDataRefMut, Either, Integer, LightUserData, MaybeSend, Number, RegistryKey, VmState,
|
||||
};
|
||||
pub use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods, UserDataRef,
|
||||
UserDataRefMut, UserDataRegistry,
|
||||
AppDataRef, AppDataRefMut, Either, Integer, LightUserData, MaybeSend, MaybeSync, Number, RegistryKey,
|
||||
VmState,
|
||||
};
|
||||
#[doc(inline)]
|
||||
pub use crate::userdata::AnyUserData;
|
||||
pub use crate::value::{Nil, Value};
|
||||
|
||||
// Re-export some types to keep backward compatibility and avoid breaking changes in the public API.
|
||||
#[doc(hidden)]
|
||||
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
|
||||
#[cfg(feature = "luau")]
|
||||
#[doc(hidden)]
|
||||
pub use crate::chunk::{CompileConstant, Compiler};
|
||||
#[doc(hidden)]
|
||||
pub use crate::error::{ErrorContext, ExternalError, ExternalResult};
|
||||
#[doc(hidden)]
|
||||
pub use crate::string::LuaString as String;
|
||||
#[doc(hidden)]
|
||||
pub use crate::table::{TablePairs, TableSequence};
|
||||
#[doc(hidden)]
|
||||
pub use crate::thread::{ThreadEvent, ThreadStatus, ThreadTriggers};
|
||||
#[doc(hidden)]
|
||||
pub use crate::userdata::{
|
||||
MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods, UserDataOwned, UserDataRef,
|
||||
UserDataRefMut, UserDataRegistry,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[doc(inline)]
|
||||
pub use crate::debug::HookTriggers;
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub use crate::{
|
||||
buffer::Buffer,
|
||||
chunk::{CompileConstant, Compiler},
|
||||
function::CoverageInfo,
|
||||
luau::{HeapDump, NavigateError, Require, TextRequirer},
|
||||
vector::Vector,
|
||||
};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub use crate::{thread::AsyncThread, traits::LuaNativeAsyncFn};
|
||||
pub use crate::{buffer::Buffer, vector::Vector};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[doc(hidden)]
|
||||
pub use crate::serde::{DeserializeOptions, SerializeOptions};
|
||||
#[cfg(feature = "serde")]
|
||||
#[doc(inline)]
|
||||
pub use crate::{
|
||||
serde::{de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt},
|
||||
value::SerializableValue,
|
||||
};
|
||||
pub use crate::{serde::LuaSerdeExt, value::SerializableValue};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
@@ -156,54 +173,7 @@ pub mod serde;
|
||||
#[macro_use]
|
||||
extern crate mlua_derive;
|
||||
|
||||
/// Create a type that implements [`AsChunk`] and can capture Rust variables.
|
||||
///
|
||||
/// This macro allows to write Lua code directly in Rust code.
|
||||
///
|
||||
/// Rust variables can be referenced from Lua using `$` prefix, as shown in the example below.
|
||||
/// User's Rust types needs to implement [`UserData`] or [`IntoLua`] traits.
|
||||
///
|
||||
/// Captured variables are **moved** into the chunk.
|
||||
///
|
||||
/// ```
|
||||
/// use mlua::{Lua, Result, chunk};
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let name = "Rustacean";
|
||||
/// lua.load(chunk! {
|
||||
/// print("hello, " .. $name)
|
||||
/// }).exec()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Syntax issues
|
||||
///
|
||||
/// Since the Rust tokenizer will tokenize Lua code, this imposes some restrictions.
|
||||
/// The main thing to remember is:
|
||||
///
|
||||
/// - Use double quoted strings (`""`) instead of single quoted strings (`''`).
|
||||
///
|
||||
/// (Single quoted strings only work if they contain a single character, since in Rust,
|
||||
/// `'a'` is a character literal).
|
||||
///
|
||||
/// - Using Lua comments `--` is not desirable in **stable** Rust and can have bad side effects.
|
||||
///
|
||||
/// This is because procedural macros have Line/Column information available only in
|
||||
/// **nightly** Rust. Instead, Lua chunks represented as a big single line of code in stable Rust.
|
||||
///
|
||||
/// As workaround, Rust comments `//` can be used.
|
||||
///
|
||||
/// Other minor limitations:
|
||||
///
|
||||
/// - Certain escape codes in string literals don't work. (Specifically: `\a`, `\b`, `\f`, `\v`,
|
||||
/// `\123` (octal escape codes), `\u`, and `\U`).
|
||||
///
|
||||
/// These are accepted: : `\\`, `\n`, `\t`, `\r`, `\xAB` (hex escape codes), and `\0`.
|
||||
///
|
||||
/// - The `//` (floor division) operator is unusable, as its start a comment.
|
||||
///
|
||||
/// Everything else should work.
|
||||
#[doc = include_str!("../docs/chunk.md")]
|
||||
#[cfg(feature = "macros")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
pub use mlua_derive::chunk;
|
||||
@@ -216,47 +186,17 @@ pub use mlua_derive::chunk;
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
pub use mlua_derive::FromLua;
|
||||
|
||||
/// Registers Lua module entrypoint.
|
||||
///
|
||||
/// You can register multiple entrypoints as required.
|
||||
///
|
||||
/// ```ignore
|
||||
/// use mlua::{Lua, Result, Table};
|
||||
///
|
||||
/// #[mlua::lua_module]
|
||||
/// fn my_module(lua: &Lua) -> Result<Table> {
|
||||
/// let exports = lua.create_table()?;
|
||||
/// exports.set("hello", "world")?;
|
||||
/// Ok(exports)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Internally in the code above the compiler defines C function `luaopen_my_module`.
|
||||
///
|
||||
/// You can also pass options to the attribute:
|
||||
///
|
||||
/// * name - name of the module, defaults to the name of the function
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[mlua::lua_module(name = "alt_module")]
|
||||
/// fn my_module(lua: &Lua) -> Result<Table> {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// * skip_memory_check - skip memory allocation checks for some operations.
|
||||
///
|
||||
/// In module mode, mlua runs in unknown environment and cannot say are there any memory
|
||||
/// limits or not. As result, some operations that require memory allocation runs in
|
||||
/// protected mode. Setting this attribute will improve performance of such operations
|
||||
/// with risk of having uncaught exceptions and memory leaks.
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[mlua::lua_module(skip_memory_check)]
|
||||
/// fn my_module(lua: &Lua) -> Result<Table> {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
#[doc = include_str!("../docs/UserData.md")]
|
||||
#[cfg(feature = "macros")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
pub use mlua_derive::UserData;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "macros")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
pub use mlua_derive::userdata_impl;
|
||||
|
||||
#[doc = include_str!("../docs/lua_module.md")]
|
||||
#[cfg(all(feature = "mlua_derive", any(feature = "module", doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
|
||||
pub use mlua_derive::lua_module;
|
||||
|
||||
+13
-13
@@ -79,10 +79,10 @@ impl HeapDump {
|
||||
let mut size_by_type = HashMap::new();
|
||||
let objects = self.data["objects"].as_object()?;
|
||||
for obj in objects.values() {
|
||||
if let Some(cat_id) = category_id {
|
||||
if obj["cat"].as_i64()? != cat_id {
|
||||
continue;
|
||||
}
|
||||
if let Some(cat_id) = category_id
|
||||
&& obj["cat"].as_i64()? != cat_id
|
||||
{
|
||||
continue;
|
||||
}
|
||||
update_size(&mut size_by_type, obj["type"].as_str()?, obj["size"].as_u64()?);
|
||||
}
|
||||
@@ -123,18 +123,18 @@ impl HeapDump {
|
||||
if obj["type"] != "userdata" {
|
||||
continue;
|
||||
}
|
||||
if let Some(cat_id) = category_id {
|
||||
if obj["cat"].as_i64()? != cat_id {
|
||||
continue;
|
||||
}
|
||||
if let Some(cat_id) = category_id
|
||||
&& obj["cat"].as_i64()? != cat_id
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine userdata type from metatable
|
||||
let mut ud_type = "unknown";
|
||||
if let Some(metatable_addr) = obj["metatable"].as_str() {
|
||||
if let Some(t) = get_key(objects, &objects[metatable_addr], "__type") {
|
||||
ud_type = t;
|
||||
}
|
||||
if let Some(metatable_addr) = obj["metatable"].as_str()
|
||||
&& let Some(t) = get_key(objects, &objects[metatable_addr], "__type")
|
||||
{
|
||||
ud_type = t;
|
||||
}
|
||||
update_size(&mut size_by_userdata, ud_type, obj["size"].as_u64()?);
|
||||
}
|
||||
@@ -155,7 +155,7 @@ impl HeapDump {
|
||||
|
||||
/// Updates the size mapping for a given key.
|
||||
fn update_size<K: Eq + Hash>(size_type: &mut HashMap<K, (usize, u64)>, key: K, size: u64) {
|
||||
let (ref mut count, ref mut total_size) = size_type.entry(key).or_insert((0, 0));
|
||||
let (count, total_size) = size_type.entry(key).or_insert((0, 0));
|
||||
*count += 1;
|
||||
*total_size += size;
|
||||
}
|
||||
|
||||
+10
-3
@@ -1,3 +1,10 @@
|
||||
//! Luau-specific extensions and types.
|
||||
//!
|
||||
//! This module provides Luau-specific functionality including custom [`require`] implementations,
|
||||
//! heap memory analysis, and Luau VM integration utilities.
|
||||
//!
|
||||
//! [`require`]: crate::Lua::create_require_function
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::c_int;
|
||||
use std::ptr;
|
||||
@@ -5,12 +12,12 @@ use std::ptr;
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{callback_error_ext, ExtraData, Lua};
|
||||
use crate::state::{ExtraData, Lua, callback_error_ext};
|
||||
use crate::traits::{FromLuaMulti, IntoLua};
|
||||
use crate::types::MaybeSend;
|
||||
|
||||
pub use heap_dump::HeapDump;
|
||||
pub use require::{NavigateError, Require, TextRequirer};
|
||||
pub use require::{FsRequirer, NavigateError, Require};
|
||||
|
||||
// Since Luau has some missing standard functions, we re-implement them here
|
||||
|
||||
@@ -86,7 +93,7 @@ impl Lua {
|
||||
}
|
||||
|
||||
// Enable default `require` implementation
|
||||
let require = self.create_require_function(require::TextRequirer::new())?;
|
||||
let require = self.create_require_function(FsRequirer::new())?;
|
||||
self.globals().raw_set("require", require)?;
|
||||
|
||||
Ok(())
|
||||
|
||||
+46
-5
@@ -8,12 +8,11 @@ use std::{fmt, mem, ptr};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{callback_error_ext, Lua};
|
||||
use crate::state::{Lua, callback_error_ext};
|
||||
use crate::table::Table;
|
||||
use crate::types::MaybeSend;
|
||||
|
||||
// TODO: Rename to FsRequirer
|
||||
pub use fs::TextRequirer;
|
||||
pub use fs::FsRequirer;
|
||||
|
||||
/// An error that can occur during navigation in the Luau `require-by-string` system.
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -67,6 +66,24 @@ pub trait Require {
|
||||
/// configuration file.
|
||||
fn jump_to_alias(&mut self, path: &str) -> StdResult<(), NavigateError>;
|
||||
|
||||
/// Provides an initial alias override opportunity prior to searching for
|
||||
/// configuration files.
|
||||
///
|
||||
/// If `Ok(())` is returned, alias resolution stops here and the internal state
|
||||
/// must point at the aliased location.
|
||||
fn to_alias_override(&mut self, _alias: &str) -> StdResult<(), NavigateError> {
|
||||
Err(NavigateError::NotFound)
|
||||
}
|
||||
|
||||
/// Provides a final opportunity to resolve an alias if it cannot be found in
|
||||
/// configuration files.
|
||||
///
|
||||
/// If `Ok(())` is returned, alias resolution stops here and the internal state
|
||||
/// must point at the aliased location.
|
||||
fn to_alias_fallback(&mut self, _alias: &str) -> StdResult<(), NavigateError> {
|
||||
Err(NavigateError::NotFound)
|
||||
}
|
||||
|
||||
// Navigate to parent directory
|
||||
fn to_parent(&mut self) -> StdResult<(), NavigateError>;
|
||||
|
||||
@@ -193,6 +210,30 @@ pub(super) unsafe extern "C-unwind" fn init_config(config: *mut ffi::luarequire_
|
||||
})
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn to_alias_override(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
alias_unprefixed: *const c_char,
|
||||
) -> ffi::luarequire_NavigateResult {
|
||||
let mut this = try_borrow_mut!(state, ctx);
|
||||
let alias = CStr::from_ptr(alias_unprefixed).to_string_lossy();
|
||||
callback_error_ext(state, ptr::null_mut(), true, move |_, _| {
|
||||
this.to_alias_override(&alias).into_nav_result()
|
||||
})
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn to_alias_fallback(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
alias_unprefixed: *const c_char,
|
||||
) -> ffi::luarequire_NavigateResult {
|
||||
let mut this = try_borrow_mut!(state, ctx);
|
||||
let alias = CStr::from_ptr(alias_unprefixed).to_string_lossy();
|
||||
callback_error_ext(state, ptr::null_mut(), true, move |_, _| {
|
||||
this.to_alias_fallback(&alias).into_nav_result()
|
||||
})
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn to_parent(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
@@ -299,8 +340,8 @@ pub(super) unsafe extern "C-unwind" fn init_config(config: *mut ffi::luarequire_
|
||||
(*config).is_require_allowed = is_require_allowed;
|
||||
(*config).reset = reset;
|
||||
(*config).jump_to_alias = jump_to_alias;
|
||||
(*config).to_alias_override = None;
|
||||
(*config).to_alias_fallback = None;
|
||||
(*config).to_alias_override = Some(to_alias_override);
|
||||
(*config).to_alias_fallback = Some(to_alias_fallback);
|
||||
(*config).to_parent = to_parent;
|
||||
(*config).to_child = to_child;
|
||||
(*config).is_module_present = is_module_present;
|
||||
|
||||
+10
-10
@@ -12,7 +12,7 @@ use super::{NavigateError, Require};
|
||||
|
||||
/// The standard implementation of Luau `require-by-string` navigation.
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TextRequirer {
|
||||
pub struct FsRequirer {
|
||||
/// An absolute path to the current Luau module (not mapped to a physical file)
|
||||
abs_path: PathBuf,
|
||||
/// A relative path to the current Luau module (not mapped to a physical file)
|
||||
@@ -22,7 +22,7 @@ pub struct TextRequirer {
|
||||
resolved_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl TextRequirer {
|
||||
impl FsRequirer {
|
||||
/// The prefix used for chunk names in the require system.
|
||||
/// Only chunk names starting with this prefix are allowed to be used in `require`.
|
||||
const CHUNK_PREFIX: &str = "@";
|
||||
@@ -36,16 +36,16 @@ impl TextRequirer {
|
||||
/// The filename for the Luau configuration file.
|
||||
const LUAU_CONFIG_FILENAME: &str = ".config.luau";
|
||||
|
||||
/// Creates a new `TextRequirer` instance.
|
||||
/// Creates a new `FsRequirer` instance.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn normalize_chunk_name(chunk_name: &str) -> &str {
|
||||
if let Some((path, line)) = chunk_name.rsplit_once(':') {
|
||||
if line.parse::<u32>().is_ok() {
|
||||
return path;
|
||||
}
|
||||
if let Some((path, line)) = chunk_name.rsplit_once(':')
|
||||
&& line.parse::<u32>().is_ok()
|
||||
{
|
||||
return path;
|
||||
}
|
||||
chunk_name
|
||||
}
|
||||
@@ -114,7 +114,7 @@ impl TextRequirer {
|
||||
}
|
||||
}
|
||||
|
||||
impl Require for TextRequirer {
|
||||
impl Require for FsRequirer {
|
||||
fn is_require_allowed(&self, chunk_name: &str) -> bool {
|
||||
chunk_name.starts_with(Self::CHUNK_PREFIX)
|
||||
}
|
||||
@@ -231,7 +231,7 @@ impl Require for TextRequirer {
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
|
||||
use super::TextRequirer;
|
||||
use super::FsRequirer;
|
||||
|
||||
#[test]
|
||||
fn test_path_normalize() {
|
||||
@@ -267,7 +267,7 @@ mod tests {
|
||||
// '..' disappears if path is absolute and component is non-erasable
|
||||
("/../", "/"),
|
||||
] {
|
||||
let path = TextRequirer::normalize_path(input.as_ref());
|
||||
let path = FsRequirer::normalize_path(input.as_ref());
|
||||
assert_eq!(
|
||||
&path,
|
||||
expected.as_ref() as &Path,
|
||||
|
||||
@@ -28,9 +28,7 @@ impl MemoryState {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[rustversion::since(1.85)]
|
||||
#[inline]
|
||||
#[allow(clippy::incompatible_msrv)]
|
||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
let mut mem_state = ptr::null_mut();
|
||||
if !ptr::fn_addr_eq(ffi::lua_getallocf(state, &mut mem_state), ALLOCATOR) {
|
||||
@@ -39,17 +37,6 @@ impl MemoryState {
|
||||
mem_state as *mut MemoryState
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[rustversion::before(1.85)]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
let mut mem_state = ptr::null_mut();
|
||||
if ffi::lua_getallocf(state, &mut mem_state) != ALLOCATOR {
|
||||
mem_state = ptr::null_mut();
|
||||
}
|
||||
mem_state as *mut MemoryState
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn used_memory(&self) -> usize {
|
||||
self.used_memory as usize
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use std::collections::{vec_deque, VecDeque};
|
||||
use std::collections::{VecDeque, vec_deque};
|
||||
use std::iter::FromIterator;
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
+23
-14
@@ -3,35 +3,44 @@
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
AnyUserData as LuaAnyUserData, BorrowedBytes as LuaBorrowedBytes, BorrowedStr as LuaBorrowedStr,
|
||||
Chunk as LuaChunk, Either as LuaEither, Error as LuaError, ErrorContext as LuaErrorContext,
|
||||
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
|
||||
Function as LuaFunction, FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode, Integer as LuaInteger,
|
||||
IntoLua, IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaNativeFn, LuaNativeFnMut, LuaOptions,
|
||||
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
|
||||
Either as LuaEither, Error as LuaError, FromLua, FromLuaMulti, Function as LuaFunction,
|
||||
Integer as LuaInteger, IntoLua, IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaOptions,
|
||||
LuaString, MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
|
||||
ObjectLike as LuaObjectLike, RegistryKey as LuaRegistryKey, Result as LuaResult, StdLib as LuaStdLib,
|
||||
String as LuaString, Table as LuaTable, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence,
|
||||
Thread as LuaThread, ThreadStatus as LuaThreadStatus, UserData as LuaUserData,
|
||||
UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
|
||||
UserDataMethods as LuaUserDataMethods, UserDataRef as LuaUserDataRef,
|
||||
UserDataRefMut as LuaUserDataRefMut, UserDataRegistry as LuaUserDataRegistry, Value as LuaValue,
|
||||
Variadic as LuaVariadic, VmState as LuaVmState, WeakLua,
|
||||
Table as LuaTable, Thread as LuaThread, UserData as LuaUserData, UserDataFields as LuaUserDataFields,
|
||||
UserDataMetatable as LuaUserDataMetatable, UserDataMethods as LuaUserDataMethods,
|
||||
UserDataOwned as LuaUserDataOwned, UserDataRef as LuaUserDataRef, UserDataRefMut as LuaUserDataRefMut,
|
||||
UserDataRegistry as LuaUserDataRegistry, Value as LuaValue, Variadic as LuaVariadic,
|
||||
VmState as LuaVmState, WeakLua, chunk::AsChunk as AsLuaChunk, chunk::Chunk as LuaChunk,
|
||||
chunk::ChunkMode as LuaChunkMode, error::ErrorContext as LuaErrorContext,
|
||||
error::ExternalError as LuaExternalError, error::ExternalResult as LuaExternalResult,
|
||||
function::FunctionInfo as LuaFunctionInfo, function::LuaNativeFn, function::LuaNativeFnMut,
|
||||
state::GcIncParams as LuaGcIncParams, state::GcMode as LuaGcMode, table::TablePairs as LuaTablePairs,
|
||||
table::TableSequence as LuaTableSequence, thread::ThreadStatus as LuaThreadStatus,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::HookTriggers as LuaHookTriggers;
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua55"))]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::state::GcGenParams as LuaGcGenParams;
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
CompileConstant as LuaCompileConstant, CoverageInfo as LuaCoverageInfo,
|
||||
NavigateError as LuaNavigateError, Require as LuaRequire, TextRequirer as LuaTextRequirer,
|
||||
Vector as LuaVector,
|
||||
chunk::{CompileConstant as LuaCompileConstant, Compiler as LuaCompiler},
|
||||
luau::{
|
||||
FsRequirer as LuaFsRequirer, HeapDump as LuaHeapDump, NavigateError as LuaNavigateError,
|
||||
Require as LuaRequire,
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{AsyncThread as LuaAsyncThread, LuaNativeAsyncFn};
|
||||
pub use crate::{function::LuaNativeAsyncFn, thread::AsyncThread as LuaAsyncThread};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[doc(no_inline)]
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ use crate::state::{Lua, LuaGuard, RawLua};
|
||||
use crate::traits::{FromLuaMulti, IntoLuaMulti};
|
||||
use crate::types::{Callback, CallbackUpvalue, ScopedCallback, ValueRef};
|
||||
use crate::userdata::{AnyUserData, UserData, UserDataRegistry, UserDataStorage};
|
||||
use crate::util::{self, check_stack, get_metatable_ptr, get_userdata, take_userdata, StackGuard};
|
||||
use crate::util::{self, StackGuard, check_stack, get_metatable_ptr, get_userdata, take_userdata};
|
||||
|
||||
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
|
||||
/// callbacks that are not required to be `Send` or `'static`.
|
||||
|
||||
+3
-4
@@ -4,7 +4,6 @@ use std::cell::RefCell;
|
||||
use std::os::raw::c_void;
|
||||
use std::rc::Rc;
|
||||
use std::result::Result as StdResult;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use rustc_hash::FxHashSet;
|
||||
use serde::de::{self, IntoDeserializer};
|
||||
@@ -243,14 +242,14 @@ impl<'de> serde::Deserializer<'de> for Deserializer {
|
||||
Value::Table(table) => {
|
||||
let _guard = RecursionGuard::new(&table, &self.visited);
|
||||
|
||||
let mut iter = table.pairs::<StdString, Value>();
|
||||
let mut iter = table.pairs::<String, Value>();
|
||||
let (variant, value) = match iter.next() {
|
||||
Some(v) => v?,
|
||||
None => {
|
||||
return Err(de::Error::invalid_value(
|
||||
de::Unexpected::Map,
|
||||
&"map with a single key",
|
||||
))
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -621,7 +620,7 @@ impl<'de> de::MapAccess<'de> for MapDeserializer<'_> {
|
||||
}
|
||||
|
||||
struct EnumDeserializer {
|
||||
variant: StdString,
|
||||
variant: String,
|
||||
value: Option<Value>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
|
||||
+4
-6
@@ -37,8 +37,8 @@ pub trait LuaSerdeExt: Sealed {
|
||||
fn null(&self) -> Value;
|
||||
|
||||
/// A metatable attachable to a Lua table to systematically encode it as Array (instead of Map).
|
||||
/// As result, encoded Array will contain only sequence part of the table, with the same length
|
||||
/// as the `#` operator on that table.
|
||||
/// As a result, encoded Array will contain only sequence part of the table, with the same
|
||||
/// length as the `#` operator on that table.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@@ -242,7 +242,5 @@ static ARRAY_METATABLE_REGISTRY_KEY: u8 = 0;
|
||||
pub mod de;
|
||||
pub mod ser;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use de::Deserializer;
|
||||
#[doc(inline)]
|
||||
pub use ser::Serializer;
|
||||
pub use de::{Deserializer, Options as DeserializeOptions};
|
||||
pub use ser::{Options as SerializeOptions, Serializer};
|
||||
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
//! Serialize a Rust data structure into Lua value.
|
||||
|
||||
use serde::{ser, Serialize};
|
||||
use serde::{Serialize, ser};
|
||||
|
||||
use super::LuaSerdeExt;
|
||||
use crate::error::{Error, Result};
|
||||
@@ -531,10 +531,10 @@ impl ser::SerializeStruct for SerializeStruct<'_> {
|
||||
Some(table @ Value::Table(_)) => Ok(table),
|
||||
Some(value @ Value::String(_)) if self.options.detect_serde_json_arbitrary_precision => {
|
||||
let number_s = value.to_string()?;
|
||||
if number_s.contains(['.', 'e', 'E']) {
|
||||
if let Ok(number) = number_s.parse().map(Value::Number) {
|
||||
return Ok(number);
|
||||
}
|
||||
if number_s.contains(['.', 'e', 'E'])
|
||||
&& let Ok(number) = number_s.parse().map(Value::Number)
|
||||
{
|
||||
return Ok(number);
|
||||
}
|
||||
Ok(number_s
|
||||
.parse()
|
||||
|
||||
+326
-281
@@ -1,3 +1,8 @@
|
||||
//! Lua state management.
|
||||
//!
|
||||
//! This module provides the main [`Lua`] state handle together with state-specific
|
||||
//! configuration and garbage collector controls.
|
||||
|
||||
use std::any::TypeId;
|
||||
use std::cell::{BorrowError, BorrowMutError, RefCell};
|
||||
use std::marker::PhantomData;
|
||||
@@ -15,16 +20,16 @@ use crate::memory::MemoryState;
|
||||
use crate::multi::MultiValue;
|
||||
use crate::scope::Scope;
|
||||
use crate::stdlib::StdLib;
|
||||
use crate::string::String;
|
||||
use crate::string::LuaString;
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::thread::{Thread, ThreadEvent, ThreadTriggers};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::types::{
|
||||
AppDataRef, AppDataRefMut, ArcReentrantMutexGuard, Integer, LuaType, MaybeSend, Number, ReentrantMutex,
|
||||
ReentrantMutexGuard, RegistryKey, VmState, XRc, XWeak,
|
||||
AppDataRef, AppDataRefMut, ArcReentrantMutexGuard, Integer, LuaType, MaybeSend, MaybeSync, Number,
|
||||
ReentrantMutex, ReentrantMutexGuard, RegistryKey, VmState, XRc, XWeak,
|
||||
};
|
||||
use crate::userdata::{AnyUserData, UserData, UserDataProxy, UserDataRegistry, UserDataStorage};
|
||||
use crate::util::{assert_stack, check_stack, protect_lua_closure, push_string, rawset_field, StackGuard};
|
||||
use crate::util::{StackGuard, assert_stack, check_stack, protect_lua_closure, push_string, rawset_field};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
@@ -44,6 +49,7 @@ use {
|
||||
use serde::Serialize;
|
||||
|
||||
pub(crate) use extra::ExtraData;
|
||||
#[doc(hidden)]
|
||||
pub use raw::RawLua;
|
||||
pub(crate) use util::callback_error_ext;
|
||||
|
||||
@@ -62,20 +68,126 @@ pub struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
|
||||
pub(crate) struct LuaGuard(ArcReentrantMutexGuard<RawLua>);
|
||||
|
||||
/// Mode of the Lua garbage collector (GC).
|
||||
///
|
||||
/// In Lua 5.4 GC can work in two modes: incremental and generational.
|
||||
/// Previous Lua versions support only incremental GC.
|
||||
/// Tuning parameters for the incremental GC collector.
|
||||
///
|
||||
/// More information can be found in the Lua [documentation].
|
||||
///
|
||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#2.5
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum GCMode {
|
||||
Incremental,
|
||||
/// [documentation]: https://www.lua.org/manual/5.5/manual.html#2.5.1
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct GcIncParams {
|
||||
/// Pause between successive GC cycles, expressed as a percentage of live memory.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub pause: Option<c_int>,
|
||||
|
||||
/// Target heap size as a percentage of live data, controlling how aggressively
|
||||
/// the GC reclaims memory (`LUA_GCSETGOAL`).
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub goal: Option<c_int>,
|
||||
|
||||
/// GC work performed per unit of memory allocated.
|
||||
pub step_multiplier: Option<c_int>,
|
||||
|
||||
/// Granularity of each GC step (see Lua reference for details).
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54", feature = "luau"))))]
|
||||
pub step_size: Option<c_int>,
|
||||
}
|
||||
|
||||
impl GcIncParams {
|
||||
/// Sets the `pause` parameter.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn pause(mut self, v: c_int) -> Self {
|
||||
self.pause = Some(v);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `goal` parameter.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn goal(mut self, v: c_int) -> Self {
|
||||
self.goal = Some(v);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `step_multiplier` parameter.
|
||||
pub fn step_multiplier(mut self, v: c_int) -> Self {
|
||||
self.step_multiplier = Some(v);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `step_size` parameter.
|
||||
#[cfg(any(feature = "lua55", feature = "lua54", feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54", feature = "luau"))))]
|
||||
pub fn step_size(mut self, v: c_int) -> Self {
|
||||
self.step_size = Some(v);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Tuning parameters for the generational GC collector (Lua 5.4+).
|
||||
///
|
||||
/// More information can be found in the Lua [documentation].
|
||||
///
|
||||
/// [documentation]: https://www.lua.org/manual/5.5/manual.html#2.5.2
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54"))))]
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct GcGenParams {
|
||||
/// Frequency of minor (young-generation) collection steps.
|
||||
pub minor_multiplier: Option<c_int>,
|
||||
|
||||
/// Threshold controlling how large the young generation can grow before triggering
|
||||
/// a shift from minor to major collection.
|
||||
pub minor_to_major: Option<c_int>,
|
||||
|
||||
/// Threshold controlling how much the major collection must shrink the heap before
|
||||
/// switching back to minor (young-generation) collection.
|
||||
#[cfg(feature = "lua55")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua55")))]
|
||||
pub major_to_minor: Option<c_int>,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
impl GcGenParams {
|
||||
/// Sets the `minor_multiplier` parameter.
|
||||
pub fn minor_multiplier(mut self, v: c_int) -> Self {
|
||||
self.minor_multiplier = Some(v);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `minor_to_major` threshold.
|
||||
pub fn minor_to_major(mut self, v: c_int) -> Self {
|
||||
self.minor_to_major = Some(v);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `major_to_minor` parameter.
|
||||
#[cfg(feature = "lua55")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua55")))]
|
||||
pub fn major_to_minor(mut self, v: c_int) -> Self {
|
||||
self.major_to_minor = Some(v);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Lua garbage collector (GC) operating mode.
|
||||
///
|
||||
/// Use [`Lua::gc_set_mode`] to switch the collector mode and/or tune its parameters.
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum GcMode {
|
||||
/// Incremental mark-and-sweep
|
||||
Incremental(GcIncParams),
|
||||
|
||||
/// Generational
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54"))))]
|
||||
Generational,
|
||||
Generational(GcGenParams),
|
||||
}
|
||||
|
||||
/// Controls Lua interpreter behavior such as Rust panics handling.
|
||||
@@ -337,30 +449,27 @@ impl Lua {
|
||||
R::from_stack_multi(nresults, &lua)
|
||||
}
|
||||
|
||||
/// Runs callback with the inner RawLua value. It can be used to manually push and get values on
|
||||
/// the stack.
|
||||
/// Calls provided function passing a reference to the [`RawLua`] handle.
|
||||
///
|
||||
/// This function is safe because all unsafe actions with RawLua can only be done with unsafe
|
||||
/// Provided [`RawLua`] handle can be used to manually pushing/popping values to/from the stack.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result, FromLua, IntoLua};
|
||||
/// # use mlua::{Lua, Result, FromLua, IntoLua, IntoLuaMulti};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let n: i32 = {
|
||||
/// let num = 11i32;
|
||||
/// lua.exec_raw_lua(|lua| {
|
||||
/// unsafe {
|
||||
/// <i32 as IntoLua>::push_into_stack(num, lua)?;
|
||||
/// let nums = (3, 4, 5);
|
||||
/// lua.exec_raw_lua(|rawlua| unsafe {
|
||||
/// nums.push_into_stack_multi(rawlua)?;
|
||||
/// let mut sum = 0;
|
||||
/// for _ in 0..3 {
|
||||
/// sum += rawlua.pop::<i32>()?;
|
||||
/// }
|
||||
///
|
||||
/// let n = unsafe {
|
||||
/// <i32 as FromLua>::from_stack(-1, lua)?
|
||||
/// };
|
||||
/// Result::Ok(n)
|
||||
/// Result::Ok(sum)
|
||||
/// })
|
||||
/// }?;
|
||||
/// assert_eq!(n, 11);
|
||||
/// assert_eq!(n, 12);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
@@ -437,31 +546,6 @@ impl Lua {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[deprecated(since = "0.11.0", note = "Use `register_module` instead")]
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
pub fn load_from_function<T: FromLua>(&self, modname: &str, func: Function) -> Result<T> {
|
||||
let loaded = unsafe {
|
||||
self.exec_raw::<Table>((), |state| {
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_LOADED_TABLE);
|
||||
})?
|
||||
};
|
||||
|
||||
let value = match loaded.raw_get(modname)? {
|
||||
Value::Nil => {
|
||||
let result = match func.call(modname)? {
|
||||
Value::Nil => Value::Boolean(true),
|
||||
res => res,
|
||||
};
|
||||
loaded.raw_set(modname, &result)?;
|
||||
result
|
||||
}
|
||||
res => res,
|
||||
};
|
||||
T::from_lua(value, self)
|
||||
}
|
||||
|
||||
/// Unloads module `modname`.
|
||||
///
|
||||
/// This method does not support unloading binary Lua modules since they are internally cached
|
||||
@@ -758,92 +842,87 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a thread creation callback that will be called when a thread is created.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn set_thread_creation_callback<F>(&self, callback: F)
|
||||
/// Sets a callback invoked when thread lifecycle events occur.
|
||||
///
|
||||
/// `triggers` controls which events trigger the callback, see [`ThreadTriggers`] for more
|
||||
/// details.
|
||||
///
|
||||
/// Only one callback can be registered at a time. Calling this again replaces the previous
|
||||
/// callback and its triggers.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Subscribe only to yield events:
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result, ThreadTriggers, ThreadEvent};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// lua.set_thread_event_callback(
|
||||
/// ThreadTriggers::ON_YIELD,
|
||||
/// |_lua, event| {
|
||||
/// if let ThreadEvent::Yield(thread) = event {
|
||||
/// println!("thread yielded");
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// },
|
||||
/// );
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn set_thread_event_callback<F>(&self, triggers: ThreadTriggers, callback: F)
|
||||
where
|
||||
F: Fn(&Lua, Thread) -> Result<()> + MaybeSend + 'static,
|
||||
F: Fn(&Lua, ThreadEvent) -> Result<()> + MaybeSend + 'static,
|
||||
{
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
(*lua.extra.get()).thread_creation_callback = Some(XRc::new(callback));
|
||||
(*ffi::lua_callbacks(lua.main_state())).userthread = Some(Self::userthread_proc);
|
||||
(*lua.extra.get()).thread_triggers = triggers;
|
||||
(*lua.extra.get()).thread_event_callback = Some(XRc::new(callback));
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
let proc = Self::userthread_proc as _;
|
||||
(*ffi::lua_callbacks(lua.main_state())).userthread = triggers.on_create.then_some(proc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a thread collection callback that will be called when a thread is destroyed.
|
||||
/// Removes the thread event callback previously set by [`Lua::set_thread_event_callback`].
|
||||
///
|
||||
/// Luau GC does not support exceptions during collection, so the callback must be
|
||||
/// non-panicking. If the callback panics, the program will be aborted.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn set_thread_collection_callback<F>(&self, callback: F)
|
||||
where
|
||||
F: Fn(crate::LightUserData) + MaybeSend + 'static,
|
||||
{
|
||||
/// This function has no effect if a callback was not previously set.
|
||||
pub fn remove_thread_event_callback(&self) {
|
||||
let lua = self.lock();
|
||||
let extra = lua.extra.get();
|
||||
unsafe {
|
||||
(*lua.extra.get()).thread_collection_callback = Some(XRc::new(callback));
|
||||
(*ffi::lua_callbacks(lua.main_state())).userthread = Some(Self::userthread_proc);
|
||||
(*extra).thread_triggers = ThreadTriggers::new();
|
||||
(*extra).thread_event_callback = None;
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
(*ffi::lua_callbacks(lua.main_state())).userthread = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
unsafe extern "C-unwind" fn userthread_proc(parent: *mut ffi::lua_State, child: *mut ffi::lua_State) {
|
||||
// Only handle thread creation
|
||||
if parent.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
let extra = ExtraData::get(child);
|
||||
if !parent.is_null() {
|
||||
// Thread is created
|
||||
let callback = match (*extra).thread_creation_callback {
|
||||
Some(ref cb) => cb.clone(),
|
||||
None => return,
|
||||
};
|
||||
if XRc::strong_count(&callback) > 2 {
|
||||
return; // Don't allow recursion
|
||||
}
|
||||
ffi::lua_pushthread(child);
|
||||
ffi::lua_xmove(child, (*extra).ref_thread, 1);
|
||||
let value = Thread((*extra).raw_lua().pop_ref_thread(), child);
|
||||
callback_error_ext(parent, extra, false, move |extra, _| {
|
||||
callback((*extra).lua(), value)
|
||||
})
|
||||
} else {
|
||||
// Thread is about to be collected
|
||||
let callback = match (*extra).thread_collection_callback {
|
||||
Some(ref cb) => cb.clone(),
|
||||
None => return,
|
||||
};
|
||||
|
||||
// We need to wrap the callback call in non-unwind function as it's not safe to unwind when
|
||||
// Luau GC is running.
|
||||
// This will trigger `abort()` if the callback panics.
|
||||
unsafe extern "C" fn run_callback(
|
||||
callback: *const crate::types::ThreadCollectionCallback,
|
||||
value: *mut ffi::lua_State,
|
||||
) {
|
||||
(*callback)(crate::LightUserData(value as _));
|
||||
}
|
||||
|
||||
(*extra).running_gc = true;
|
||||
run_callback(&callback, child);
|
||||
(*extra).running_gc = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes any thread creation or collection callbacks previously set by
|
||||
/// [`Lua::set_thread_creation_callback`] or [`Lua::set_thread_collection_callback`].
|
||||
///
|
||||
/// This function has no effect if a thread callbacks were not previously set.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn remove_thread_callbacks(&self) {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
let extra = lua.extra.get();
|
||||
(*extra).thread_creation_callback = None;
|
||||
(*extra).thread_collection_callback = None;
|
||||
(*ffi::lua_callbacks(lua.main_state())).userthread = None;
|
||||
if !(*extra).thread_triggers.on_create {
|
||||
return;
|
||||
}
|
||||
let callback = match &(*extra).thread_event_callback {
|
||||
Some(cb) if XRc::strong_count(cb) == 1 => cb.clone(),
|
||||
_ => return,
|
||||
};
|
||||
ffi::lua_pushthread(child);
|
||||
ffi::lua_xmove(child, (*extra).ref_thread, 1);
|
||||
let thread = Thread((*extra).raw_lua().pop_ref_thread(), child);
|
||||
callback_error_ext(parent, extra, false, move |extra, _| {
|
||||
callback((*extra).lua(), ThreadEvent::Create(thread))
|
||||
})
|
||||
}
|
||||
|
||||
/// Sets the warning function to be used by Lua to emit warnings.
|
||||
@@ -855,7 +934,6 @@ impl Lua {
|
||||
{
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
use std::string::String as StdString;
|
||||
|
||||
unsafe extern "C-unwind" fn warn_proc(ud: *mut c_void, msg: *const c_char, tocont: c_int) {
|
||||
let extra = ud as *mut ExtraData;
|
||||
@@ -865,7 +943,7 @@ impl Lua {
|
||||
if XRc::strong_count(&warn_callback) > 2 {
|
||||
return Ok(());
|
||||
}
|
||||
let msg = StdString::from_utf8_lossy(CStr::from_ptr(msg).to_bytes());
|
||||
let msg = String::from_utf8_lossy(CStr::from_ptr(msg).to_bytes());
|
||||
warn_callback((*extra).lua(), &msg, tocont != 0)
|
||||
});
|
||||
}
|
||||
@@ -910,8 +988,8 @@ impl Lua {
|
||||
|
||||
/// Gets information about the interpreter runtime stack at the given level.
|
||||
///
|
||||
/// This function calls callback `f`, passing the [`Debug`] structure that can be used to get
|
||||
/// information about the function executing at a given level.
|
||||
/// This function calls callback `f`, passing the [`struct@Debug`] structure that can be used to
|
||||
/// get information about the function executing at a given level.
|
||||
/// Level `0` is the current running function, whereas level `n+1` is the function that has
|
||||
/// called level `n` (except for tail calls, which do not count in the stack).
|
||||
pub fn inspect_stack<R>(&self, level: usize, f: impl FnOnce(&Debug) -> R) -> Option<R> {
|
||||
@@ -936,7 +1014,7 @@ impl Lua {
|
||||
///
|
||||
/// The `msg` parameter, if provided, is added at the beginning of the traceback.
|
||||
/// The `level` parameter works the same way as in [`Lua::inspect_stack`].
|
||||
pub fn traceback(&self, msg: Option<&str>, level: usize) -> Result<String> {
|
||||
pub fn traceback(&self, msg: Option<&str>, level: usize) -> Result<LuaString> {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
check_stack(lua.state(), 3)?;
|
||||
@@ -948,7 +1026,7 @@ impl Lua {
|
||||
// `protect_lua` adds it's own call frame, so we need to increase level by 1
|
||||
ffi::luaL_traceback(state, state, msg, (level + 1) as c_int);
|
||||
})?;
|
||||
Ok(String(lua.pop_ref()))
|
||||
Ok(LuaString(lua.pop_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -999,19 +1077,19 @@ impl Lua {
|
||||
unsafe { ffi::lua_gc(lua.main_state(), ffi::LUA_GCISRUNNING, 0) != 0 }
|
||||
}
|
||||
|
||||
/// Stop the Lua GC from running
|
||||
/// Stops the Lua GC from running.
|
||||
pub fn gc_stop(&self) {
|
||||
let lua = self.lock();
|
||||
unsafe { ffi::lua_gc(lua.main_state(), ffi::LUA_GCSTOP, 0) };
|
||||
}
|
||||
|
||||
/// Restarts the Lua GC if it is not running
|
||||
/// Restarts the Lua GC if it is not running.
|
||||
pub fn gc_restart(&self) {
|
||||
let lua = self.lock();
|
||||
unsafe { ffi::lua_gc(lua.main_state(), ffi::LUA_GCRESTART, 0) };
|
||||
}
|
||||
|
||||
/// Perform a full garbage-collection cycle.
|
||||
/// Performs a full garbage-collection cycle.
|
||||
///
|
||||
/// It may be necessary to call this function twice to collect all currently unreachable
|
||||
/// objects. Once to finish the current gc cycle, and once to start and finish the next cycle.
|
||||
@@ -1024,153 +1102,128 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Steps the garbage collector one indivisible step.
|
||||
/// Performs a basic step of garbage collection.
|
||||
///
|
||||
/// Returns `true` if this has finished a collection cycle.
|
||||
/// In incremental mode, a basic step corresponds to the current step size. In generational
|
||||
/// mode, a basic step performs a full minor collection or an incremental step, if the collector
|
||||
/// has scheduled one.
|
||||
///
|
||||
/// In incremental mode, returns `true` if this step has finished a collection cycle.
|
||||
/// In generational mode, returns `true` if the step finished a major collection.
|
||||
pub fn gc_step(&self) -> Result<bool> {
|
||||
self.gc_step_kbytes(0)
|
||||
}
|
||||
|
||||
/// Steps the garbage collector as though memory had been allocated.
|
||||
///
|
||||
/// if `kbytes` is 0, then this is the same as calling `gc_step`. Returns true if this step has
|
||||
/// finished a collection cycle.
|
||||
pub fn gc_step_kbytes(&self, kbytes: c_int) -> Result<bool> {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state();
|
||||
unsafe {
|
||||
check_stack(state, 3)?;
|
||||
protect_lua!(state, 0, 0, |state| {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSTEP, kbytes) != 0
|
||||
ffi::lua_gc(state, ffi::LUA_GCSTEP, 0) != 0
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the `pause` value of the collector.
|
||||
/// Switches the GC to the given mode with the provided parameters.
|
||||
///
|
||||
/// Returns the previous value of `pause`. More information can be found in the Lua
|
||||
/// [documentation].
|
||||
/// Returns the previous [`GcMode`]. The returned value's parameter fields are always
|
||||
/// `None` because Lua's C API does not provide a way to read back current parameter values
|
||||
/// without changing them.
|
||||
///
|
||||
/// For Luau this parameter sets GC goal
|
||||
/// # Examples
|
||||
///
|
||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#2.5
|
||||
pub fn gc_set_pause(&self, pause: c_int) -> c_int {
|
||||
/// Switch to generational mode (Lua 5.4+):
|
||||
/// ```ignore
|
||||
/// let prev = lua.gc_set_mode(GcMode::Generational(GcGenParams::default()));
|
||||
/// ```
|
||||
///
|
||||
/// Switch to incremental mode with custom parameters:
|
||||
/// ```ignore
|
||||
/// lua.gc_set_mode(GcMode::Incremental(
|
||||
/// GcIncParams::default().pause(200).step_multiplier(100)
|
||||
/// ));
|
||||
/// ```
|
||||
pub fn gc_set_mode(&self, mode: GcMode) -> GcMode {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state();
|
||||
unsafe {
|
||||
|
||||
match mode {
|
||||
#[cfg(feature = "lua55")]
|
||||
return ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPPAUSE, pause);
|
||||
|
||||
#[cfg(not(any(feature = "lua55", feature = "luau")))]
|
||||
return ffi::lua_gc(state, ffi::LUA_GCSETPAUSE, pause);
|
||||
|
||||
GcMode::Incremental(params) => unsafe {
|
||||
if let Some(v) = params.pause {
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPPAUSE, v);
|
||||
}
|
||||
if let Some(v) = params.step_multiplier {
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPSTEPMUL, v);
|
||||
}
|
||||
if let Some(v) = params.step_size {
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPSTEPSIZE, v);
|
||||
}
|
||||
match ffi::lua_gc(state, ffi::LUA_GCINC) {
|
||||
ffi::LUA_GCINC => GcMode::Incremental(GcIncParams::default()),
|
||||
ffi::LUA_GCGEN => GcMode::Generational(GcGenParams::default()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
#[cfg(feature = "lua54")]
|
||||
GcMode::Incremental(params) => unsafe {
|
||||
let pause = params.pause.unwrap_or(0);
|
||||
let step_mul = params.step_multiplier.unwrap_or(0);
|
||||
let step_size = params.step_size.unwrap_or(0);
|
||||
match ffi::lua_gc(state, ffi::LUA_GCINC, pause, step_mul, step_size) {
|
||||
ffi::LUA_GCINC => GcMode::Incremental(GcIncParams::default()),
|
||||
ffi::LUA_GCGEN => GcMode::Generational(GcGenParams::default()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
#[cfg(any(feature = "lua53", feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
GcMode::Incremental(params) => unsafe {
|
||||
if let Some(v) = params.pause {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSETPAUSE, v);
|
||||
}
|
||||
if let Some(v) = params.step_multiplier {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSETSTEPMUL, v);
|
||||
}
|
||||
GcMode::Incremental(GcIncParams::default())
|
||||
},
|
||||
#[cfg(feature = "luau")]
|
||||
return ffi::lua_gc(state, ffi::LUA_GCSETGOAL, pause);
|
||||
}
|
||||
}
|
||||
GcMode::Incremental(params) => unsafe {
|
||||
if let Some(v) = params.goal {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSETGOAL, v);
|
||||
}
|
||||
if let Some(v) = params.step_multiplier {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSETSTEPMUL, v);
|
||||
}
|
||||
if let Some(v) = params.step_size {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSETSTEPSIZE, v);
|
||||
}
|
||||
GcMode::Incremental(GcIncParams::default())
|
||||
},
|
||||
|
||||
/// Sets the `step multiplier` value of the collector.
|
||||
///
|
||||
/// Returns the previous value of the `step multiplier`. More information can be found in the
|
||||
/// Lua [documentation].
|
||||
///
|
||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#2.5
|
||||
pub fn gc_set_step_multiplier(&self, step_multiplier: c_int) -> c_int {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
#[cfg(feature = "lua55")]
|
||||
return ffi::lua_gc(
|
||||
lua.main_state(),
|
||||
ffi::LUA_GCPARAM,
|
||||
ffi::LUA_GCPSTEPMUL,
|
||||
step_multiplier,
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "lua55"))]
|
||||
return ffi::lua_gc(lua.main_state(), ffi::LUA_GCSETSTEPMUL, step_multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
/// Changes the collector to incremental mode with the given parameters.
|
||||
///
|
||||
/// Returns the previous mode (always `GCMode::Incremental` in Lua < 5.4).
|
||||
/// More information can be found in the Lua [documentation].
|
||||
///
|
||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#2.5.1
|
||||
pub fn gc_inc(&self, pause: c_int, step_multiplier: c_int, step_size: c_int) -> GCMode {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state();
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "lua51",
|
||||
feature = "luajit",
|
||||
feature = "luau"
|
||||
))]
|
||||
unsafe {
|
||||
if pause > 0 {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
ffi::lua_gc(state, ffi::LUA_GCSETPAUSE, pause);
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_gc(state, ffi::LUA_GCSETGOAL, pause);
|
||||
}
|
||||
|
||||
if step_multiplier > 0 {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSETSTEPMUL, step_multiplier);
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
if step_size > 0 {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSETSTEPSIZE, step_size);
|
||||
}
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let _ = step_size; // Ignored
|
||||
|
||||
return GCMode::Incremental;
|
||||
}
|
||||
|
||||
#[cfg(feature = "lua55")]
|
||||
let prev_mode = unsafe {
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPPAUSE, pause);
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPSTEPMUL, step_multiplier);
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPSTEPSIZE, step_size);
|
||||
ffi::lua_gc(state, ffi::LUA_GCINC)
|
||||
};
|
||||
#[cfg(feature = "lua54")]
|
||||
let prev_mode = unsafe { ffi::lua_gc(state, ffi::LUA_GCINC, pause, step_multiplier, step_size) };
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
match prev_mode {
|
||||
ffi::LUA_GCINC => GCMode::Incremental,
|
||||
ffi::LUA_GCGEN => GCMode::Generational,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Changes the collector to generational mode with the given parameters.
|
||||
///
|
||||
/// Returns the previous mode. More information about the generational GC
|
||||
/// can be found in the Lua 5.4 [documentation][lua_doc].
|
||||
///
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#2.5.2
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", feature = "lua54"))))]
|
||||
pub fn gc_gen(&self, minor_multiplier: c_int, major_multiplier: c_int) -> GCMode {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state();
|
||||
#[cfg(feature = "lua55")]
|
||||
let prev_mode = unsafe {
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPMINORMUL, minor_multiplier);
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPMINORMAJOR, major_multiplier);
|
||||
// TODO: LUA_GCPMAJORMINOR
|
||||
ffi::lua_gc(state, ffi::LUA_GCGEN)
|
||||
};
|
||||
#[cfg(not(feature = "lua55"))]
|
||||
let prev_mode = unsafe { ffi::lua_gc(state, ffi::LUA_GCGEN, minor_multiplier, major_multiplier) };
|
||||
match prev_mode {
|
||||
ffi::LUA_GCGEN => GCMode::Generational,
|
||||
ffi::LUA_GCINC => GCMode::Incremental,
|
||||
_ => unreachable!(),
|
||||
GcMode::Generational(params) => unsafe {
|
||||
if let Some(v) = params.minor_multiplier {
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPMINORMUL, v);
|
||||
}
|
||||
if let Some(v) = params.minor_to_major {
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPMINORMAJOR, v);
|
||||
}
|
||||
if let Some(v) = params.major_to_minor {
|
||||
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPMAJORMINOR, v);
|
||||
}
|
||||
match ffi::lua_gc(state, ffi::LUA_GCGEN) {
|
||||
ffi::LUA_GCGEN => GcMode::Generational(GcGenParams::default()),
|
||||
ffi::LUA_GCINC => GcMode::Incremental(GcIncParams::default()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
#[cfg(feature = "lua54")]
|
||||
GcMode::Generational(params) => unsafe {
|
||||
let minor = params.minor_multiplier.unwrap_or(0);
|
||||
let minor_to_major = params.minor_to_major.unwrap_or(0);
|
||||
match ffi::lua_gc(state, ffi::LUA_GCGEN, minor, minor_to_major) {
|
||||
ffi::LUA_GCGEN => GcMode::Generational(GcGenParams::default()),
|
||||
ffi::LUA_GCINC => GcMode::Incremental(GcIncParams::default()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1205,10 +1258,10 @@ impl Lua {
|
||||
#[doc(hidden)]
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub fn set_fflag(name: &str, enabled: bool) -> StdResult<(), ()> {
|
||||
if let Ok(name) = std::ffi::CString::new(name) {
|
||||
if unsafe { ffi::luau_setfflag(name.as_ptr(), enabled as c_int) != 0 } {
|
||||
return Ok(());
|
||||
}
|
||||
if let Ok(name) = std::ffi::CString::new(name)
|
||||
&& unsafe { ffi::luau_setfflag(name.as_ptr(), enabled as c_int) != 0 }
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
@@ -1248,7 +1301,7 @@ impl Lua {
|
||||
/// Lua strings can be arbitrary `[u8]` data including embedded nulls, so in addition to `&str`
|
||||
/// and `&String`, you can also pass plain `&[u8]` here.
|
||||
#[inline]
|
||||
pub fn create_string(&self, s: impl AsRef<[u8]>) -> Result<String> {
|
||||
pub fn create_string(&self, s: impl AsRef<[u8]>) -> Result<LuaString> {
|
||||
unsafe { self.lock().create_string(s.as_ref()) }
|
||||
}
|
||||
|
||||
@@ -1259,7 +1312,7 @@ impl Lua {
|
||||
#[cfg(feature = "lua55")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua55")))]
|
||||
#[inline]
|
||||
pub fn create_external_string(&self, s: impl Into<Vec<u8>>) -> Result<String> {
|
||||
pub fn create_external_string(&self, s: impl Into<Vec<u8>>) -> Result<LuaString> {
|
||||
unsafe { self.lock().create_external_string(s.into()) }
|
||||
}
|
||||
|
||||
@@ -1457,7 +1510,7 @@ impl Lua {
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`AsyncThread`]: crate::AsyncThread
|
||||
/// [`AsyncThread`]: crate::thread::AsyncThread
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn create_async_function<F, A, FR, R>(&self, func: F) -> Result<Function>
|
||||
@@ -1493,7 +1546,7 @@ impl Lua {
|
||||
#[inline]
|
||||
pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: UserData + MaybeSend + 'static,
|
||||
T: UserData + MaybeSend + MaybeSync + 'static,
|
||||
{
|
||||
unsafe { self.lock().make_userdata(UserDataStorage::new(data)) }
|
||||
}
|
||||
@@ -1504,7 +1557,7 @@ impl Lua {
|
||||
#[inline]
|
||||
pub fn create_ser_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: UserData + Serialize + MaybeSend + 'static,
|
||||
T: UserData + Serialize + MaybeSend + MaybeSync + 'static,
|
||||
{
|
||||
unsafe { self.lock().make_userdata(UserDataStorage::new_ser(data)) }
|
||||
}
|
||||
@@ -1519,7 +1572,7 @@ impl Lua {
|
||||
#[inline]
|
||||
pub fn create_any_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: MaybeSend + 'static,
|
||||
T: MaybeSend + MaybeSync + 'static,
|
||||
{
|
||||
unsafe { self.lock().make_any_userdata(UserDataStorage::new(data)) }
|
||||
}
|
||||
@@ -1532,7 +1585,7 @@ impl Lua {
|
||||
#[inline]
|
||||
pub fn create_ser_any_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: Serialize + MaybeSend + 'static,
|
||||
T: Serialize + MaybeSend + MaybeSync + 'static,
|
||||
{
|
||||
unsafe { (self.lock()).make_any_userdata(UserDataStorage::new_ser(data)) }
|
||||
}
|
||||
@@ -1741,7 +1794,7 @@ impl Lua {
|
||||
///
|
||||
/// To succeed, the value must be a string (in which case this is a no-op), an integer, or a
|
||||
/// number.
|
||||
pub fn coerce_string(&self, v: Value) -> Result<Option<String>> {
|
||||
pub fn coerce_string(&self, v: Value) -> Result<Option<LuaString>> {
|
||||
Ok(match v {
|
||||
Value::String(s) => Some(s),
|
||||
v => unsafe {
|
||||
@@ -1759,7 +1812,7 @@ impl Lua {
|
||||
})?
|
||||
};
|
||||
if !res.is_null() {
|
||||
Some(String(lua.pop_ref()))
|
||||
Some(LuaString(lua.pop_ref()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -1785,11 +1838,7 @@ impl Lua {
|
||||
lua.push_value(&v)?;
|
||||
let mut isint = 0;
|
||||
let i = ffi::lua_tointegerx(state, -1, &mut isint);
|
||||
if isint == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(i)
|
||||
}
|
||||
if isint == 0 { None } else { Some(i) }
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1811,11 +1860,7 @@ impl Lua {
|
||||
lua.push_value(&v)?;
|
||||
let mut isnum = 0;
|
||||
let n = ffi::lua_tonumberx(state, -1, &mut isnum);
|
||||
if isnum == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(n)
|
||||
}
|
||||
if isnum == 0 { None } else { Some(n) }
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
+7
-10
@@ -12,9 +12,10 @@ use rustc_hash::FxHashMap;
|
||||
use crate::error::Result;
|
||||
use crate::state::RawLua;
|
||||
use crate::stdlib::StdLib;
|
||||
use crate::types::{AppData, ReentrantMutex, XRc};
|
||||
use crate::thread::ThreadTriggers;
|
||||
use crate::types::{AppData, ReentrantMutex, ThreadEventCallback, XRc};
|
||||
use crate::userdata::RawUserDataRegistry;
|
||||
use crate::util::{get_internal_metatable, push_internal_userdata, TypeKey, WrappedFailure};
|
||||
use crate::util::{TypeKey, WrappedFailure, get_internal_metatable, push_internal_userdata};
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
use crate::chunk::Compiler;
|
||||
@@ -81,10 +82,8 @@ pub(crate) struct ExtraData {
|
||||
pub(super) warn_callback: Option<crate::types::WarnCallback>,
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) interrupt_callback: Option<crate::types::InterruptCallback>,
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) thread_creation_callback: Option<crate::types::ThreadCreationCallback>,
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) thread_collection_callback: Option<crate::types::ThreadCollectionCallback>,
|
||||
pub(super) thread_triggers: ThreadTriggers,
|
||||
pub(super) thread_event_callback: Option<ThreadEventCallback>,
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
pub(crate) running_gc: bool,
|
||||
@@ -186,10 +185,8 @@ impl ExtraData {
|
||||
warn_callback: None,
|
||||
#[cfg(feature = "luau")]
|
||||
interrupt_callback: None,
|
||||
#[cfg(feature = "luau")]
|
||||
thread_creation_callback: None,
|
||||
#[cfg(feature = "luau")]
|
||||
thread_collection_callback: None,
|
||||
thread_triggers: ThreadTriggers::default(),
|
||||
thread_event_callback: None,
|
||||
#[cfg(feature = "luau")]
|
||||
sandboxed: false,
|
||||
#[cfg(feature = "luau")]
|
||||
|
||||
+94
-44
@@ -1,7 +1,7 @@
|
||||
use std::any::TypeId;
|
||||
use std::cell::{Cell, UnsafeCell};
|
||||
use std::ffi::CStr;
|
||||
use std::mem;
|
||||
use std::mem::{self, ManuallyDrop};
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::resume_unwind;
|
||||
use std::ptr::{self, NonNull};
|
||||
@@ -10,26 +10,26 @@ use std::sync::Arc;
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::memory::{MemoryState, ALLOCATOR};
|
||||
use crate::memory::{ALLOCATOR, MemoryState};
|
||||
use crate::state::util::callback_error_ext;
|
||||
use crate::stdlib::StdLib;
|
||||
use crate::string::String;
|
||||
use crate::string::LuaString;
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::traits::IntoLua;
|
||||
use crate::thread::{Thread, ThreadTriggers};
|
||||
use crate::traits::{FromLua, IntoLua};
|
||||
use crate::types::{
|
||||
AppDataRef, AppDataRefMut, Callback, CallbackUpvalue, DestructedUserdata, Integer, LightUserData,
|
||||
LuaType, MaybeSend, ReentrantMutex, RegistryKey, ValueRef, XRc,
|
||||
LuaType, MaybeSend, ReentrantMutex, RegistryKey, ThreadEventCallback, ValueRef, XRc,
|
||||
};
|
||||
use crate::userdata::{
|
||||
init_userdata_metatable, AnyUserData, MetaMethod, RawUserDataRegistry, UserData, UserDataRegistry,
|
||||
UserDataStorage,
|
||||
AnyUserData, MetaMethod, RawUserDataRegistry, UserData, UserDataRegistry, UserDataStorage,
|
||||
init_userdata_metatable,
|
||||
};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, get_destructed_userdata_metatable, get_internal_userdata, get_main_state,
|
||||
get_metatable_ptr, get_userdata, init_error_registry, init_internal_metatable, pop_error,
|
||||
push_internal_userdata, push_string, push_table, push_userdata, rawset_field, safe_pcall, safe_xpcall,
|
||||
short_type_name, StackGuard, WrappedFailure,
|
||||
StackGuard, WrappedFailure, assert_stack, check_stack, get_destructed_userdata_metatable,
|
||||
get_internal_userdata, get_main_state, get_metatable_ptr, get_userdata, init_error_registry,
|
||||
init_internal_metatable, pop_error, push_internal_userdata, push_string, push_table, push_userdata,
|
||||
rawset_field, safe_pcall, safe_xpcall, short_type_name,
|
||||
};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
@@ -50,13 +50,13 @@ use {
|
||||
std::task::{Context, Poll, Waker},
|
||||
};
|
||||
|
||||
/// An inner Lua struct which holds a raw Lua state.
|
||||
/// An internal Lua struct which holds a raw Lua state.
|
||||
#[doc(hidden)]
|
||||
pub struct RawLua {
|
||||
// The state is dynamic and depends on context
|
||||
pub(super) state: Cell<*mut ffi::lua_State>,
|
||||
pub(super) main_state: Option<NonNull<ffi::lua_State>>,
|
||||
pub(super) extra: XRc<UnsafeCell<ExtraData>>,
|
||||
pub(super) extra: ManuallyDrop<XRc<UnsafeCell<ExtraData>>>,
|
||||
owned: bool,
|
||||
}
|
||||
|
||||
@@ -82,6 +82,9 @@ impl Drop for RawLua {
|
||||
if !mem_state.is_null() {
|
||||
drop(Box::from_raw(mem_state));
|
||||
}
|
||||
|
||||
// Drop the `ExtraData` reference after `lua_close` has collected the registry entry
|
||||
ManuallyDrop::drop(&mut self.extra);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -245,7 +248,7 @@ impl RawLua {
|
||||
state: Cell::new(state),
|
||||
// Make sure that we don't store current state as main state (if it's not available)
|
||||
main_state: get_main_state(state).and_then(NonNull::new),
|
||||
extra: XRc::clone(&extra),
|
||||
extra: ManuallyDrop::new(XRc::clone(&extra)),
|
||||
owned,
|
||||
}));
|
||||
(*extra.get()).set_lua(&rawlua);
|
||||
@@ -516,34 +519,34 @@ impl RawLua {
|
||||
}
|
||||
|
||||
/// See [`Lua::create_string`]
|
||||
pub(crate) unsafe fn create_string(&self, s: &[u8]) -> Result<String> {
|
||||
pub(crate) unsafe fn create_string(&self, s: &[u8]) -> Result<LuaString> {
|
||||
let state = self.state();
|
||||
if self.unlikely_memory_error() {
|
||||
push_string(state, s, false)?;
|
||||
return Ok(String(self.pop_ref()));
|
||||
return Ok(LuaString(self.pop_ref()));
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
push_string(state, s, true)?;
|
||||
Ok(String(self.pop_ref()))
|
||||
Ok(LuaString(self.pop_ref()))
|
||||
}
|
||||
|
||||
/// Creates an external string, that is, a string that uses memory not managed by Lua.
|
||||
///
|
||||
/// Modifies the input data to add `\0` terminator.
|
||||
#[cfg(feature = "lua55")]
|
||||
pub(crate) unsafe fn create_external_string(&self, bytes: Vec<u8>) -> Result<String> {
|
||||
pub(crate) unsafe fn create_external_string(&self, bytes: Vec<u8>) -> Result<LuaString> {
|
||||
let state = self.state();
|
||||
if self.unlikely_memory_error() {
|
||||
crate::util::push_external_string(state, bytes, false)?;
|
||||
return Ok(String(self.pop_ref()));
|
||||
return Ok(LuaString(self.pop_ref()));
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
crate::util::push_external_string(state, bytes, true)?;
|
||||
Ok(String(self.pop_ref()))
|
||||
Ok(LuaString(self.pop_ref()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
@@ -640,7 +643,7 @@ impl RawLua {
|
||||
|
||||
let protect = !self.unlikely_memory_error();
|
||||
#[cfg(feature = "luau")]
|
||||
let protect = protect || (*self.extra.get()).thread_creation_callback.is_some();
|
||||
let protect = protect || self.thread_event_triggers().on_create;
|
||||
|
||||
let thread_state = if !protect {
|
||||
ffi::lua_newthread(state)
|
||||
@@ -653,6 +656,19 @@ impl RawLua {
|
||||
self.set_thread_hook(thread_state, HookKind::Global)?;
|
||||
|
||||
let thread = Thread(self.pop_ref(), thread_state);
|
||||
|
||||
// Exec creation callback for non-Luau (Luau handles this via `userthread_proc`)
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if self.thread_event_triggers().on_create {
|
||||
let extra = self.extra.get();
|
||||
if let Some(ref cb) = (*extra).thread_event_callback
|
||||
&& XRc::strong_count(cb) == 1
|
||||
{
|
||||
let cb = cb.clone();
|
||||
cb((*extra).lua(), crate::thread::ThreadEvent::Create(thread.clone()))?;
|
||||
}
|
||||
}
|
||||
|
||||
ffi::lua_xpush(self.ref_thread(), thread_state, func.0.index);
|
||||
Ok(thread)
|
||||
}
|
||||
@@ -681,13 +697,23 @@ impl RawLua {
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) unsafe fn recycle_thread(&self, thread: &mut Thread) {
|
||||
let extra = &mut *self.extra.get();
|
||||
if extra.thread_pool.len() < extra.thread_pool.capacity() {
|
||||
if let Some(index) = thread.0.index_count.take() {
|
||||
extra.thread_pool.push(index);
|
||||
}
|
||||
if extra.thread_pool.len() < extra.thread_pool.capacity()
|
||||
&& let Some(index) = thread.0.index_count.take()
|
||||
{
|
||||
extra.thread_pool.push(index);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn thread_event_triggers(&self) -> ThreadTriggers {
|
||||
(*self.extra.get()).thread_triggers
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn thread_event_callback(&self) -> Option<ThreadEventCallback> {
|
||||
(*self.extra.get()).thread_event_callback.clone()
|
||||
}
|
||||
|
||||
/// Pushes a primitive type value onto the Lua stack.
|
||||
pub(crate) unsafe fn push_primitive_type<T: LuaType>(&self) -> bool {
|
||||
match T::TYPE_ID {
|
||||
@@ -731,14 +757,27 @@ impl RawLua {
|
||||
/// Pushes a value that implements `IntoLua` onto the Lua stack.
|
||||
///
|
||||
/// Uses up to 2 stack spaces to push a single value, does not call `checkstack`.
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
#[inline(always)]
|
||||
pub unsafe fn push(&self, value: impl IntoLua) -> Result<()> {
|
||||
value.push_into_stack(self)
|
||||
}
|
||||
|
||||
/// Pops a value that implements [`FromLua`] from the top of the Lua stack.
|
||||
///
|
||||
/// Uses up to 1 stack space, does not call `checkstack`.
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
#[inline(always)]
|
||||
pub unsafe fn pop<R: FromLua>(&self) -> Result<R> {
|
||||
let v = R::from_stack(-1, self)?;
|
||||
ffi::lua_pop(self.state(), 1);
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
/// Pushes a `Value` (by reference) onto the Lua stack.
|
||||
///
|
||||
/// Uses 2 stack spaces, does not call `checkstack`.
|
||||
/// Uses up to 2 stack spaces, does not call `checkstack`.
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
pub unsafe fn push_value(&self, value: &Value) -> Result<()> {
|
||||
let state = self.state();
|
||||
match value {
|
||||
@@ -773,6 +812,7 @@ impl RawLua {
|
||||
/// Pops a value from the Lua stack.
|
||||
///
|
||||
/// Uses up to 1 stack spaces, does not call `checkstack`.
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
#[inline]
|
||||
pub unsafe fn pop_value(&self) -> Value {
|
||||
let value = self.stack_value(-1, None);
|
||||
@@ -803,15 +843,22 @@ impl RawLua {
|
||||
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
ffi::LUA_TNUMBER => {
|
||||
use crate::types::Number;
|
||||
|
||||
let n = ffi::lua_tonumber(state, idx);
|
||||
match num_traits::cast(n) {
|
||||
Some(i) if n.to_bits() == (i as Number).to_bits() => Value::Integer(i),
|
||||
Some(i) if n.to_bits() == (i as crate::types::Number).to_bits() => Value::Integer(i),
|
||||
_ => Value::Number(n),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::LUA_TINTEGER => {
|
||||
let i = ffi::lua_tointeger64(state, idx, ptr::null_mut());
|
||||
match num_traits::cast(i) {
|
||||
Some(i) => Value::Integer(i),
|
||||
_ => Value::Number(i as crate::types::Number),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::LUA_TVECTOR => {
|
||||
let v = ffi::lua_tovector(state, idx);
|
||||
@@ -824,7 +871,7 @@ impl RawLua {
|
||||
|
||||
ffi::LUA_TSTRING => {
|
||||
ffi::lua_xpush(state, self.ref_thread(), idx);
|
||||
Value::String(String(self.pop_ref_thread()))
|
||||
Value::String(LuaString(self.pop_ref_thread()))
|
||||
}
|
||||
|
||||
ffi::LUA_TTABLE => {
|
||||
@@ -949,7 +996,7 @@ impl RawLua {
|
||||
// Check if userdata/metatable is already registered
|
||||
let type_id = TypeId::of::<T>();
|
||||
if let Some(&table_id) = (*self.extra.get()).registered_userdata_t.get(&type_id) {
|
||||
return Ok(table_id as Integer);
|
||||
return Ok(table_id);
|
||||
}
|
||||
|
||||
// Create a new metatable from `UserData` definition
|
||||
@@ -968,7 +1015,7 @@ impl RawLua {
|
||||
// Check if userdata/metatable is already registered
|
||||
let type_id = TypeId::of::<T>();
|
||||
if let Some(&table_id) = (*self.extra.get()).registered_userdata_t.get(&type_id) {
|
||||
return Ok(table_id as Integer);
|
||||
return Ok(table_id);
|
||||
}
|
||||
|
||||
// Check if metatable creation is pending or create an empty metatable otherwise
|
||||
@@ -983,7 +1030,7 @@ impl RawLua {
|
||||
unsafe fn make_userdata_with_metatable<T>(
|
||||
&self,
|
||||
data: UserDataStorage<T>,
|
||||
get_metatable_id: impl FnOnce() -> Result<Integer>,
|
||||
get_metatable_id: impl FnOnce() -> Result<c_int>,
|
||||
) -> Result<AnyUserData> {
|
||||
let state = self.state();
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -993,7 +1040,7 @@ impl RawLua {
|
||||
let mt_id = get_metatable_id()?;
|
||||
let protect = !self.unlikely_memory_error();
|
||||
push_userdata(state, data, protect)?;
|
||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, mt_id);
|
||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, mt_id as _);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
|
||||
// Set empty environment for Lua 5.1
|
||||
@@ -1011,7 +1058,7 @@ impl RawLua {
|
||||
Ok(AnyUserData(self.pop_ref()))
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn create_userdata_metatable(&self, registry: RawUserDataRegistry) -> Result<Integer> {
|
||||
pub(crate) unsafe fn create_userdata_metatable(&self, registry: RawUserDataRegistry) -> Result<c_int> {
|
||||
let state = self.state();
|
||||
let type_id = registry.type_id;
|
||||
|
||||
@@ -1027,7 +1074,7 @@ impl RawLua {
|
||||
}
|
||||
self.register_userdata_metatable(mt_ptr, type_id);
|
||||
|
||||
Ok(id as Integer)
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn push_userdata_metatable(&self, mut registry: RawUserDataRegistry) -> Result<()> {
|
||||
@@ -1126,7 +1173,7 @@ impl RawLua {
|
||||
#[cfg(feature = "luau")]
|
||||
if registry.enable_namecall {
|
||||
let map: &mut rustc_hash::FxHashMap<_, crate::types::CallbackPtr> =
|
||||
methods_map.get_or_insert_with(Default::default);
|
||||
methods_map.get_or_insert_default();
|
||||
for (k, m) in ®istry.methods {
|
||||
map.insert(k.as_bytes().to_vec(), &**m);
|
||||
}
|
||||
@@ -1219,13 +1266,11 @@ impl RawLua {
|
||||
Ok(type_id) => Ok(type_id),
|
||||
Err(Error::UserDataTypeMismatch) if ffi::lua_type(state, idx) != ffi::LUA_TUSERDATA => {
|
||||
// Report `FromLuaConversionError` instead
|
||||
// In Luau `luaL_typename` return heap-allocated string that is valid only for
|
||||
// the `state` lifetime.
|
||||
// `lua_typename` is used instead to get a truly static string.
|
||||
let idx_type_name = CStr::from_ptr(ffi::lua_typename(state, ffi::lua_type(state, idx)));
|
||||
let idx_type_name = idx_type_name.to_str().unwrap();
|
||||
let type_name = CStr::from_ptr(ffi::lua_typename(state, ffi::lua_type(state, idx)))
|
||||
.to_str()
|
||||
.unwrap_or("unknown");
|
||||
let message = format!("expected userdata of type '{}'", short_type_name::<T>());
|
||||
Err(Error::from_lua_conversion(idx_type_name, "userdata", message))
|
||||
Err(Error::from_lua_conversion(type_name, "userdata", message))
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
@@ -1586,6 +1631,11 @@ unsafe fn load_std_libs(state: *mut ffi::lua_State, libs: StdLib) -> Result<()>
|
||||
requiref(state, ffi::LUA_VECLIBNAME, ffi::luaopen_vector, 1)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
if libs.contains(StdLib::INTEGER) {
|
||||
requiref(state, ffi::LUA_INTLIBNAME, ffi::luaopen_integer, 1)?;
|
||||
}
|
||||
|
||||
if libs.contains(StdLib::MATH) {
|
||||
requiref(state, ffi::LUA_MATHLIBNAME, ffi::luaopen_math, 1)?;
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
use std::os::raw::c_int;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::panic::{AssertUnwindSafe, catch_unwind};
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::{ExtraData, RawLua};
|
||||
use crate::util::{self, get_internal_metatable, WrappedFailure};
|
||||
use crate::util::{self, WrappedFailure, get_internal_metatable};
|
||||
|
||||
struct StateGuard<'a>(&'a RawLua, *mut ffi::lua_State);
|
||||
|
||||
|
||||
+14
-2
@@ -1,4 +1,4 @@
|
||||
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
|
||||
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};
|
||||
|
||||
/// Flags describing the set of lua standard libraries to load.
|
||||
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
@@ -73,10 +73,15 @@ impl StdLib {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub const VECTOR: StdLib = StdLib(1 << 10);
|
||||
|
||||
/// [`integer`](https://luau.org/library#integer-library) library
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub const INTEGER: StdLib = StdLib(1 << 11);
|
||||
|
||||
/// [`jit`](http://luajit.org/ext_jit.html) library
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
|
||||
pub const JIT: StdLib = StdLib(1 << 11);
|
||||
pub const JIT: StdLib = StdLib(1 << 12);
|
||||
|
||||
/// (**unsafe**) [`ffi`](http://luajit.org/ext_ffi.html) library
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
@@ -139,3 +144,10 @@ impl BitXorAssign for StdLib {
|
||||
*self = StdLib(self.0 ^ rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Not for StdLib {
|
||||
type Output = Self;
|
||||
fn not(self) -> Self::Output {
|
||||
StdLib(!self.0)
|
||||
}
|
||||
}
|
||||
|
||||
+88
-85
@@ -1,9 +1,12 @@
|
||||
use std::borrow::{Borrow, Cow};
|
||||
//! Lua string handling.
|
||||
//!
|
||||
//! This module provides types for working with Lua strings from Rust.
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::string::String as StdString;
|
||||
use std::{cmp, fmt, slice, str};
|
||||
use std::{cmp, fmt, mem, slice, str};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::Lua;
|
||||
@@ -20,38 +23,41 @@ use {
|
||||
/// Handle to an internal Lua string.
|
||||
///
|
||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||
#[derive(Clone)]
|
||||
pub struct String(pub(crate) ValueRef);
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct LuaString(pub(crate) ValueRef);
|
||||
|
||||
impl String {
|
||||
impl LuaString {
|
||||
/// Get a [`BorrowedStr`] if the Lua string is valid UTF-8.
|
||||
///
|
||||
/// The returned `BorrowedStr` holds a strong reference to the Lua state to guarantee the
|
||||
/// validity of the underlying data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result, String};
|
||||
/// # use mlua::{Lua, LuaString, Result};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// # let lua = Lua::new();
|
||||
/// let globals = lua.globals();
|
||||
///
|
||||
/// let version: String = globals.get("_VERSION")?;
|
||||
/// let version: LuaString = globals.get("_VERSION")?;
|
||||
/// assert!(version.to_str()?.contains("Lua"));
|
||||
///
|
||||
/// let non_utf8: String = lua.load(r#" "test\255" "#).eval()?;
|
||||
/// let non_utf8: LuaString = lua.load(r#" "test\255" "#).eval()?;
|
||||
/// assert!(non_utf8.to_str().is_err());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_str(&self) -> Result<BorrowedStr<'_>> {
|
||||
pub fn to_str(&self) -> Result<BorrowedStr> {
|
||||
BorrowedStr::try_from(self)
|
||||
}
|
||||
|
||||
/// Converts this string to a [`StdString`].
|
||||
/// Converts this Lua string to a [`String`].
|
||||
///
|
||||
/// Any non-Unicode sequences are replaced with [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
|
||||
///
|
||||
/// This method returns [`StdString`] instead of [`Cow<'_, str>`] because lifetime cannot be
|
||||
/// This method returns [`String`] instead of [`Cow<'_, str>`] because lifetime cannot be
|
||||
/// bound to a weak Lua object.
|
||||
///
|
||||
/// [U+FFFD]: std::char::REPLACEMENT_CHARACTER
|
||||
@@ -70,11 +76,11 @@ impl String {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_string_lossy(&self) -> StdString {
|
||||
StdString::from_utf8_lossy(&self.as_bytes()).into_owned()
|
||||
pub fn to_string_lossy(&self) -> String {
|
||||
String::from_utf8_lossy(&self.as_bytes()).into_owned()
|
||||
}
|
||||
|
||||
/// Returns an object that implements [`Display`] for safely printing a Lua [`String`] that may
|
||||
/// Returns an object that implements [`Display`] for safely printing a [`LuaString`] that may
|
||||
/// contain non-Unicode data.
|
||||
///
|
||||
/// This may perform lossy conversion.
|
||||
@@ -86,32 +92,33 @@ impl String {
|
||||
|
||||
/// Get the bytes that make up this string.
|
||||
///
|
||||
/// The returned slice will not contain the terminating null byte, but will contain any null
|
||||
/// bytes embedded into the Lua string.
|
||||
/// The returned `BorrowedStr` holds a strong reference to the Lua state to guarantee the
|
||||
/// validity of the underlying data. The data will not contain the terminating null byte, but
|
||||
/// will contain any null bytes embedded into the Lua string.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result, String};
|
||||
/// # use mlua::{Lua, LuaString, Result};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// # let lua = Lua::new();
|
||||
/// let non_utf8: String = lua.load(r#" "test\255" "#).eval()?;
|
||||
/// let non_utf8: LuaString = lua.load(r#" "test\255" "#).eval()?;
|
||||
/// assert!(non_utf8.to_str().is_err()); // oh no :(
|
||||
/// assert_eq!(non_utf8.as_bytes(), &b"test\xff"[..]);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> BorrowedBytes<'_> {
|
||||
pub fn as_bytes(&self) -> BorrowedBytes {
|
||||
BorrowedBytes::from(self)
|
||||
}
|
||||
|
||||
/// Get the bytes that make up this string, including the trailing null byte.
|
||||
pub fn as_bytes_with_nul(&self) -> BorrowedBytes<'_> {
|
||||
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(self);
|
||||
pub fn as_bytes_with_nul(&self) -> BorrowedBytes {
|
||||
let BorrowedBytes { buf, vref, _lua } = BorrowedBytes::from(self);
|
||||
// Include the trailing null byte (it's always present but excluded by default)
|
||||
let buf = unsafe { slice::from_raw_parts((*buf).as_ptr(), (*buf).len() + 1) };
|
||||
BorrowedBytes { buf, borrow, _lua }
|
||||
BorrowedBytes { buf, vref, _lua }
|
||||
}
|
||||
|
||||
// Does not return the terminating null byte
|
||||
@@ -135,18 +142,21 @@ impl String {
|
||||
(slice, lua)
|
||||
}
|
||||
|
||||
/// Converts this string to a generic C pointer.
|
||||
/// Converts this Lua string to a generic C pointer.
|
||||
///
|
||||
/// There is no way to convert the pointer back to its original value.
|
||||
///
|
||||
/// Typically this function is used only for hashing and debug information.
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
self.0.to_pointer()
|
||||
// In Lua < 5.4 (excluding Luau), string pointers are NULL
|
||||
// Use alternative approach
|
||||
let lua = self.0.lua.lock();
|
||||
unsafe { ffi::lua_tostring(lua.ref_thread(), self.0.index) as *const c_void }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for String {
|
||||
impl fmt::Debug for LuaString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let bytes = self.as_bytes();
|
||||
// Check if the string is valid utf8
|
||||
@@ -162,12 +172,12 @@ impl fmt::Debug for String {
|
||||
|
||||
// Lua strings are basically `&[u8]` slices, so implement `PartialEq` for anything resembling that.
|
||||
//
|
||||
// This makes our `String` comparable with `Vec<u8>`, `[u8]`, `&str` and `String`.
|
||||
// This makes our `LuaString` comparable with `Vec<u8>`, `[u8]`, `&str` and `String`.
|
||||
//
|
||||
// The only downside is that this disallows a comparison with `Cow<str>`, as that only implements
|
||||
// `AsRef<str>`, which collides with this impl. Requiring `AsRef<str>` would fix that, but limit us
|
||||
// in other ways.
|
||||
impl<T> PartialEq<T> for String
|
||||
impl<T> PartialEq<T> for LuaString
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
@@ -176,43 +186,37 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for String {
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
self.as_bytes() == other.as_bytes()
|
||||
}
|
||||
}
|
||||
impl Eq for LuaString {}
|
||||
|
||||
impl Eq for String {}
|
||||
|
||||
impl<T> PartialOrd<T> for String
|
||||
impl<T> PartialOrd<T> for LuaString
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
|
||||
self.as_bytes().partial_cmp(&other.as_ref())
|
||||
<[u8]>::partial_cmp(&self.as_bytes(), other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for String {
|
||||
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
|
||||
impl PartialOrd for LuaString {
|
||||
fn partial_cmp(&self, other: &LuaString) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for String {
|
||||
fn cmp(&self, other: &String) -> cmp::Ordering {
|
||||
impl Ord for LuaString {
|
||||
fn cmp(&self, other: &LuaString) -> cmp::Ordering {
|
||||
self.as_bytes().cmp(&other.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for String {
|
||||
impl Hash for LuaString {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.as_bytes().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for String {
|
||||
impl Serialize for LuaString {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
@@ -224,7 +228,7 @@ impl Serialize for String {
|
||||
}
|
||||
}
|
||||
|
||||
struct Display<'a>(&'a String);
|
||||
struct Display<'a>(&'a LuaString);
|
||||
|
||||
impl fmt::Display for Display<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
@@ -234,14 +238,14 @@ impl fmt::Display for Display<'_> {
|
||||
}
|
||||
|
||||
/// A borrowed string (`&str`) that holds a strong reference to the Lua state.
|
||||
pub struct BorrowedStr<'a> {
|
||||
pub struct BorrowedStr {
|
||||
// `buf` points to a readonly memory managed by Lua
|
||||
pub(crate) buf: &'a str,
|
||||
pub(crate) borrow: Cow<'a, String>,
|
||||
pub(crate) buf: &'static str,
|
||||
pub(crate) vref: ValueRef,
|
||||
pub(crate) _lua: Lua,
|
||||
}
|
||||
|
||||
impl Deref for BorrowedStr<'_> {
|
||||
impl Deref for BorrowedStr {
|
||||
type Target = str;
|
||||
|
||||
#[inline(always)]
|
||||
@@ -250,33 +254,33 @@ impl Deref for BorrowedStr<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for BorrowedStr<'_> {
|
||||
impl Borrow<str> for BorrowedStr {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &str {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for BorrowedStr<'_> {
|
||||
impl AsRef<str> for BorrowedStr {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BorrowedStr<'_> {
|
||||
impl fmt::Display for BorrowedStr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.buf.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BorrowedStr<'_> {
|
||||
impl fmt::Debug for BorrowedStr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.buf.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq<T> for BorrowedStr<'_>
|
||||
impl<T> PartialEq<T> for BorrowedStr
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
@@ -285,9 +289,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for BorrowedStr<'_> {}
|
||||
impl Eq for BorrowedStr {}
|
||||
|
||||
impl<T> PartialOrd<T> for BorrowedStr<'_>
|
||||
impl<T> PartialOrd<T> for BorrowedStr
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
@@ -296,36 +300,33 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for BorrowedStr<'_> {
|
||||
impl Ord for BorrowedStr {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.buf.cmp(other.buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a String> for BorrowedStr<'a> {
|
||||
impl TryFrom<&LuaString> for BorrowedStr {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn try_from(value: &'a String) -> Result<Self> {
|
||||
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(value);
|
||||
let buf = str::from_utf8(buf).map_err(|e| Error::FromLuaConversionError {
|
||||
from: "string",
|
||||
to: "&str".to_string(),
|
||||
message: Some(e.to_string()),
|
||||
})?;
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
fn try_from(value: &LuaString) -> Result<Self> {
|
||||
let BorrowedBytes { buf, vref, _lua } = BorrowedBytes::from(value);
|
||||
let buf =
|
||||
str::from_utf8(buf).map_err(|e| Error::from_lua_conversion("string", "&str", e.to_string()))?;
|
||||
Ok(Self { buf, vref, _lua })
|
||||
}
|
||||
}
|
||||
|
||||
/// A borrowed byte slice (`&[u8]`) that holds a strong reference to the Lua state.
|
||||
pub struct BorrowedBytes<'a> {
|
||||
pub struct BorrowedBytes {
|
||||
// `buf` points to a readonly memory managed by Lua
|
||||
pub(crate) buf: &'a [u8],
|
||||
pub(crate) borrow: Cow<'a, String>,
|
||||
pub(crate) buf: &'static [u8],
|
||||
pub(crate) vref: ValueRef,
|
||||
pub(crate) _lua: Lua,
|
||||
}
|
||||
|
||||
impl Deref for BorrowedBytes<'_> {
|
||||
impl Deref for BorrowedBytes {
|
||||
type Target = [u8];
|
||||
|
||||
#[inline(always)]
|
||||
@@ -334,27 +335,27 @@ impl Deref for BorrowedBytes<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<[u8]> for BorrowedBytes<'_> {
|
||||
impl Borrow<[u8]> for BorrowedBytes {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &[u8] {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for BorrowedBytes<'_> {
|
||||
impl AsRef<[u8]> for BorrowedBytes {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BorrowedBytes<'_> {
|
||||
impl fmt::Debug for BorrowedBytes {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.buf.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq<T> for BorrowedBytes<'_>
|
||||
impl<T> PartialEq<T> for BorrowedBytes
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
@@ -363,9 +364,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for BorrowedBytes<'_> {}
|
||||
impl Eq for BorrowedBytes {}
|
||||
|
||||
impl<T> PartialOrd<T> for BorrowedBytes<'_>
|
||||
impl<T> PartialOrd<T> for BorrowedBytes
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
@@ -374,13 +375,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for BorrowedBytes<'_> {
|
||||
impl Ord for BorrowedBytes {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.buf.cmp(other.buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a BorrowedBytes<'_> {
|
||||
impl<'a> IntoIterator for &'a BorrowedBytes {
|
||||
type Item = &'a u8;
|
||||
type IntoIter = slice::Iter<'a, u8>;
|
||||
|
||||
@@ -389,18 +390,20 @@ impl<'a> IntoIterator for &'a BorrowedBytes<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a String> for BorrowedBytes<'a> {
|
||||
impl From<&LuaString> for BorrowedBytes {
|
||||
#[inline]
|
||||
fn from(value: &'a String) -> Self {
|
||||
fn from(value: &LuaString) -> Self {
|
||||
let (buf, _lua) = unsafe { value.to_slice() };
|
||||
let borrow = Cow::Borrowed(value);
|
||||
Self { buf, borrow, _lua }
|
||||
let vref = value.0.clone();
|
||||
// SAFETY: The `buf` is valid for the lifetime of the Lua state and occupied slot index
|
||||
let buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
|
||||
Self { buf, vref, _lua }
|
||||
}
|
||||
}
|
||||
|
||||
struct WrappedString<T: AsRef<[u8]>>(T);
|
||||
|
||||
impl String {
|
||||
impl LuaString {
|
||||
/// Wraps bytes, returning an opaque type that implements [`IntoLua`] trait.
|
||||
///
|
||||
/// This function uses [`Lua::create_string`] under the hood.
|
||||
@@ -415,7 +418,7 @@ impl<T: AsRef<[u8]>> IntoLua for WrappedString<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaType for String {
|
||||
impl LuaType for LuaString {
|
||||
const TYPE_ID: c_int = ffi::LUA_TSTRING;
|
||||
}
|
||||
|
||||
@@ -424,9 +427,9 @@ mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(String: Send);
|
||||
static_assertions::assert_not_impl_any!(LuaString: Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(String: Send, Sync);
|
||||
static_assertions::assert_impl_all!(LuaString: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(BorrowedBytes: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
|
||||
+168
-19
@@ -1,15 +1,167 @@
|
||||
//! Lua table handling.
|
||||
//!
|
||||
//! Tables are Lua's primary data structure, used for arrays, dictionaries, objects, modules,
|
||||
//! and more. This module provides types for creating and manipulating Lua tables from Rust.
|
||||
//!
|
||||
//! # Basic Operations
|
||||
//!
|
||||
//! Tables support key-value access similar to Rust's `HashMap`:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Lua, Result};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//! let table = lua.create_table()?;
|
||||
//!
|
||||
//! // Set and get values
|
||||
//! table.set("key", "value")?;
|
||||
//! let value: String = table.get("key")?;
|
||||
//! assert_eq!(value, "value");
|
||||
//!
|
||||
//! // Keys and values can be any Lua-compatible type
|
||||
//! table.set(1, "first")?;
|
||||
//! table.set("nested", lua.create_table()?)?;
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Array Operations
|
||||
//!
|
||||
//! Tables can be used as arrays with 1-based indexing:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Lua, Result};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//! let array = lua.create_table()?;
|
||||
//!
|
||||
//! // Push values to the end (like Vec::push)
|
||||
//! array.push("first")?;
|
||||
//! array.push("second")?;
|
||||
//! array.push("third")?;
|
||||
//!
|
||||
//! // Pop from the end
|
||||
//! let last: String = array.pop()?;
|
||||
//! assert_eq!(last, "third");
|
||||
//!
|
||||
//! // Get length
|
||||
//! assert_eq!(array.raw_len(), 2);
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Iteration
|
||||
//!
|
||||
//! Iterate over all key-value pairs with [`Table::pairs`]:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Lua, Result, Value};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//! let table = lua.create_table()?;
|
||||
//! table.set("a", 1)?;
|
||||
//! table.set("b", 2)?;
|
||||
//!
|
||||
//! for pair in table.pairs::<String, i32>() {
|
||||
//! let (key, value) = pair?;
|
||||
//! println!("{key} = {value}");
|
||||
//! }
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! For array portions, use [`Table::sequence_values`]:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Lua, Result};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//! let array = lua.create_sequence_from(["a", "b", "c"])?;
|
||||
//!
|
||||
//! for value in array.sequence_values::<String>() {
|
||||
//! println!("{}", value?);
|
||||
//! }
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Raw vs Normal Access
|
||||
//!
|
||||
//! Methods prefixed with `raw_` (like [`Table::raw_get`], [`Table::raw_set`]) bypass
|
||||
//! metamethods, directly accessing the table's contents. Normal methods may trigger
|
||||
//! `__index`, `__newindex`, and other metamethods:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Lua, Result};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//!
|
||||
//! // raw_set bypasses __newindex metamethod
|
||||
//! let t = lua.create_table()?;
|
||||
//! t.raw_set("key", "value")?;
|
||||
//!
|
||||
//! // raw_get bypasses __index metamethod
|
||||
//! let v: String = t.raw_get("key")?;
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Metatables
|
||||
//!
|
||||
//! Tables can have metatables that customize their behavior:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Lua, Result};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//!
|
||||
//! let table = lua.create_table()?;
|
||||
//! let metatable = lua.create_table()?;
|
||||
//!
|
||||
//! // Set a default value via __index
|
||||
//! metatable.set("__index", lua.create_function(|_, _: ()| Ok("default"))?)?;
|
||||
//! table.set_metatable(Some(metatable))?;
|
||||
//!
|
||||
//! // Accessing missing keys returns "default"
|
||||
//! let value: String = table.get("missing")?;
|
||||
//! assert_eq!(value, "default");
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Global Table
|
||||
//!
|
||||
//! The Lua global environment is itself a table, accessible via [`Lua::globals`]:
|
||||
//!
|
||||
//! ```
|
||||
//! # use mlua::{Lua, Result};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//! let globals = lua.globals();
|
||||
//!
|
||||
//! // Set a global variable
|
||||
//! globals.set("my_var", 42)?;
|
||||
//!
|
||||
//! // Now accessible from Lua code
|
||||
//! let result: i32 = lua.load("my_var + 8").eval()?;
|
||||
//! assert_eq!(result, 50);
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! [`Lua::globals`]: crate::Lua::globals
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_void;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{LuaGuard, RawLua, WeakLua};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
|
||||
use crate::types::{Integer, ValueRef};
|
||||
use crate::util::{assert_stack, check_stack, get_metatable_ptr, StackGuard};
|
||||
use crate::util::{StackGuard, assert_stack, check_stack, get_metatable_ptr};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -226,15 +378,15 @@ impl Table {
|
||||
// Compare using `__eq` metamethod if exists
|
||||
// First, check the self for the metamethod.
|
||||
// If self does not define it, then check the other table.
|
||||
if let Some(mt) = self.metatable() {
|
||||
if mt.contains_key("__eq")? {
|
||||
return mt.get::<Function>("__eq")?.call((self, other));
|
||||
}
|
||||
if let Some(mt) = self.metatable()
|
||||
&& let Some(eq_func) = mt.get::<Option<Function>>("__eq")?
|
||||
{
|
||||
return eq_func.call((self, other));
|
||||
}
|
||||
if let Some(mt) = other.metatable() {
|
||||
if mt.contains_key("__eq")? {
|
||||
return mt.get::<Function>("__eq")?.call((self, other));
|
||||
}
|
||||
if let Some(mt) = other.metatable()
|
||||
&& let Some(eq_func) = mt.get::<Option<Function>>("__eq")?
|
||||
{
|
||||
return eq_func.call((self, other));
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
@@ -626,10 +778,8 @@ impl Table {
|
||||
ffi::lua_pushnil(state);
|
||||
while ffi::lua_next(state, -2) != 0 {
|
||||
let k = K::from_stack(-2, &lua)?;
|
||||
let v = V::from_stack(-1, &lua)?;
|
||||
let v = lua.pop::<V>()?;
|
||||
f(k, v)?;
|
||||
// Keep key for next iteration
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -702,8 +852,7 @@ impl Table {
|
||||
if len.is_none() && t == ffi::LUA_TNIL {
|
||||
break;
|
||||
}
|
||||
f(V::from_stack(-1, &lua)?)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
f(lua.pop::<V>()?)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -1008,7 +1157,7 @@ impl ObjectLike for Table {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_string(&self) -> Result<StdString> {
|
||||
fn to_string(&self) -> Result<String> {
|
||||
Value::Table(Table(self.0.clone())).to_string()
|
||||
}
|
||||
|
||||
@@ -1070,7 +1219,7 @@ impl Serialize for SerializableTable<'_> {
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
use crate::serde::de::{check_value_for_skip, MapPairs, RecursionGuard};
|
||||
use crate::serde::de::{MapPairs, RecursionGuard, check_value_for_skip};
|
||||
use crate::value::SerializableValue;
|
||||
|
||||
let convert_result = |res: Result<()>, serialize_err: Option<S::Error>| match res {
|
||||
@@ -1098,7 +1247,7 @@ impl Serialize for SerializableTable<'_> {
|
||||
seq.serialize_element(&SerializableValue::new(&value, options, Some(visited)))
|
||||
.map_err(|err| {
|
||||
serialize_err = Some(err);
|
||||
Error::SerializeError(StdString::new())
|
||||
Error::SerializeError(String::new())
|
||||
})
|
||||
});
|
||||
convert_result(res, serialize_err)?;
|
||||
@@ -1123,7 +1272,7 @@ impl Serialize for SerializableTable<'_> {
|
||||
)
|
||||
.map_err(|err| {
|
||||
serialize_err = Some(err);
|
||||
Error::SerializeError(StdString::new())
|
||||
Error::SerializeError(String::new())
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
+218
-14
@@ -1,3 +1,40 @@
|
||||
//! Lua thread (coroutine) handling.
|
||||
//!
|
||||
//! This module provides types for creating and working with Lua coroutines from Rust.
|
||||
//! Coroutines allow cooperative multitasking within a single Lua state by suspending and
|
||||
//! resuming execution at well-defined yield points.
|
||||
//!
|
||||
//! # Basic Usage
|
||||
//!
|
||||
//! Threads are created via [`Lua::create_thread`] and driven by calling [`Thread::resume`]:
|
||||
//!
|
||||
//! ```rust
|
||||
//! # use mlua::{Lua, Result, Thread};
|
||||
//! # fn main() -> Result<()> {
|
||||
//! let lua = Lua::new();
|
||||
//! let thread: Thread = lua.load(r#"
|
||||
//! coroutine.create(function(a, b)
|
||||
//! coroutine.yield(a + b)
|
||||
//! return a * b
|
||||
//! end)
|
||||
//! "#).eval()?;
|
||||
//!
|
||||
//! assert_eq!(thread.resume::<i32>((3, 4))?, 7);
|
||||
//! assert_eq!(thread.resume::<i32>(())?, 12);
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Async Support
|
||||
//!
|
||||
//! When the `async` feature is enabled, a [`Thread`] can be converted into an [`AsyncThread`]
|
||||
//! via [`Thread::into_async`], which implements both [`Future`] and [`Stream`].
|
||||
//! This integrates Lua coroutines naturally with Rust async runtimes such as Tokio.
|
||||
//!
|
||||
//! [`Lua::create_thread`]: crate::Lua::create_thread
|
||||
//! [`Future`]: std::future::Future
|
||||
//! [`Stream`]: futures_util::stream::Stream
|
||||
|
||||
use std::fmt;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
|
||||
@@ -5,8 +42,8 @@ use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::RawLua;
|
||||
use crate::traits::{FromLuaMulti, IntoLuaMulti};
|
||||
use crate::types::{LuaType, ValueRef};
|
||||
use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
|
||||
use crate::types::{LuaType, ValueRef, XRc};
|
||||
use crate::util::{StackGuard, check_stack, error_traceback_thread, pop_error};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use crate::{
|
||||
@@ -26,6 +63,85 @@ use {
|
||||
},
|
||||
};
|
||||
|
||||
/// Controls which thread lifecycle events trigger the callback.
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[non_exhaustive]
|
||||
pub struct ThreadTriggers {
|
||||
/// Trigger the callback when a new thread is created.
|
||||
pub on_create: bool,
|
||||
/// Trigger the callback before a thread is resumed (via [`Thread::resume`]).
|
||||
pub on_resume: bool,
|
||||
/// Trigger the callback after a thread yields.
|
||||
pub on_yield: bool,
|
||||
}
|
||||
|
||||
impl ThreadTriggers {
|
||||
/// An instance of [`ThreadTriggers`] with `on_create` trigger set.
|
||||
pub const ON_CREATE: Self = Self::new().on_create();
|
||||
|
||||
/// An instance of [`ThreadTriggers`] with `on_resume` trigger set.
|
||||
pub const ON_RESUME: Self = Self::new().on_resume();
|
||||
|
||||
/// An instance of [`ThreadTriggers`] with `on_yield` trigger set.
|
||||
pub const ON_YIELD: Self = Self::new().on_yield();
|
||||
|
||||
/// Returns a new instance of `ThreadTriggers` with all triggers disabled.
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
on_create: false,
|
||||
on_resume: false,
|
||||
on_yield: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an instance of `ThreadTriggers` with `on_create` trigger set.
|
||||
pub const fn on_create(mut self) -> Self {
|
||||
self.on_create = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns an instance of `ThreadTriggers` with `on_resume` trigger set.
|
||||
pub const fn on_resume(mut self) -> Self {
|
||||
self.on_resume = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns an instance of `ThreadTriggers` with `on_yield` trigger set.
|
||||
pub const fn on_yield(mut self) -> Self {
|
||||
self.on_yield = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::BitOr for ThreadTriggers {
|
||||
type Output = Self;
|
||||
|
||||
fn bitor(mut self, rhs: Self) -> Self::Output {
|
||||
self.on_create |= rhs.on_create;
|
||||
self.on_resume |= rhs.on_resume;
|
||||
self.on_yield |= rhs.on_yield;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::BitOrAssign for ThreadTriggers {
|
||||
fn bitor_assign(&mut self, rhs: Self) {
|
||||
*self = *self | rhs;
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a thread (coroutine) event.
|
||||
#[derive(Debug, Clone)]
|
||||
#[non_exhaustive]
|
||||
pub enum ThreadEvent {
|
||||
/// A new thread was created.
|
||||
Create(Thread),
|
||||
/// A thread is about to be resumed via [`Thread::resume`].
|
||||
Resume(Thread),
|
||||
/// A thread has just yielded.
|
||||
Yield(Thread),
|
||||
}
|
||||
|
||||
/// Status of a Lua thread (coroutine).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum ThreadStatus {
|
||||
@@ -61,7 +177,6 @@ impl ThreadStatusInner {
|
||||
matches!(self, ThreadStatusInner::New(_) | ThreadStatusInner::Yielded(_))
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline(always)]
|
||||
fn is_yielded(self) -> bool {
|
||||
matches!(self, ThreadStatusInner::Yielded(_))
|
||||
@@ -69,7 +184,7 @@ impl ThreadStatusInner {
|
||||
}
|
||||
|
||||
/// Handle to an internal Lua thread (coroutine).
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Thread(pub(crate) ValueRef, pub(crate) *mut ffi::lua_State);
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
@@ -92,7 +207,6 @@ pub struct AsyncThread<R> {
|
||||
|
||||
impl Thread {
|
||||
/// Returns reference to the Lua state that this thread is associated with.
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
pub fn state(&self) -> *mut ffi::lua_State {
|
||||
self.1
|
||||
@@ -157,6 +271,14 @@ impl Thread {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
|
||||
// Exec thread resume callback
|
||||
if lua.thread_event_triggers().on_resume
|
||||
&& let Some(cb) = lua.thread_event_callback()
|
||||
&& XRc::strong_count(&cb) <= 2
|
||||
{
|
||||
cb(lua.lua(), ThreadEvent::Resume(self.clone()))?;
|
||||
}
|
||||
|
||||
let nargs = args.push_into_stack_multi(&lua)?;
|
||||
if nargs > 0 {
|
||||
check_stack(thread_state, nargs)?;
|
||||
@@ -165,7 +287,17 @@ impl Thread {
|
||||
}
|
||||
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
let (_, nresults) = self.resume_inner(&lua, pushed_nargs)?;
|
||||
let (status, nresults) = self.resume_inner(&lua, pushed_nargs)?;
|
||||
|
||||
// Exec thread yield callback
|
||||
if lua.thread_event_triggers().on_yield
|
||||
&& status.is_yielded()
|
||||
&& let Some(cb) = lua.thread_event_callback()
|
||||
&& XRc::strong_count(&cb) <= 2
|
||||
{
|
||||
cb(lua.lua(), ThreadEvent::Yield(self.clone()))?;
|
||||
}
|
||||
|
||||
check_stack(state, nresults + 1)?;
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
|
||||
@@ -193,12 +325,30 @@ impl Thread {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
|
||||
// Exec thread resume callback
|
||||
if lua.thread_event_triggers().on_resume
|
||||
&& let Some(cb) = lua.thread_event_callback()
|
||||
&& XRc::strong_count(&cb) <= 2
|
||||
{
|
||||
cb(lua.lua(), ThreadEvent::Resume(self.clone()))?;
|
||||
}
|
||||
|
||||
check_stack(state, 1)?;
|
||||
error.push_into_stack(&lua)?;
|
||||
ffi::lua_xmove(state, thread_state, 1);
|
||||
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
let (_, nresults) = self.resume_inner(&lua, ffi::LUA_RESUMEERROR)?;
|
||||
let (status, nresults) = self.resume_inner(&lua, ffi::LUA_RESUMEERROR)?;
|
||||
|
||||
// Exec thread yield callback
|
||||
if lua.thread_event_triggers().on_yield
|
||||
&& status.is_yielded()
|
||||
&& let Some(cb) = lua.thread_event_callback()
|
||||
&& XRc::strong_count(&cb) <= 2
|
||||
{
|
||||
cb(lua.lua(), ThreadEvent::Yield(self.clone()))?;
|
||||
}
|
||||
|
||||
check_stack(state, nresults + 1)?;
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
|
||||
@@ -259,6 +409,31 @@ impl Thread {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this thread is resumable (meaning it can be resumed by calling
|
||||
/// [`Thread::resume`]).
|
||||
#[inline(always)]
|
||||
pub fn is_resumable(&self) -> bool {
|
||||
self.status() == ThreadStatus::Resumable
|
||||
}
|
||||
|
||||
/// Returns `true` if this thread is currently running.
|
||||
#[inline(always)]
|
||||
pub fn is_running(&self) -> bool {
|
||||
self.status() == ThreadStatus::Running
|
||||
}
|
||||
|
||||
/// Returns `true` if this thread has finished executing.
|
||||
#[inline(always)]
|
||||
pub fn is_finished(&self) -> bool {
|
||||
self.status() == ThreadStatus::Finished
|
||||
}
|
||||
|
||||
/// Returns `true` if this thread has raised a Lua error during execution.
|
||||
#[inline(always)]
|
||||
pub fn is_error(&self) -> bool {
|
||||
self.status() == ThreadStatus::Error
|
||||
}
|
||||
|
||||
/// Sets a hook function that will periodically be called as Lua code executes.
|
||||
///
|
||||
/// This function is similar or [`Lua::set_hook`] except that it sets for the thread.
|
||||
@@ -295,7 +470,7 @@ impl Thread {
|
||||
/// Resets a thread
|
||||
///
|
||||
/// In [Lua 5.4]: cleans its call stack and closes all pending to-be-closed variables.
|
||||
/// Returns a error in case of either the original error that stopped the thread or errors
|
||||
/// Returns an error in case of either the original error that stopped the thread or errors
|
||||
/// in closing methods.
|
||||
///
|
||||
/// In Luau: resets to the initial state of a newly created Lua thread.
|
||||
@@ -449,6 +624,8 @@ impl Thread {
|
||||
/// Please note that Luau links environment table with chunk when loading it into Lua state.
|
||||
/// Therefore you need to load chunks into a thread to link with the thread environment.
|
||||
///
|
||||
/// [`Lua::sandbox`]: crate::Lua::sandbox
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@@ -502,12 +679,6 @@ impl fmt::Debug for Thread {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Thread {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaType for Thread {
|
||||
const TYPE_ID: c_int = ffi::LUA_TTHREAD;
|
||||
}
|
||||
@@ -523,6 +694,7 @@ impl<R> AsyncThread<R> {
|
||||
#[cfg(feature = "async")]
|
||||
impl<R> Drop for AsyncThread<R> {
|
||||
fn drop(&mut self) {
|
||||
#[allow(clippy::collapsible_if)]
|
||||
if self.recycle {
|
||||
if let Some(lua) = self.thread.0.lua.try_lock() {
|
||||
unsafe {
|
||||
@@ -564,9 +736,25 @@ impl<R: FromLuaMulti> Stream for AsyncThread<R> {
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
let _wg = WakerGuard::new(&lua, cx.waker());
|
||||
|
||||
// Exec thread resume callback
|
||||
if lua.thread_event_triggers().on_resume
|
||||
&& let Some(cb) = lua.thread_event_callback()
|
||||
&& XRc::strong_count(&cb) <= 2
|
||||
{
|
||||
cb(lua.lua(), ThreadEvent::Resume(self.thread.clone()))?;
|
||||
}
|
||||
|
||||
let (status, nresults) = (self.thread).resume_inner(&lua, nargs)?;
|
||||
|
||||
if status.is_yielded() {
|
||||
// Exec thread yield callback
|
||||
if lua.thread_event_triggers().on_yield
|
||||
&& let Some(cb) = lua.thread_event_callback()
|
||||
&& XRc::strong_count(&cb) <= 2
|
||||
{
|
||||
cb(lua.lua(), ThreadEvent::Yield(self.thread.clone()))?;
|
||||
}
|
||||
|
||||
if nresults == 1 && is_poll_pending(thread_state) {
|
||||
return Poll::Pending;
|
||||
}
|
||||
@@ -600,9 +788,25 @@ impl<R: FromLuaMulti> Future for AsyncThread<R> {
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
let _wg = WakerGuard::new(&lua, cx.waker());
|
||||
|
||||
// Exec thread resume callback
|
||||
if lua.thread_event_triggers().on_resume
|
||||
&& let Some(cb) = lua.thread_event_callback()
|
||||
&& XRc::strong_count(&cb) <= 2
|
||||
{
|
||||
cb(lua.lua(), ThreadEvent::Resume(self.thread.clone()))?;
|
||||
}
|
||||
|
||||
let (status, nresults) = self.thread.resume_inner(&lua, nargs)?;
|
||||
|
||||
if status.is_yielded() {
|
||||
// Exec thread yield callback
|
||||
if lua.thread_event_triggers().on_yield
|
||||
&& let Some(cb) = lua.thread_event_callback()
|
||||
&& XRc::strong_count(&cb) <= 2
|
||||
{
|
||||
cb(lua.lua(), ThreadEvent::Yield(self.thread.clone()))?;
|
||||
}
|
||||
|
||||
if !(nresults == 1 && is_poll_pending(thread_state)) {
|
||||
// Ignore values returned via yield()
|
||||
cx.waker().wake_by_ref();
|
||||
|
||||
+8
-96
@@ -1,17 +1,20 @@
|
||||
//! Core conversion and extension traits.
|
||||
//!
|
||||
//! This module provides the fundamental traits for converting values between Rust and Lua,
|
||||
//! and for defining native Lua callable functions.
|
||||
|
||||
use std::os::raw::c_int;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::multi::MultiValue;
|
||||
use crate::private::Sealed;
|
||||
use crate::state::{Lua, RawLua, WeakLua};
|
||||
use crate::types::MaybeSend;
|
||||
use crate::util::{check_stack, parse_lookup_path, short_type_name};
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {crate::function::AsyncCallFuture, std::future::Future};
|
||||
use crate::function::AsyncCallFuture;
|
||||
|
||||
/// Trait for types convertible to [`Value`].
|
||||
pub trait IntoLua: Sized {
|
||||
@@ -236,7 +239,7 @@ pub trait ObjectLike: Sealed {
|
||||
/// Converts the object to a string in a human-readable format.
|
||||
///
|
||||
/// This might invoke the `__tostring` metamethod.
|
||||
fn to_string(&self) -> Result<StdString>;
|
||||
fn to_string(&self) -> Result<String>;
|
||||
|
||||
/// Converts the object to a Lua value.
|
||||
fn to_value(&self) -> Value;
|
||||
@@ -246,100 +249,9 @@ pub trait ObjectLike: Sealed {
|
||||
fn weak_lua(&self) -> &WeakLua;
|
||||
}
|
||||
|
||||
/// A trait for types that can be used as Lua functions.
|
||||
pub trait LuaNativeFn<A: FromLuaMulti> {
|
||||
type Output: IntoLuaMulti;
|
||||
|
||||
fn call(&self, args: A) -> Self::Output;
|
||||
}
|
||||
|
||||
/// A trait for types with mutable state that can be used as Lua functions.
|
||||
pub trait LuaNativeFnMut<A: FromLuaMulti> {
|
||||
type Output: IntoLuaMulti;
|
||||
|
||||
fn call(&mut self, args: A) -> Self::Output;
|
||||
}
|
||||
|
||||
/// A trait for types that returns a future and can be used as Lua functions.
|
||||
#[cfg(feature = "async")]
|
||||
pub trait LuaNativeAsyncFn<A: FromLuaMulti> {
|
||||
type Output: IntoLuaMulti;
|
||||
|
||||
fn call(&self, args: A) -> impl Future<Output = Self::Output> + MaybeSend + 'static;
|
||||
}
|
||||
|
||||
macro_rules! impl_lua_native_fn {
|
||||
($($A:ident),*) => {
|
||||
impl<FN, $($A,)* R> LuaNativeFn<($($A,)*)> for FN
|
||||
where
|
||||
FN: Fn($($A,)*) -> R + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&self, args: ($($A,)*)) -> Self::Output {
|
||||
let ($($A,)*) = args;
|
||||
self($($A,)*)
|
||||
}
|
||||
}
|
||||
|
||||
impl<FN, $($A,)* R> LuaNativeFnMut<($($A,)*)> for FN
|
||||
where
|
||||
FN: FnMut($($A,)*) -> R + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&mut self, args: ($($A,)*)) -> Self::Output {
|
||||
let ($($A,)*) = args;
|
||||
self($($A,)*)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<FN, $($A,)* Fut, R> LuaNativeAsyncFn<($($A,)*)> for FN
|
||||
where
|
||||
FN: Fn($($A,)*) -> Fut + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
Fut: Future<Output = R> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&self, args: ($($A,)*)) -> impl Future<Output = Self::Output> + MaybeSend + 'static {
|
||||
let ($($A,)*) = args;
|
||||
self($($A,)*)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_lua_native_fn!();
|
||||
impl_lua_native_fn!(A);
|
||||
impl_lua_native_fn!(A, B);
|
||||
impl_lua_native_fn!(A, B, C);
|
||||
impl_lua_native_fn!(A, B, C, D);
|
||||
impl_lua_native_fn!(A, B, C, D, E);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
|
||||
|
||||
pub(crate) trait ShortTypeName {
|
||||
#[inline(always)]
|
||||
fn type_name() -> StdString {
|
||||
fn type_name() -> String {
|
||||
short_type_name::<Self>()
|
||||
}
|
||||
}
|
||||
|
||||
+16
-10
@@ -96,17 +96,11 @@ pub(crate) type InterruptCallback = XRc<dyn Fn(&Lua) -> Result<VmState> + Send>;
|
||||
#[cfg(all(not(feature = "send"), feature = "luau"))]
|
||||
pub(crate) type InterruptCallback = XRc<dyn Fn(&Lua) -> Result<VmState>>;
|
||||
|
||||
#[cfg(all(feature = "send", feature = "luau"))]
|
||||
pub(crate) type ThreadCreationCallback = XRc<dyn Fn(&Lua, crate::Thread) -> Result<()> + Send>;
|
||||
#[cfg(feature = "send")]
|
||||
pub(crate) type ThreadEventCallback = XRc<dyn Fn(&Lua, crate::thread::ThreadEvent) -> Result<()> + Send>;
|
||||
|
||||
#[cfg(all(not(feature = "send"), feature = "luau"))]
|
||||
pub(crate) type ThreadCreationCallback = XRc<dyn Fn(&Lua, crate::Thread) -> Result<()>>;
|
||||
|
||||
#[cfg(all(feature = "send", feature = "luau"))]
|
||||
pub(crate) type ThreadCollectionCallback = XRc<dyn Fn(crate::LightUserData) + Send>;
|
||||
|
||||
#[cfg(all(not(feature = "send"), feature = "luau"))]
|
||||
pub(crate) type ThreadCollectionCallback = XRc<dyn Fn(crate::LightUserData)>;
|
||||
#[cfg(not(feature = "send"))]
|
||||
pub(crate) type ThreadEventCallback = XRc<dyn Fn(&Lua, crate::thread::ThreadEvent) -> Result<()>>;
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
#[cfg(any(feature = "lua55", feature = "lua54"))]
|
||||
@@ -128,6 +122,18 @@ pub trait MaybeSend {}
|
||||
#[cfg(not(feature = "send"))]
|
||||
impl<T> MaybeSend for T {}
|
||||
|
||||
/// A trait that adds `Sync` requirement if `send` feature is enabled.
|
||||
#[cfg(feature = "send")]
|
||||
pub trait MaybeSync: Sync {}
|
||||
#[cfg(feature = "send")]
|
||||
impl<T: Sync> MaybeSync for T {}
|
||||
|
||||
/// A trait that adds `Sync` requirement if `send` feature is enabled.
|
||||
#[cfg(not(feature = "send"))]
|
||||
pub trait MaybeSync {}
|
||||
#[cfg(not(feature = "send"))]
|
||||
impl<T> MaybeSync for T {}
|
||||
|
||||
pub(crate) struct DestructedUserdata;
|
||||
|
||||
pub(crate) trait LuaType {
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ mod inner {
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0 .0
|
||||
&self.0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,10 +55,10 @@ impl Drop for ValueRef {
|
||||
if let Some(ValueRefIndex(index)) = self.index_count.take() {
|
||||
// It's guaranteed that the inner value returns exactly once.
|
||||
// This means in particular that the value is not dropped.
|
||||
if XRc::into_inner(index).is_some() {
|
||||
if let Some(lua) = self.lua.try_lock() {
|
||||
unsafe { lua.drop_ref(self) };
|
||||
}
|
||||
if XRc::into_inner(index).is_some()
|
||||
&& let Some(lua) = self.lua.try_lock()
|
||||
{
|
||||
unsafe { lua.drop_ref(self) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+93
-43
@@ -1,18 +1,22 @@
|
||||
//! Lua userdata handling.
|
||||
//!
|
||||
//! This module provides types for creating and working with Lua userdata from Rust.
|
||||
|
||||
use std::any::TypeId;
|
||||
use std::ffi::CStr;
|
||||
use std::fmt;
|
||||
use std::hash::Hash;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::Either;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::Lua;
|
||||
use crate::string::String;
|
||||
use crate::string::LuaString;
|
||||
use crate::table::{Table, TablePairs};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::types::{MaybeSend, ValueRef};
|
||||
use crate::util::{check_stack, get_userdata, push_string, short_type_name, take_userdata, StackGuard};
|
||||
use crate::types::{MaybeSend, MaybeSync, ValueRef};
|
||||
use crate::util::{StackGuard, check_stack, get_userdata, push_string, short_type_name, take_userdata};
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -26,12 +30,12 @@ use {
|
||||
|
||||
// Re-export for convenience
|
||||
pub(crate) use cell::UserDataStorage;
|
||||
pub use r#ref::{UserDataRef, UserDataRefMut};
|
||||
pub use r#ref::{UserDataOwned, UserDataRef, UserDataRefMut};
|
||||
pub use registry::UserDataRegistry;
|
||||
pub(crate) use registry::{RawUserDataRegistry, UserDataProxy};
|
||||
pub(crate) use util::{
|
||||
borrow_userdata_scoped, borrow_userdata_scoped_mut, collect_userdata, init_userdata_metatable,
|
||||
TypeIdHints,
|
||||
TypeIdHints, borrow_userdata_scoped, borrow_userdata_scoped_mut, collect_userdata,
|
||||
init_userdata_metatable,
|
||||
};
|
||||
|
||||
/// Kinds of metamethods that can be overridden.
|
||||
@@ -124,6 +128,11 @@ pub enum MetaMethod {
|
||||
///
|
||||
/// This is not an operator, but will be called by methods such as `tostring` and `print`.
|
||||
ToString,
|
||||
/// The `__todebugstring` metamethod for debug purposes.
|
||||
///
|
||||
/// This is an mlua-specific metamethod that can be used to provide debug representation for
|
||||
/// userdata.
|
||||
ToDebugString,
|
||||
/// The `__pairs` metamethod.
|
||||
///
|
||||
/// This is not an operator, but it will be called by the built-in `pairs` function.
|
||||
@@ -185,7 +194,7 @@ impl PartialEq<MetaMethod> for &str {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<MetaMethod> for StdString {
|
||||
impl PartialEq<MetaMethod> for String {
|
||||
fn eq(&self, other: &MetaMethod) -> bool {
|
||||
self == other.name()
|
||||
}
|
||||
@@ -233,6 +242,7 @@ impl MetaMethod {
|
||||
MetaMethod::NewIndex => "__newindex",
|
||||
MetaMethod::Call => "__call",
|
||||
MetaMethod::ToString => "__tostring",
|
||||
MetaMethod::ToDebugString => "__todebugstring",
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua55",
|
||||
@@ -279,7 +289,7 @@ impl AsRef<str> for MetaMethod {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MetaMethod> for StdString {
|
||||
impl From<MetaMethod> for String {
|
||||
#[inline]
|
||||
fn from(method: MetaMethod) -> Self {
|
||||
method.name().to_owned()
|
||||
@@ -295,7 +305,7 @@ pub trait UserDataMethods<T> {
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
|
||||
/// be used as a fall-back if no regular method is found.
|
||||
fn add_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -306,7 +316,7 @@ pub trait UserDataMethods<T> {
|
||||
/// Refer to [`add_method`] for more information about the implementation.
|
||||
///
|
||||
/// [`add_method`]: UserDataMethods::add_method
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -319,8 +329,7 @@ pub trait UserDataMethods<T> {
|
||||
///
|
||||
/// The method can be called only once per userdata instance, subsequent calls will result in a
|
||||
/// [`Error::UserDataDestructed`] error.
|
||||
#[doc(hidden)]
|
||||
fn add_method_once<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_method_once<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(&Lua, T, A) -> Result<R> + MaybeSend + 'static,
|
||||
@@ -342,7 +351,7 @@ pub trait UserDataMethods<T> {
|
||||
/// [`add_method`]: UserDataMethods::add_method
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -357,7 +366,7 @@ pub trait UserDataMethods<T> {
|
||||
/// [`add_method`]: UserDataMethods::add_method
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -374,8 +383,7 @@ pub trait UserDataMethods<T> {
|
||||
/// [`Error::UserDataDestructed`] error.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
#[doc(hidden)]
|
||||
fn add_async_method_once<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_method_once<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, T, A) -> MR + MaybeSend + 'static,
|
||||
@@ -398,7 +406,7 @@ pub trait UserDataMethods<T> {
|
||||
/// The first argument will be a [`AnyUserData`] of type `T` if the method is called with Lua
|
||||
/// method syntax: `my_userdata:my_method(arg1, arg2)`, or it is passed in as the first
|
||||
/// argument: `my_userdata.my_method(my_userdata, arg1, arg2)`.
|
||||
fn add_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_function<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -409,7 +417,7 @@ pub trait UserDataMethods<T> {
|
||||
/// This is a version of [`add_function`] that accepts a `FnMut` argument.
|
||||
///
|
||||
/// [`add_function`]: UserDataMethods::add_function
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -423,7 +431,7 @@ pub trait UserDataMethods<T> {
|
||||
/// [`add_function`]: UserDataMethods::add_function
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -438,7 +446,7 @@ pub trait UserDataMethods<T> {
|
||||
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||
///
|
||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -452,7 +460,7 @@ pub trait UserDataMethods<T> {
|
||||
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||
///
|
||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -468,7 +476,7 @@ pub trait UserDataMethods<T> {
|
||||
docsrs,
|
||||
doc(cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau")))))
|
||||
)]
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -484,7 +492,7 @@ pub trait UserDataMethods<T> {
|
||||
/// [`add_meta_method_mut`]: UserDataMethods::add_meta_method_mut
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -497,7 +505,7 @@ pub trait UserDataMethods<T> {
|
||||
/// Metamethods for binary operators can be triggered if either the left or right argument to
|
||||
/// the binary operator has a metatable, so the first argument here is not necessarily a
|
||||
/// userdata of type `T`.
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -508,7 +516,7 @@ pub trait UserDataMethods<T> {
|
||||
/// This is a version of [`add_meta_function`] that accepts a `FnMut` argument.
|
||||
///
|
||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -524,7 +532,7 @@ pub trait UserDataMethods<T> {
|
||||
docsrs,
|
||||
doc(cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau")))))
|
||||
)]
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -543,7 +551,7 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__index` metamethod, it will
|
||||
/// be used as a fall-back if no regular field or method are found.
|
||||
fn add_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
||||
fn add_field<V>(&mut self, name: impl Into<String>, value: V)
|
||||
where
|
||||
V: IntoLua + 'static;
|
||||
|
||||
@@ -554,7 +562,7 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
|
||||
/// be used as a fall-back if no regular field or method are found.
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua;
|
||||
@@ -567,21 +575,21 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__newindex` metamethod, the `__newindex` metamethod
|
||||
/// will be used as a fall-back if no regular field is found.
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua;
|
||||
|
||||
/// Add a regular field getter as a function which accepts a generic [`AnyUserData`] of type `T`
|
||||
/// argument.
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua;
|
||||
|
||||
/// Add a regular field setter as a function which accepts a generic [`AnyUserData`] of type `T`
|
||||
/// first argument.
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua;
|
||||
@@ -594,7 +602,7 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
||||
/// like `__gc` or `__metatable`.
|
||||
fn add_meta_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
||||
fn add_meta_field<V>(&mut self, name: impl Into<String>, value: V)
|
||||
where
|
||||
V: IntoLua + 'static;
|
||||
|
||||
@@ -606,7 +614,7 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
||||
/// like `__gc` or `__metatable`.
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl Into<StdString>, f: F)
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl Into<String>, f: F)
|
||||
where
|
||||
F: FnOnce(&Lua) -> Result<R> + 'static,
|
||||
R: IntoLua;
|
||||
@@ -706,7 +714,7 @@ pub trait UserData: Sized {
|
||||
///
|
||||
/// [`is`]: crate::AnyUserData::is
|
||||
/// [`borrow`]: crate::AnyUserData::borrow
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct AnyUserData(pub(crate) ValueRef);
|
||||
|
||||
impl AnyUserData {
|
||||
@@ -1021,8 +1029,8 @@ impl AnyUserData {
|
||||
|
||||
/// Returns a type name of this userdata (from a metatable field).
|
||||
///
|
||||
/// If no type name is set, returns `None`.
|
||||
pub fn type_name(&self) -> Result<Option<StdString>> {
|
||||
/// If no type name is set, returns `userdata`.
|
||||
pub fn type_name(&self) -> Result<LuaString> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
@@ -1039,8 +1047,8 @@ impl AnyUserData {
|
||||
ffi::luaL_getmetafield(state, -1, MetaMethod::Type.as_cstr().as_ptr())
|
||||
};
|
||||
match name_type {
|
||||
ffi::LUA_TSTRING => Ok(Some(String(lua.pop_ref()).to_str()?.to_owned())),
|
||||
_ => Ok(None),
|
||||
ffi::LUA_TSTRING => Ok(LuaString(lua.pop_ref())),
|
||||
_ => lua.create_string(b"userdata"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1076,6 +1084,48 @@ impl AnyUserData {
|
||||
};
|
||||
is_serializable().unwrap_or(false)
|
||||
}
|
||||
|
||||
unsafe fn invoke_tostring_dbg(&self) -> Result<Option<String>> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
let _guard = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
// Try `__todebugstring` metamethod first, then `__tostring`
|
||||
#[allow(clippy::collapsible_if)]
|
||||
if ffi::luaL_callmeta(state, -1, cstr!("__todebugstring")) == 0 {
|
||||
if ffi::luaL_callmeta(state, -1, cstr!("__tostring")) == 0 {
|
||||
ffi::lua_pushnil(state);
|
||||
}
|
||||
}
|
||||
})?;
|
||||
Ok(lua.pop_value().as_string().map(|s| s.to_string_lossy()))
|
||||
}
|
||||
|
||||
pub(crate) fn fmt_pretty(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
// Try converting to a (debug) string first, with fallback to `__name/__type`
|
||||
match unsafe { self.invoke_tostring_dbg() } {
|
||||
Ok(Some(s)) => write!(fmt, "{s}"),
|
||||
_ => {
|
||||
let name = self.type_name().ok();
|
||||
let name = (name.as_ref())
|
||||
.map(|s| Either::Left(s.display()))
|
||||
.unwrap_or(Either::Right("userdata"));
|
||||
write!(fmt, "{name}: {:?}", self.to_pointer())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for AnyUserData {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
if fmt.alternate() {
|
||||
return self.fmt_pretty(fmt);
|
||||
}
|
||||
fmt.debug_tuple("AnyUserData").field(&self.0).finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to a [`AnyUserData`] metatable.
|
||||
@@ -1126,13 +1176,13 @@ impl UserDataMetatable {
|
||||
/// It skips restricted metamethods, such as `__gc` or `__metatable`.
|
||||
///
|
||||
/// This struct is created by the [`UserDataMetatable::pairs`] method.
|
||||
pub struct UserDataMetatablePairs<'a, V>(TablePairs<'a, StdString, V>);
|
||||
pub struct UserDataMetatablePairs<'a, V>(TablePairs<'a, String, V>);
|
||||
|
||||
impl<V> Iterator for UserDataMetatablePairs<'_, V>
|
||||
where
|
||||
V: FromLua,
|
||||
{
|
||||
type Item = Result<(StdString, V)>;
|
||||
type Item = Result<(String, V)>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
@@ -1172,7 +1222,7 @@ impl AnyUserData {
|
||||
/// Wraps any Rust type, returning an opaque type that implements [`IntoLua`] trait.
|
||||
///
|
||||
/// This function uses [`Lua::create_any_userdata`] under the hood.
|
||||
pub fn wrap<T: MaybeSend + 'static>(data: T) -> impl IntoLua {
|
||||
pub fn wrap<T: MaybeSend + MaybeSync + 'static>(data: T) -> impl IntoLua {
|
||||
WrappedUserdata(move |lua| lua.create_any_userdata(data))
|
||||
}
|
||||
|
||||
@@ -1182,7 +1232,7 @@ impl AnyUserData {
|
||||
/// This function uses [`Lua::create_ser_any_userdata`] under the hood.
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
pub fn wrap_ser<T: Serialize + MaybeSend + 'static>(data: T) -> impl IntoLua {
|
||||
pub fn wrap_ser<T: Serialize + MaybeSend + MaybeSync + 'static>(data: T) -> impl IntoLua {
|
||||
WrappedUserdata(move |lua| lua.create_ser_any_userdata(data))
|
||||
}
|
||||
}
|
||||
|
||||
+33
-65
@@ -1,4 +1,4 @@
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
use std::cell::RefCell;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
@@ -6,14 +6,14 @@ use serde::ser::{Serialize, Serializer};
|
||||
use crate::error::{Error, Result};
|
||||
use crate::types::XRc;
|
||||
|
||||
use super::lock::{RawLock, UserDataLock};
|
||||
use super::lock::{RawLock, RwLock, UserDataLock};
|
||||
use super::r#ref::{UserDataRef, UserDataRefMut};
|
||||
|
||||
#[cfg(all(feature = "serde", not(feature = "send")))]
|
||||
type DynSerialize = dyn erased_serde::Serialize;
|
||||
|
||||
#[cfg(all(feature = "serde", feature = "send"))]
|
||||
type DynSerialize = dyn erased_serde::Serialize + Send;
|
||||
type DynSerialize = dyn erased_serde::Serialize + Send + Sync;
|
||||
|
||||
pub(crate) enum UserDataStorage<T> {
|
||||
Owned(UserDataVariant<T>),
|
||||
@@ -23,9 +23,9 @@ pub(crate) enum UserDataStorage<T> {
|
||||
// A enum for storing userdata values.
|
||||
// It's stored inside a Lua VM and protected by the outer `ReentrantMutex`.
|
||||
pub(crate) enum UserDataVariant<T> {
|
||||
Default(XRc<UserDataCell<T>>),
|
||||
Default(XRc<RwLock<T>>),
|
||||
#[cfg(feature = "serde")]
|
||||
Serializable(XRc<UserDataCell<Box<DynSerialize>>>, bool), // bool is `is_sync`
|
||||
Serializable(XRc<RwLock<Box<DynSerialize>>>),
|
||||
}
|
||||
|
||||
impl<T> Clone for UserDataVariant<T> {
|
||||
@@ -34,7 +34,7 @@ impl<T> Clone for UserDataVariant<T> {
|
||||
match self {
|
||||
Self::Default(inner) => Self::Default(XRc::clone(inner)),
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, is_sync) => Self::Serializable(XRc::clone(inner), *is_sync),
|
||||
Self::Serializable(inner) => Self::Serializable(XRc::clone(inner)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,10 +42,12 @@ impl<T> Clone for UserDataVariant<T> {
|
||||
impl<T> UserDataVariant<T> {
|
||||
#[inline(always)]
|
||||
pub(super) fn try_borrow_scoped<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
|
||||
// We don't need to check for `T: Sync` because when this method is used (internally),
|
||||
// Lua mutex is already locked.
|
||||
// If non-`Sync` userdata is already borrowed by another thread (via `UserDataRef`), it will be
|
||||
// exclusively locked.
|
||||
// Shared (read) lock is always correct for in-place borrows:
|
||||
// - this method is called internally while the Lua mutex is held, ensuring exclusive Lua-level
|
||||
// access per call frame
|
||||
// - with `send` feature, all owned userdata satisfies `T: Sync`, so simultaneous shared references
|
||||
// from multiple threads are sound
|
||||
// - without `send` feature, single-threaded execution makes shared lock safe for any `T`
|
||||
let _guard = (self.raw_lock().try_lock_shared_guarded()).map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(f(unsafe { &*self.as_ptr() }))
|
||||
}
|
||||
@@ -78,10 +80,12 @@ impl<T> UserDataVariant<T> {
|
||||
return Err(Error::UserDataBorrowMutError);
|
||||
}
|
||||
Ok(match self {
|
||||
Self::Default(inner) => XRc::into_inner(inner).unwrap().value.into_inner(),
|
||||
Self::Default(inner) => XRc::into_inner(inner).unwrap().into_inner(),
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, _) => unsafe {
|
||||
let raw = Box::into_raw(XRc::into_inner(inner).unwrap().value.into_inner());
|
||||
Self::Serializable(inner) => unsafe {
|
||||
// The serde variant erases `T` to `Box<DynSerialize>`, so we
|
||||
// must cast the raw pointer back to recover the concrete type.
|
||||
let raw = Box::into_raw(XRc::into_inner(inner).unwrap().into_inner());
|
||||
*Box::from_raw(raw as *mut T)
|
||||
},
|
||||
})
|
||||
@@ -92,25 +96,25 @@ impl<T> UserDataVariant<T> {
|
||||
match self {
|
||||
Self::Default(inner) => XRc::strong_count(inner),
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, _) => XRc::strong_count(inner),
|
||||
Self::Serializable(inner) => XRc::strong_count(inner),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn raw_lock(&self) -> &RawLock {
|
||||
match self {
|
||||
Self::Default(inner) => &inner.raw_lock,
|
||||
Self::Default(inner) => unsafe { inner.raw() },
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, _) => &inner.raw_lock,
|
||||
Self::Serializable(inner) => unsafe { inner.raw() },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn as_ptr(&self) -> *mut T {
|
||||
match self {
|
||||
Self::Default(inner) => inner.value.get(),
|
||||
Self::Default(inner) => inner.data_ptr(),
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, _) => unsafe { &mut **(inner.value.get() as *mut Box<T>) },
|
||||
Self::Serializable(inner) => unsafe { (&mut **inner.data_ptr()) as *mut DynSerialize as *mut T },
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,51 +123,16 @@ impl<T> UserDataVariant<T> {
|
||||
impl Serialize for UserDataStorage<()> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
match self {
|
||||
Self::Owned(variant @ UserDataVariant::Serializable(inner, is_sync)) => unsafe {
|
||||
#[cfg(feature = "send")]
|
||||
if *is_sync {
|
||||
let _guard = (variant.raw_lock().try_lock_shared_guarded())
|
||||
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
(*inner.value.get()).serialize(serializer)
|
||||
} else {
|
||||
let _guard = (variant.raw_lock().try_lock_exclusive_guarded())
|
||||
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
(*inner.value.get()).serialize(serializer)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
{
|
||||
let _ = is_sync;
|
||||
let _guard = (variant.raw_lock().try_lock_shared_guarded())
|
||||
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
(*inner.value.get()).serialize(serializer)
|
||||
}
|
||||
Self::Owned(variant @ UserDataVariant::Serializable(inner)) => unsafe {
|
||||
let _guard = (variant.raw_lock().try_lock_shared_guarded())
|
||||
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
(*inner.data_ptr()).serialize(serializer)
|
||||
},
|
||||
_ => Err(serde::ser::Error::custom("cannot serialize <userdata>")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that provides interior mutability for a userdata value (thread-safe).
|
||||
pub(crate) struct UserDataCell<T> {
|
||||
raw_lock: RawLock,
|
||||
value: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl<T: Send> Send for UserDataCell<T> {}
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl<T: Send> Sync for UserDataCell<T> {}
|
||||
|
||||
impl<T> UserDataCell<T> {
|
||||
#[inline(always)]
|
||||
fn new(value: T) -> Self {
|
||||
UserDataCell {
|
||||
raw_lock: RawLock::INIT,
|
||||
value: UnsafeCell::new(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum ScopedUserDataVariant<T> {
|
||||
Ref(*const T),
|
||||
RefMut(RefCell<*mut T>),
|
||||
@@ -173,10 +142,10 @@ pub(crate) enum ScopedUserDataVariant<T> {
|
||||
impl<T> Drop for ScopedUserDataVariant<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if let Self::Boxed(value) = self {
|
||||
if let Ok(value) = value.try_borrow_mut() {
|
||||
unsafe { drop(Box::from_raw(*value)) };
|
||||
}
|
||||
if let Self::Boxed(value) = self
|
||||
&& let Ok(value) = value.try_borrow_mut()
|
||||
{
|
||||
unsafe { drop(Box::from_raw(*value)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,7 +153,7 @@ impl<T> Drop for ScopedUserDataVariant<T> {
|
||||
impl<T: 'static> UserDataStorage<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(data: T) -> Self {
|
||||
Self::Owned(UserDataVariant::Default(XRc::new(UserDataCell::new(data))))
|
||||
Self::Owned(UserDataVariant::Default(XRc::new(RwLock::new(data))))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -201,11 +170,10 @@ impl<T: 'static> UserDataStorage<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_ser(data: T) -> Self
|
||||
where
|
||||
T: Serialize + crate::types::MaybeSend,
|
||||
T: Serialize + crate::types::MaybeSend + crate::types::MaybeSync,
|
||||
{
|
||||
let data = Box::new(data) as Box<DynSerialize>;
|
||||
let is_sync = super::util::is_sync::<T>();
|
||||
let variant = UserDataVariant::Serializable(XRc::new(UserDataCell::new(data)), is_sync);
|
||||
let variant = UserDataVariant::Serializable(XRc::new(RwLock::new(data)));
|
||||
Self::Owned(variant)
|
||||
}
|
||||
|
||||
|
||||
+44
-19
@@ -1,6 +1,4 @@
|
||||
pub(crate) trait UserDataLock {
|
||||
const INIT: Self;
|
||||
|
||||
fn is_locked(&self) -> bool;
|
||||
fn try_lock_shared(&self) -> bool;
|
||||
fn try_lock_exclusive(&self) -> bool;
|
||||
@@ -48,12 +46,12 @@ impl<L: UserDataLock + ?Sized> Drop for LockGuard<'_, L> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use lock_impl::RawLock;
|
||||
pub(crate) use lock_impl::{RawLock, RwLock};
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
mod lock_impl {
|
||||
use std::cell::Cell;
|
||||
use std::cell::{Cell, UnsafeCell};
|
||||
|
||||
// Positive values represent the number of read references.
|
||||
// Negative values represent the number of write references (only one allowed).
|
||||
@@ -62,9 +60,6 @@ mod lock_impl {
|
||||
const UNUSED: isize = 0;
|
||||
|
||||
impl super::UserDataLock for RawLock {
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const INIT: Self = Cell::new(UNUSED);
|
||||
|
||||
#[inline(always)]
|
||||
fn is_locked(&self) -> bool {
|
||||
self.get() != UNUSED
|
||||
@@ -72,7 +67,7 @@ mod lock_impl {
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_shared(&self) -> bool {
|
||||
let flag = self.get().wrapping_add(1);
|
||||
let flag = self.get().checked_add(1).expect("userdata lock count overflow");
|
||||
if flag <= UNUSED {
|
||||
return false;
|
||||
}
|
||||
@@ -104,41 +99,71 @@ mod lock_impl {
|
||||
self.set(flag + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// A cheap single-threaded read-write lock pairing a `parking_lot::RwLock` type.
|
||||
pub(crate) struct RwLock<T> {
|
||||
lock: RawLock,
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
impl<T> RwLock<T> {
|
||||
/// Creates a new `RwLock` containing the given value.
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(value: T) -> Self {
|
||||
RwLock {
|
||||
lock: RawLock::new(UNUSED),
|
||||
data: UnsafeCell::new(value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying raw lock.
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn raw(&self) -> &RawLock {
|
||||
&self.lock
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the underlying data.
|
||||
#[inline(always)]
|
||||
pub(crate) fn data_ptr(&self) -> *mut T {
|
||||
self.data.get()
|
||||
}
|
||||
|
||||
/// Consumes this `RwLock`, returning the underlying data.
|
||||
#[inline(always)]
|
||||
pub(crate) fn into_inner(self) -> T {
|
||||
self.data.into_inner()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
mod lock_impl {
|
||||
use parking_lot::lock_api::RawRwLock;
|
||||
|
||||
pub(crate) type RawLock = parking_lot::RawRwLock;
|
||||
pub(crate) use parking_lot::{RawRwLock as RawLock, RwLock};
|
||||
|
||||
impl super::UserDataLock for RawLock {
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const INIT: Self = <Self as parking_lot::lock_api::RawRwLock>::INIT;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_locked(&self) -> bool {
|
||||
RawRwLock::is_locked(self)
|
||||
parking_lot::lock_api::RawRwLock::is_locked(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_shared(&self) -> bool {
|
||||
RawRwLock::try_lock_shared(self)
|
||||
parking_lot::lock_api::RawRwLock::try_lock_shared(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_exclusive(&self) -> bool {
|
||||
RawRwLock::try_lock_exclusive(self)
|
||||
parking_lot::lock_api::RawRwLock::try_lock_exclusive(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn unlock_shared(&self) {
|
||||
RawRwLock::unlock_shared(self)
|
||||
parking_lot::lock_api::RawRwLock::unlock_shared(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn unlock_exclusive(&self) {
|
||||
RawRwLock::unlock_exclusive(self)
|
||||
parking_lot::lock_api::RawRwLock::unlock_exclusive(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::Function;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::WeakLua;
|
||||
use crate::table::Table;
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::value::Value;
|
||||
use crate::Function;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use crate::function::AsyncCallFuture;
|
||||
@@ -88,7 +86,7 @@ impl ObjectLike for AnyUserData {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_string(&self) -> Result<StdString> {
|
||||
fn to_string(&self) -> Result<String> {
|
||||
Value::UserData(self.clone()).to_string()
|
||||
}
|
||||
|
||||
|
||||
+75
-13
@@ -1,4 +1,4 @@
|
||||
use std::any::{type_name, TypeId};
|
||||
use std::any::{TypeId, type_name};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_int;
|
||||
use std::{fmt, mem};
|
||||
@@ -7,12 +7,11 @@ use crate::error::{Error, Result};
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::traits::FromLua;
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::util::get_userdata;
|
||||
use crate::util::{check_stack, get_userdata, take_userdata};
|
||||
use crate::value::Value;
|
||||
|
||||
use super::cell::{UserDataStorage, UserDataVariant};
|
||||
use super::lock::{LockGuard, RawLock, UserDataLock};
|
||||
use super::util::is_sync;
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
use {
|
||||
@@ -63,11 +62,10 @@ impl<T> TryFrom<UserDataVariant<T>> for UserDataRef<T> {
|
||||
|
||||
#[inline]
|
||||
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
|
||||
let guard = if cfg!(not(feature = "send")) || is_sync::<T>() {
|
||||
variant.raw_lock().try_lock_shared_guarded()
|
||||
} else {
|
||||
variant.raw_lock().try_lock_exclusive_guarded()
|
||||
};
|
||||
// Shared (read) lock is always correct:
|
||||
// - with `send` feature, `T: Sync` is guaranteed by the `MaybeSync` bound on userdata creation
|
||||
// - without `send` feature, single-threaded access makes shared lock safe for any `T`
|
||||
let guard = variant.raw_lock().try_lock_shared_guarded();
|
||||
let guard = guard.map_err(|_| Error::UserDataBorrowError)?;
|
||||
let guard = unsafe { mem::transmute::<LockGuard<_>, LockGuard<'static, _>>(guard) };
|
||||
Ok(UserDataRef::from_parts(UserDataRefInner::Default(variant), guard))
|
||||
@@ -442,15 +440,75 @@ impl<T> DerefMut for UserDataRefMutInner<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type that takes ownership of a userdata value.
|
||||
///
|
||||
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua by taking
|
||||
/// ownership of it.
|
||||
/// The original Lua userdata is marked as destructed and cannot be used further.
|
||||
pub struct UserDataOwned<T>(pub T);
|
||||
|
||||
impl<T> Deref for UserDataOwned<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataOwned<T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataOwned<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataOwned<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> FromLua for UserDataOwned<T> {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
try_value_to_userdata::<T>(value)?.take().map(UserDataOwned)
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let state = lua.state();
|
||||
let type_id = lua.get_userdata_type_id::<T>(state, idx)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, idx);
|
||||
if (*ud).has_exclusive_access() {
|
||||
check_stack(state, 1)?;
|
||||
take_userdata::<UserDataStorage<T>>(state, idx)
|
||||
.into_inner()
|
||||
.map(UserDataOwned)
|
||||
} else {
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
}
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(ud),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "userdata".to_string(),
|
||||
message: Some(format!("expected userdata of type {}", type_name::<T>())),
|
||||
}),
|
||||
_ => Err(Error::from_lua_conversion(
|
||||
value.type_name(),
|
||||
"userdata",
|
||||
format!("expected userdata of type {}", type_name::<T>()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,6 +524,10 @@ mod assertions {
|
||||
static_assertions::assert_impl_all!(UserDataRefMut<()>: Sync, Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_not_impl_all!(UserDataRefMut<std::rc::Rc<()>>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(UserDataOwned<()>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_not_impl_all!(UserDataOwned<std::rc::Rc<()>>: Send, Sync);
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_all!(UserDataRef<()>: Send, Sync);
|
||||
|
||||
+32
-27
@@ -4,15 +4,14 @@ use std::any::TypeId;
|
||||
use std::cell::RefCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_void;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::{Lua, LuaGuard};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::types::{Callback, MaybeSend};
|
||||
use crate::userdata::{
|
||||
borrow_userdata_scoped, borrow_userdata_scoped_mut, AnyUserData, MetaMethod, TypeIdHints, UserData,
|
||||
UserDataFields, UserDataMethods, UserDataStorage,
|
||||
AnyUserData, MetaMethod, TypeIdHints, UserData, UserDataFields, UserDataMethods, UserDataStorage,
|
||||
borrow_userdata_scoped, borrow_userdata_scoped_mut,
|
||||
};
|
||||
use crate::util::short_type_name;
|
||||
use crate::value::Value;
|
||||
@@ -55,7 +54,7 @@ pub(crate) struct RawUserDataRegistry {
|
||||
|
||||
pub(crate) destructor: ffi::lua_CFunction,
|
||||
pub(crate) type_id: Option<TypeId>,
|
||||
pub(crate) type_name: StdString,
|
||||
pub(crate) type_name: String,
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
pub(crate) enable_namecall: bool,
|
||||
@@ -368,7 +367,7 @@ impl<T> UserDataRegistry<T> {
|
||||
method: name.to_string(),
|
||||
type_name: value.type_name(),
|
||||
message: Some("expected nil, table or function".to_string()),
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -382,12 +381,12 @@ impl<T> UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
// Returns function name for the type `T`, without the module path
|
||||
fn get_function_name<T>(name: &str) -> StdString {
|
||||
fn get_function_name<T>(name: &str) -> String {
|
||||
format!("{}.{name}", short_type_name::<T>())
|
||||
}
|
||||
|
||||
impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
fn add_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
||||
fn add_field<V>(&mut self, name: impl Into<String>, value: V)
|
||||
where
|
||||
V: IntoLua + 'static,
|
||||
{
|
||||
@@ -395,7 +394,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
self.raw.fields.push((name, value.into_lua(self.lua.lua())));
|
||||
}
|
||||
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua,
|
||||
@@ -405,7 +404,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
self.raw.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua,
|
||||
@@ -415,7 +414,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
self.raw.field_setters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua,
|
||||
@@ -425,7 +424,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
self.raw.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl Into<StdString>, mut function: F)
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl Into<String>, mut function: F)
|
||||
where
|
||||
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua,
|
||||
@@ -435,7 +434,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
self.raw.field_setters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
||||
fn add_meta_field<V>(&mut self, name: impl Into<String>, value: V)
|
||||
where
|
||||
V: IntoLua + 'static,
|
||||
{
|
||||
@@ -445,7 +444,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
self.raw.meta_fields.push((name, field));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl Into<StdString>, f: F)
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl Into<String>, f: F)
|
||||
where
|
||||
F: FnOnce(&Lua) -> Result<R> + 'static,
|
||||
R: IntoLua,
|
||||
@@ -458,7 +457,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
fn add_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -469,7 +468,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -481,7 +480,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -495,7 +494,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -508,7 +507,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_function<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -519,7 +518,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -531,7 +530,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -543,7 +542,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -554,7 +553,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -566,7 +565,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -580,7 +579,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl Into<String>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -593,7 +592,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.async_meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -604,7 +603,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
self.raw.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -616,7 +615,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl Into<String>, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -655,6 +654,12 @@ macro_rules! lua_userdata_impl {
|
||||
// A special proxy object for UserData
|
||||
pub(crate) struct UserDataProxy<T>(pub(crate) PhantomData<T>);
|
||||
|
||||
// `UserDataProxy` holds no real `T` value, only a type marker, so it is always safe to send/share.
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl<T> Send for UserDataProxy<T> {}
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl<T> Sync for UserDataProxy<T> {}
|
||||
|
||||
lua_userdata_impl!(UserDataProxy<T>);
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use std::any::TypeId;
|
||||
use std::cell::Cell;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_int;
|
||||
use std::ptr;
|
||||
|
||||
@@ -11,35 +9,6 @@ use crate::error::{Error, Result};
|
||||
use crate::types::CallbackPtr;
|
||||
use crate::util::{get_userdata, rawget_field, rawset_field, take_userdata};
|
||||
|
||||
// This is a trick to check if a type is `Sync` or not.
|
||||
// It uses leaked specialization feature from stdlib.
|
||||
struct IsSync<'a, T> {
|
||||
is_sync: &'a Cell<bool>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Clone for IsSync<'_, T> {
|
||||
fn clone(&self) -> Self {
|
||||
self.is_sync.set(false);
|
||||
IsSync {
|
||||
is_sync: self.is_sync,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sync> Copy for IsSync<'_, T> {}
|
||||
|
||||
pub(crate) fn is_sync<T>() -> bool {
|
||||
let is_sync = Cell::new(true);
|
||||
let _ = [IsSync::<T> {
|
||||
is_sync: &is_sync,
|
||||
_marker: PhantomData,
|
||||
}]
|
||||
.clone();
|
||||
is_sync.get()
|
||||
}
|
||||
|
||||
// Userdata type hints, used to match types of wrapped userdata
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) struct TypeIdHints {
|
||||
|
||||
+4
-4
@@ -2,15 +2,15 @@ use std::any::Any;
|
||||
use std::fmt::Write as _;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||
use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::memory::MemoryState;
|
||||
use crate::util::{
|
||||
check_stack, get_internal_userdata, init_internal_metatable, push_internal_userdata, push_string,
|
||||
push_table, rawset_field, to_string, TypeKey, DESTRUCTED_USERDATA_METATABLE,
|
||||
DESTRUCTED_USERDATA_METATABLE, TypeKey, check_stack, get_internal_userdata, init_internal_metatable,
|
||||
push_internal_userdata, push_string, push_table, rawset_field, to_string,
|
||||
};
|
||||
|
||||
static WRAPPED_FAILURE_TYPE_KEY: u8 = 0;
|
||||
@@ -197,7 +197,7 @@ where
|
||||
F: FnOnce(*mut ffi::lua_State) -> R,
|
||||
R: Copy,
|
||||
{
|
||||
struct Params<F, R: Copy> {
|
||||
struct Params<F, R> {
|
||||
function: Option<F>,
|
||||
result: MaybeUninit<R>,
|
||||
nresults: c_int,
|
||||
|
||||
+6
-10
@@ -6,16 +6,16 @@ use std::{ptr, slice, str};
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
pub(crate) use error::{
|
||||
error_traceback, error_traceback_thread, init_error_registry, pop_error, protect_lua_call,
|
||||
protect_lua_closure, WrappedFailure,
|
||||
WrappedFailure, error_traceback, error_traceback_thread, init_error_registry, pop_error,
|
||||
protect_lua_call, protect_lua_closure,
|
||||
};
|
||||
pub(crate) use path::parse_path as parse_lookup_path;
|
||||
pub(crate) use short_names::short_type_name;
|
||||
pub(crate) use types::TypeKey;
|
||||
pub(crate) use userdata::{
|
||||
get_destructed_userdata_metatable, get_internal_metatable, get_internal_userdata, get_userdata,
|
||||
init_internal_metatable, push_internal_userdata, push_userdata, take_userdata,
|
||||
DESTRUCTED_USERDATA_METATABLE,
|
||||
DESTRUCTED_USERDATA_METATABLE, get_destructed_userdata_metatable, get_internal_metatable,
|
||||
get_internal_userdata, get_userdata, init_internal_metatable, push_internal_userdata, push_userdata,
|
||||
take_userdata,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
@@ -264,11 +264,7 @@ pub(crate) unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut f
|
||||
// Check the current state first
|
||||
let is_main_state = ffi::lua_pushthread(state) == 1;
|
||||
ffi::lua_pop(state, 1);
|
||||
if is_main_state {
|
||||
Some(state)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if is_main_state { Some(state) } else { None }
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
Some(ffi::lua_mainthread(state))
|
||||
|
||||
+1
-1
@@ -196,7 +196,7 @@ fn unquote_string<'a>(path: &'a str, chars: &mut Peekable<CharIndices<'a>>) -> R
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{parse_path, PathKey};
|
||||
use super::{PathKey, parse_path};
|
||||
|
||||
#[test]
|
||||
fn test_parse_path() {
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::{mem, ptr};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::userdata::collect_userdata;
|
||||
use crate::util::{check_stack, get_metatable_ptr, push_table, rawset_field, TypeKey};
|
||||
use crate::util::{TypeKey, check_stack, get_metatable_ptr, push_table, rawset_field};
|
||||
|
||||
// Pushes the userdata and attaches a metatable with __gc method.
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
|
||||
+19
-33
@@ -1,19 +1,18 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashSet;
|
||||
use std::os::raw::c_void;
|
||||
use std::string::String as StdString;
|
||||
use std::{fmt, ptr, str};
|
||||
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::string::{BorrowedStr, String};
|
||||
use crate::string::{BorrowedStr, LuaString};
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{Integer, LightUserData, Number, ValueRef};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::util::{check_stack, StackGuard};
|
||||
use crate::util::{StackGuard, check_stack};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use {
|
||||
@@ -50,7 +49,7 @@ pub enum Value {
|
||||
/// An interned string, managed by Lua.
|
||||
///
|
||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||
String(String),
|
||||
String(LuaString),
|
||||
/// Reference to a Lua table.
|
||||
Table(Table),
|
||||
/// Reference to a Lua function (or closure).
|
||||
@@ -129,18 +128,13 @@ impl Value {
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
match self {
|
||||
Value::String(String(vref)) => {
|
||||
// In Lua < 5.4 (excluding Luau), string pointers are NULL
|
||||
// Use alternative approach
|
||||
let lua = vref.lua.lock();
|
||||
unsafe { ffi::lua_tostring(lua.ref_thread(), vref.index) as *const c_void }
|
||||
}
|
||||
Value::LightUserData(ud) => ud.0,
|
||||
Value::Table(Table(vref))
|
||||
| Value::Function(Function(vref))
|
||||
| Value::Thread(Thread(vref, ..))
|
||||
| Value::UserData(AnyUserData(vref))
|
||||
| Value::Other(vref) => vref.to_pointer(),
|
||||
Value::String(s) => s.to_pointer(),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Buffer(crate::Buffer(vref)) => vref.to_pointer(),
|
||||
_ => ptr::null(),
|
||||
@@ -151,8 +145,8 @@ impl Value {
|
||||
///
|
||||
/// This might invoke the `__tostring` metamethod for non-primitive types (eg. tables,
|
||||
/// functions).
|
||||
pub fn to_string(&self) -> Result<StdString> {
|
||||
unsafe fn invoke_to_string(vref: &ValueRef) -> Result<StdString> {
|
||||
pub fn to_string(&self) -> Result<String> {
|
||||
unsafe fn invoke_tostring(vref: &ValueRef) -> Result<String> {
|
||||
let lua = vref.lua.lock();
|
||||
let state = lua.state();
|
||||
let _guard = StackGuard::new(state);
|
||||
@@ -162,7 +156,7 @@ impl Value {
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||
})?;
|
||||
Ok(String(lua.pop_ref()).to_str()?.to_string())
|
||||
Ok(LuaString(lua.pop_ref()).to_str()?.to_string())
|
||||
}
|
||||
|
||||
match self {
|
||||
@@ -179,9 +173,9 @@ impl Value {
|
||||
| Value::Function(Function(vref))
|
||||
| Value::Thread(Thread(vref, ..))
|
||||
| Value::UserData(AnyUserData(vref))
|
||||
| Value::Other(vref) => unsafe { invoke_to_string(vref) },
|
||||
| Value::Other(vref) => unsafe { invoke_tostring(vref) },
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Buffer(crate::Buffer(vref)) => unsafe { invoke_to_string(vref) },
|
||||
Value::Buffer(crate::Buffer(vref)) => unsafe { invoke_tostring(vref) },
|
||||
Value::Error(err) => Ok(err.to_string()),
|
||||
}
|
||||
}
|
||||
@@ -336,17 +330,17 @@ impl Value {
|
||||
self.as_number()
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a Lua [`String`].
|
||||
/// Returns `true` if the value is a [`LuaString`].
|
||||
#[inline]
|
||||
pub fn is_string(&self) -> bool {
|
||||
self.as_string().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to Lua [`String`].
|
||||
/// Cast the value to a [`LuaString`].
|
||||
///
|
||||
/// If the value is a Lua [`String`], returns it or `None` otherwise.
|
||||
/// If the value is a [`LuaString`], returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_string(&self) -> Option<&String> {
|
||||
pub fn as_string(&self) -> Option<&LuaString> {
|
||||
match self {
|
||||
Value::String(s) => Some(s),
|
||||
_ => None,
|
||||
@@ -355,26 +349,26 @@ impl Value {
|
||||
|
||||
/// Cast the value to [`BorrowedStr`].
|
||||
///
|
||||
/// If the value is a Lua [`String`], try to convert it to [`BorrowedStr`] or return `None`
|
||||
/// If the value is a [`LuaString`], try to convert it to [`BorrowedStr`] or return `None`
|
||||
/// otherwise.
|
||||
#[deprecated(
|
||||
since = "0.11.0",
|
||||
note = "This method does not follow Rust naming convention. Use `as_string().and_then(|s| s.to_str().ok())` instead."
|
||||
)]
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> Option<BorrowedStr<'_>> {
|
||||
pub fn as_str(&self) -> Option<BorrowedStr> {
|
||||
self.as_string().and_then(|s| s.to_str().ok())
|
||||
}
|
||||
|
||||
/// Cast the value to [`StdString`].
|
||||
/// Cast the value to [`String`].
|
||||
///
|
||||
/// If the value is a Lua [`String`], converts it to [`StdString`] or returns `None` otherwise.
|
||||
/// If the value is a [`LuaString`], converts it to [`String`] or returns `None` otherwise.
|
||||
#[deprecated(
|
||||
since = "0.11.0",
|
||||
note = "This method does not follow Rust naming convention. Use `as_string().map(|s| s.to_string_lossy())` instead."
|
||||
)]
|
||||
#[inline]
|
||||
pub fn as_string_lossy(&self) -> Option<StdString> {
|
||||
pub fn as_string_lossy(&self) -> Option<String> {
|
||||
self.as_string().map(|s| s.to_string_lossy())
|
||||
}
|
||||
|
||||
@@ -562,15 +556,7 @@ impl Value {
|
||||
t @ Value::Table(_) => write!(fmt, "table: {:?}", t.to_pointer()),
|
||||
f @ Value::Function(_) => write!(fmt, "function: {:?}", f.to_pointer()),
|
||||
t @ Value::Thread(_) => write!(fmt, "thread: {:?}", t.to_pointer()),
|
||||
u @ Value::UserData(ud) => {
|
||||
// Try `__name/__type` first then `__tostring`
|
||||
let name = ud.type_name().ok().flatten();
|
||||
let s = name
|
||||
.map(|name| format!("{name}: {:?}", u.to_pointer()))
|
||||
.or_else(|| u.to_string().ok())
|
||||
.unwrap_or_else(|| format!("userdata: {:?}", u.to_pointer()));
|
||||
write!(fmt, "{s}")
|
||||
}
|
||||
Value::UserData(ud) => ud.fmt_pretty(fmt),
|
||||
#[cfg(feature = "luau")]
|
||||
buf @ Value::Buffer(_) => write!(fmt, "buffer: {:?}", buf.to_pointer()),
|
||||
Value::Error(e) if recursive => write!(fmt, "{e:?}"),
|
||||
|
||||
+5
-6
@@ -1,6 +1,5 @@
|
||||
#![cfg(feature = "async")]
|
||||
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -8,7 +7,7 @@ use futures_util::stream::TryStreamExt;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use mlua::{
|
||||
Error, Function, Lua, LuaOptions, MultiValue, ObjectLike, Result, StdLib, Table, ThreadStatus, UserData,
|
||||
Error, Function, Lua, LuaOptions, MultiValue, ObjectLike, Result, StdLib, Table, UserData,
|
||||
UserDataMethods, UserDataRef, Value,
|
||||
};
|
||||
|
||||
@@ -40,9 +39,9 @@ async fn test_async_function() -> Result<()> {
|
||||
async fn test_async_function_wrap() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = Function::wrap_async(|s: StdString| async move {
|
||||
let f = Function::wrap_async(|s: String| async move {
|
||||
tokio::task::yield_now().await;
|
||||
Ok(s)
|
||||
Ok::<_, Error>(s)
|
||||
});
|
||||
lua.globals().set("f", f)?;
|
||||
let res: String = lua.load(r#"f("hello")"#).eval_async().await?;
|
||||
@@ -68,7 +67,7 @@ async fn test_async_function_wrap() -> Result<()> {
|
||||
async fn test_async_function_wrap_raw() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = Function::wrap_raw_async(|s: StdString| async move {
|
||||
let f = Function::wrap_raw_async(|s: String| async move {
|
||||
tokio::task::yield_now().await;
|
||||
s
|
||||
});
|
||||
@@ -715,7 +714,7 @@ fn test_async_yield_with() -> Result<()> {
|
||||
assert_eq!(thread.resume::<(i32, i32)>((10, 11))?, (21, 110));
|
||||
assert_eq!(thread.resume::<(i32, i32)>((11, 12))?, (23, 132));
|
||||
assert_eq!(thread.resume::<(i32, i32)>((12, 13))?, (0, 0));
|
||||
assert_eq!(thread.status(), ThreadStatus::Finished);
|
||||
assert!(thread.is_finished());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+17
-1
@@ -1,3 +1,4 @@
|
||||
#[cfg(not(target_os = "wasi"))]
|
||||
use std::{fs, io};
|
||||
|
||||
use mlua::{Chunk, ChunkMode, Lua, Result};
|
||||
@@ -85,7 +86,7 @@ fn test_chunk_macro() -> Result<()> {
|
||||
data.raw_set("num", 1)?;
|
||||
|
||||
let ud = mlua::AnyUserData::wrap("hello");
|
||||
let f = mlua::Function::wrap(|| Ok(()));
|
||||
let f = mlua::Function::wrap(|| Ok::<_, mlua::Error>(()));
|
||||
|
||||
lua.globals().set("g", 123)?;
|
||||
|
||||
@@ -109,6 +110,21 @@ fn test_chunk_macro() -> Result<()> {
|
||||
|
||||
assert_eq!(lua.globals().get::<i32>("s")?, 321);
|
||||
|
||||
// Check line numbers in error reporting
|
||||
match lua
|
||||
.load(mlua::chunk! {
|
||||
local x = 1
|
||||
-- comment
|
||||
error("boom")
|
||||
})
|
||||
.exec()
|
||||
{
|
||||
Err(mlua::Error::RuntimeError(ref msg)) => {
|
||||
assert!(msg.contains(":3:"), "expected line 3, got: {msg}");
|
||||
}
|
||||
other => panic!("expected RuntimeError, got {other:?}"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -21,4 +21,27 @@ fn test_compilation() {
|
||||
t.compile_fail("tests/compile/non_send.rs");
|
||||
#[cfg(not(feature = "send"))]
|
||||
t.pass("tests/compile/non_send.rs");
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
{
|
||||
t.compile_fail("tests/compile/chunk_dollar_non_ident.rs");
|
||||
t.compile_fail("tests/compile/userdata_getter_and_meta.rs");
|
||||
t.compile_fail("tests/compile/userdata_getter_and_setter.rs");
|
||||
t.compile_fail("tests/compile/userdata_getter_mut_self.rs");
|
||||
t.compile_fail("tests/compile/userdata_getter_extra_arg.rs");
|
||||
t.compile_fail("tests/compile/userdata_setter_ref_self.rs");
|
||||
t.compile_fail("tests/compile/userdata_mut_slice_arg.rs");
|
||||
t.compile_fail("tests/compile/userdata_setter_no_value.rs");
|
||||
t.compile_fail("tests/compile/userdata_static_with_self.rs");
|
||||
t.compile_fail("tests/compile/userdata_meta_owned_self.rs");
|
||||
t.compile_fail("tests/compile/userdata_const_getter.rs");
|
||||
t.compile_fail("tests/compile/userdata_field_with_args.rs");
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "macros", feature = "async"))]
|
||||
{
|
||||
t.compile_fail("tests/compile/userdata_getter_async.rs");
|
||||
t.compile_fail("tests/compile/userdata_setter_async.rs");
|
||||
t.compile_fail("tests/compile/userdata_field_async.rs");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
error[E0596]: cannot borrow `s` as mutable, as it is a captured variable in a `Fn` closure
|
||||
--> tests/compile/async_any_userdata_method.rs:9:49
|
||||
|
|
||||
8 | let mut s = &s;
|
||||
| ----- `s` declared here, outside the closure
|
||||
9 | reg.add_async_method("t", |_, this, ()| async {
|
||||
| ------------- ^^^^^ cannot borrow as mutable
|
||||
| |
|
||||
| in this closure
|
||||
10 | s = &*this;
|
||||
| - mutable borrow occurs due to use of `s` in closure
|
||||
8 | let mut s = &s;
|
||||
| ----- `s` declared here, outside the closure
|
||||
9 | reg.add_async_method("t", |_, this, ()| async {
|
||||
| - ------------- ^^^^^ cannot borrow as mutable
|
||||
| | |
|
||||
| _____________| in this closure
|
||||
| |
|
||||
10 | | s = &*this;
|
||||
| | - mutable borrow occurs due to use of `s` in closure
|
||||
11 | | Ok(())
|
||||
12 | | });
|
||||
| |__________- expects `Fn` instead of `FnMut`
|
||||
|
||||
error[E0373]: async block may outlive the current function, but it borrows `this`, which is owned by the current function
|
||||
--> tests/compile/async_any_userdata_method.rs:9:49
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
use mlua::chunk;
|
||||
fn main() {
|
||||
let _ = chunk! { $42 };
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: `$` must be followed by an identifier
|
||||
--> tests/compile/chunk_dollar_non_ident.rs:3:22
|
||||
|
|
||||
3 | let _ = chunk! { $42 };
|
||||
| ^
|
||||
@@ -1,32 +1,28 @@
|
||||
error[E0277]: the type `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
error[E0277]: the type `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::raw::RawLua>`
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||
= help: within `mlua::types::sync::inner::ReentrantMutex<mlua::state::RawLua>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<*mut lua_State>`
|
||||
note: required because it appears within the type `Cell<*mut lua_State>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
| pub struct Cell<T: ?Sized> {
|
||||
| ^^^^
|
||||
note: required because it appears within the type `mlua::state::RawLua`
|
||||
--> src/state/raw.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
| pub struct RawLua {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::RawLua>`
|
||||
--> src/types/sync.rs
|
||||
|
|
||||
| pub struct PhantomData<T: PointeeSized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
| ^^^
|
||||
| pub(crate) struct ReentrantMutex<T>(T);
|
||||
| ^^^^^^^^^^^^^^
|
||||
= note: required for `Rc<mlua::types::sync::inner::ReentrantMutex<mlua::state::RawLua>>` to implement `RefUnwindSafe`
|
||||
note: required because it appears within the type `Lua`
|
||||
--> src/state.rs
|
||||
|
|
||||
@@ -44,45 +40,37 @@ note: required by a bound in `std::panic::catch_unwind`
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
error[E0277]: the type `UnsafeCell<state::extra::ExtraData>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<state::extra::ExtraData>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`
|
||||
note: required because it appears within the type `Cell<usize>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
= help: the trait `RefUnwindSafe` is not implemented for `UnsafeCell<state::extra::ExtraData>`
|
||||
= note: required for `Rc<UnsafeCell<state::extra::ExtraData>>` to implement `RefUnwindSafe`
|
||||
note: required because it appears within the type `MaybeDangling<Rc<UnsafeCell<state::extra::ExtraData>>>`
|
||||
--> $RUST/core/src/mem/maybe_dangling.rs
|
||||
|
|
||||
| pub struct Cell<T: ?Sized> {
|
||||
| ^^^^
|
||||
note: required because it appears within the type `lock_api::remutex::RawReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId>`
|
||||
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||
| pub struct MaybeDangling<P: ?Sized>(P);
|
||||
| ^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `ManuallyDrop<Rc<UnsafeCell<state::extra::ExtraData>>>`
|
||||
--> $RUST/core/src/mem/manually_drop.rs
|
||||
|
|
||||
| pub struct RawReentrantMutex<R, G> {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||
| pub struct ManuallyDrop<T: ?Sized> {
|
||||
| ^^^^^^^^^^^^
|
||||
note: required because it appears within the type `mlua::state::RawLua`
|
||||
--> src/state/raw.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
| pub struct RawLua {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::RawLua>`
|
||||
--> src/types/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
|
|
||||
| pub struct PhantomData<T: PointeeSized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
| ^^^
|
||||
| pub(crate) struct ReentrantMutex<T>(T);
|
||||
| ^^^^^^^^^^^^^^
|
||||
= note: required for `Rc<mlua::types::sync::inner::ReentrantMutex<mlua::state::RawLua>>` to implement `RefUnwindSafe`
|
||||
note: required because it appears within the type `Lua`
|
||||
--> src/state.rs
|
||||
|
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
error[E0277]: the type `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::raw::RawLua>`
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||
= help: within `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`
|
||||
note: required because it appears within the type `Cell<usize>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
| pub struct Cell<T: ?Sized> {
|
||||
| ^^^^
|
||||
note: required because it appears within the type `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
= note: required for `NonNull<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::sync::Weak<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
| struct RcInner<T: ?Sized> {
|
||||
| ^^^^^^^
|
||||
= note: required for `NonNull<rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::rc::Weak<mlua::types::sync::inner::ReentrantMutex<mlua::state::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| pub struct Weak<
|
||||
| ^^^^
|
||||
@@ -49,38 +49,105 @@ note: required by a bound in `std::panic::catch_unwind`
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
error[E0277]: the type `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`
|
||||
note: required because it appears within the type `Cell<usize>`
|
||||
= help: within `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<*mut lua_State>`
|
||||
note: required because it appears within the type `Cell<*mut lua_State>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
|
|
||||
| pub struct Cell<T: ?Sized> {
|
||||
| ^^^^
|
||||
note: required because it appears within the type `lock_api::remutex::RawReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId>`
|
||||
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||
note: required because it appears within the type `mlua::state::RawLua`
|
||||
--> src/state/raw.rs
|
||||
|
|
||||
| pub struct RawReentrantMutex<R, G> {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||
--> $CARGO/lock_api-$VERSION/src/remutex.rs
|
||||
| pub struct RawLua {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::RawLua>`
|
||||
--> src/types/sync.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
| pub(crate) struct ReentrantMutex<T>(T);
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
= note: required for `NonNull<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::sync::Weak<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
| struct RcInner<T: ?Sized> {
|
||||
| ^^^^^^^
|
||||
= note: required for `NonNull<rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::rc::Weak<mlua::types::sync::inner::ReentrantMutex<mlua::state::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| pub struct Weak<
|
||||
| ^^^^
|
||||
note: required because it appears within the type `WeakLua`
|
||||
--> src/state.rs
|
||||
|
|
||||
| pub struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
| ^^^^^^^
|
||||
note: required because it appears within the type `mlua::types::value_ref::ValueRef`
|
||||
--> src/types/value_ref.rs
|
||||
|
|
||||
| pub struct ValueRef {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `LuaTable`
|
||||
--> src/table.rs
|
||||
|
|
||||
| pub struct Table(pub(crate) ValueRef);
|
||||
| ^^^^^
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ^^^^^^^
|
||||
note: required by a bound in `std::panic::catch_unwind`
|
||||
--> $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<state::extra::ExtraData>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<state::extra::ExtraData>` may contain interior mutability and a reference may not be safely transferable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `RefUnwindSafe` is not implemented for `UnsafeCell<state::extra::ExtraData>`
|
||||
= note: required for `Rc<UnsafeCell<state::extra::ExtraData>>` to implement `RefUnwindSafe`
|
||||
note: required because it appears within the type `MaybeDangling<Rc<UnsafeCell<state::extra::ExtraData>>>`
|
||||
--> $RUST/core/src/mem/maybe_dangling.rs
|
||||
|
|
||||
| pub struct MaybeDangling<P: ?Sized>(P);
|
||||
| ^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `ManuallyDrop<Rc<UnsafeCell<state::extra::ExtraData>>>`
|
||||
--> $RUST/core/src/mem/manually_drop.rs
|
||||
|
|
||||
| pub struct ManuallyDrop<T: ?Sized> {
|
||||
| ^^^^^^^^^^^^
|
||||
note: required because it appears within the type `mlua::state::RawLua`
|
||||
--> src/state/raw.rs
|
||||
|
|
||||
| pub struct RawLua {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::RawLua>`
|
||||
--> src/types/sync.rs
|
||||
|
|
||||
| pub(crate) struct ReentrantMutex<T>(T);
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| struct RcInner<T: ?Sized> {
|
||||
| ^^^^^^^
|
||||
= note: required for `NonNull<rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::rc::Weak<mlua::types::sync::inner::ReentrantMutex<mlua::state::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| pub struct Weak<
|
||||
| ^^^^
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#[derive(Default, mlua::UserData)]
|
||||
struct Foo;
|
||||
|
||||
#[mlua::userdata_impl]
|
||||
impl Foo {
|
||||
#[lua(getter)]
|
||||
const X: u32 = 42;
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: const items do not support `getter` or `setter`
|
||||
--> tests/compile/userdata_const_getter.rs:6:5
|
||||
|
|
||||
6 | #[lua(getter)]
|
||||
| ^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1,14 @@
|
||||
use mlua::Result;
|
||||
|
||||
#[derive(Clone, Debug, mlua::UserData)]
|
||||
struct Foo;
|
||||
|
||||
#[mlua::userdata_impl]
|
||||
impl Foo {
|
||||
#[lua(field)]
|
||||
async fn description() -> Result<String> {
|
||||
Ok("foo".into())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,13 @@
|
||||
error: async field function is not supported
|
||||
--> tests/compile/userdata_field_async.rs:9:5
|
||||
|
|
||||
9 | async fn description() -> Result<String> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: unused import: `mlua::Result`
|
||||
--> tests/compile/userdata_field_async.rs:1:5
|
||||
|
|
||||
1 | use mlua::Result;
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default
|
||||
@@ -0,0 +1,14 @@
|
||||
#[derive(Default, mlua::UserData)]
|
||||
struct Foo {
|
||||
x: u32,
|
||||
}
|
||||
|
||||
#[mlua::userdata_impl]
|
||||
impl Foo {
|
||||
#[lua(field)]
|
||||
fn as_name(name: &str) -> String {
|
||||
name.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: field function must not take arguments
|
||||
--> tests/compile/userdata_field_with_args.rs:9:5
|
||||
|
|
||||
9 | fn as_name(name: &str) -> String {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1,12 @@
|
||||
#[derive(Default, mlua::UserData)]
|
||||
struct Foo;
|
||||
|
||||
#[mlua::userdata_impl]
|
||||
impl Foo {
|
||||
#[lua(getter, meta)]
|
||||
fn bar(&self) -> mlua::Result<u32> {
|
||||
Ok(42)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: `meta` can only be combined with `field`
|
||||
--> tests/compile/userdata_getter_and_meta.rs:6:5
|
||||
|
|
||||
6 | #[lua(getter, meta)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1,14 @@
|
||||
#[derive(Default, mlua::UserData)]
|
||||
struct Foo {
|
||||
x: u32,
|
||||
}
|
||||
|
||||
#[mlua::userdata_impl]
|
||||
impl Foo {
|
||||
#[lua(getter, setter)]
|
||||
fn x(&self) -> mlua::Result<u32> {
|
||||
Ok(self.x)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: at most one of `getter`, `setter`, `field` can be specified
|
||||
--> tests/compile/userdata_getter_and_setter.rs:8:5
|
||||
|
|
||||
8 | #[lua(getter, setter)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1,14 @@
|
||||
use mlua::Result;
|
||||
|
||||
#[derive(Clone, Debug, mlua::UserData)]
|
||||
struct Foo(u64);
|
||||
|
||||
#[mlua::userdata_impl]
|
||||
impl Foo {
|
||||
#[lua(getter)]
|
||||
async fn value(&self) -> Result<u64> {
|
||||
Ok(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,13 @@
|
||||
error: async field getter is not supported
|
||||
--> tests/compile/userdata_getter_async.rs:9:5
|
||||
|
|
||||
9 | async fn value(&self) -> Result<u64> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: unused import: `mlua::Result`
|
||||
--> tests/compile/userdata_getter_async.rs:1:5
|
||||
|
|
||||
1 | use mlua::Result;
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default
|
||||
@@ -0,0 +1,14 @@
|
||||
#[derive(Default, mlua::UserData)]
|
||||
struct Foo {
|
||||
x: u32,
|
||||
}
|
||||
|
||||
#[mlua::userdata_impl]
|
||||
impl Foo {
|
||||
#[lua(getter)]
|
||||
fn x(&self, extra: u32) -> mlua::Result<u32> {
|
||||
Ok(self.x + extra)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: field getter must not take additional arguments
|
||||
--> tests/compile/userdata_getter_extra_arg.rs:9:5
|
||||
|
|
||||
9 | fn x(&self, extra: u32) -> mlua::Result<u32> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1,14 @@
|
||||
#[derive(Default, mlua::UserData)]
|
||||
struct Foo {
|
||||
x: u32,
|
||||
}
|
||||
|
||||
#[mlua::userdata_impl]
|
||||
impl Foo {
|
||||
#[lua(getter)]
|
||||
fn x(&mut self) -> mlua::Result<u32> {
|
||||
Ok(self.x)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: field getter must take `&self`
|
||||
--> tests/compile/userdata_getter_mut_self.rs:9:5
|
||||
|
|
||||
9 | fn x(&mut self) -> mlua::Result<u32> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1,12 @@
|
||||
#[derive(Default, mlua::UserData)]
|
||||
struct Foo;
|
||||
|
||||
#[mlua::userdata_impl]
|
||||
impl Foo {
|
||||
#[lua(meta)]
|
||||
fn __gc(self) -> mlua::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: meta methods cannot take `self`, use `&[mut] self` instead
|
||||
--> tests/compile/userdata_meta_owned_self.rs:7:5
|
||||
|
|
||||
7 | fn __gc(self) -> mlua::Result<()> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user