mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 973b5c3bf5 | |||
| b610a79d66 | |||
| fe39ae09bf | |||
| 01714d2510 | |||
| 0bad4a0ff9 | |||
| 35b7504076 | |||
| c9b8eb5418 | |||
| ef7d123f80 | |||
| 20cba5de5b | |||
| 3e03f4201c | |||
| 5199b02346 | |||
| 5293b8d6d2 | |||
| 7541b6f3f3 | |||
| 205510a540 | |||
| 2250421438 |
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
- name: Generate code coverage
|
||||
run: |
|
||||
cargo tarpaulin --verbose --features lua53,vendored,async,send,serialize --out xml --exclude-files benches --exclude-files tests --exclude-files build --exclude-files src/ffi
|
||||
cargo tarpaulin --verbose --features lua53,vendored,async,send,serialize,macros --out xml --exclude-files benches --exclude-files build --exclude-files mlua_derive --exclude-files src/ffi --exclude-files tests
|
||||
|
||||
- name: Upload to codecov.io
|
||||
uses: codecov/codecov-action@v1
|
||||
|
||||
+12
-12
@@ -26,8 +26,8 @@ jobs:
|
||||
override: true
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --release --features "${{ matrix.lua }} vendored"
|
||||
cargo build --release --features "${{ matrix.lua }} vendored async send serialize"
|
||||
cargo build --release --features "${{ matrix.lua }},vendored"
|
||||
cargo build --release --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-18.04' && matrix.lua != 'lua54' }}
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
target: aarch64-apple-darwin
|
||||
override: true
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }} async send serialize vendored"
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
|
||||
build_aarch64_cross_ubuntu:
|
||||
name: Cross-compile to aarch64-unknown-linux-gnu
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }} async send serialize vendored"
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
|
||||
build_armv7_cross_ubuntu:
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }} async send serialize vendored"
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
|
||||
test:
|
||||
@@ -124,14 +124,14 @@ jobs:
|
||||
override: true
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --release --features "${{ matrix.lua }} vendored"
|
||||
cargo test --release --features "${{ matrix.lua }} vendored async send serialize"
|
||||
cargo test --release --features "${{ matrix.lua }},vendored"
|
||||
cargo test --release --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua53)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua53' }}
|
||||
run: |
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }} vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }} vendored async send serialize" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }},vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }},vendored,async,send,serialize,macros" -- --ignored
|
||||
shell: bash
|
||||
|
||||
test_modules:
|
||||
@@ -157,8 +157,8 @@ jobs:
|
||||
override: true
|
||||
- name: Run ${{ matrix.lua }} module tests
|
||||
run: |
|
||||
(cd examples/module && cargo build --release --features "${{ matrix.lua }} vendored")
|
||||
(cd tests/module && cargo test --release --features "${{ matrix.lua }} vendored")
|
||||
(cd examples/module && cargo build --release --features "${{ matrix.lua }},vendored")
|
||||
(cd tests/module && cargo test --release --features "${{ matrix.lua }},vendored")
|
||||
shell: bash
|
||||
|
||||
test_modules_windows:
|
||||
@@ -210,4 +210,4 @@ jobs:
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --features "${{ matrix.lua }},vendored,async,send,serialize
|
||||
args: --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
## v0.6.0-beta.2
|
||||
|
||||
- [**Breaking**] Removed `AnyUserData::has_metamethod()`
|
||||
- Added `Thread::reset()` for luajit/lua54 to recycle threads.
|
||||
It's possible to attach a new function to a thread (coroutine).
|
||||
- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optinally capturing Rust variables.
|
||||
- Improved error reporting (`Error`'s `__tostring` method formats full stacktraces). This is useful in the module mode.
|
||||
|
||||
## v0.6.0-beta.1
|
||||
|
||||
- New `UserDataFields` API
|
||||
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.6.0-beta.1" # remember to update html_root_url and mlua_derive
|
||||
version = "0.6.0-beta.2" # remember to update html_root_url and mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||
edition = "2018"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
@@ -17,7 +17,7 @@ with async/await features and support of writing native lua modules in Rust.
|
||||
"""
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["lua53", "async", "send", "serialize"]
|
||||
features = ["lua53", "async", "send", "serialize", "macros"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
@@ -38,9 +38,10 @@ module = ["mlua_derive"]
|
||||
async = ["futures-core", "futures-task", "futures-util"]
|
||||
send = []
|
||||
serialize = ["serde", "erased-serde"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "0.5", optional = true, path = "mlua_derive" }
|
||||
mlua_derive = { version = "=0.6.0-beta.2", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "0.2", features = ["std"], default_features = false }
|
||||
once_cell = { version = "1.7" }
|
||||
num-traits = { version = "0.2.14" }
|
||||
|
||||
@@ -17,7 +17,7 @@ _safe_ (as far as it's possible), high level, easy to use, practical and flexibl
|
||||
|
||||
Started as [rlua](https://github.com/amethyst/rlua/tree/0.15.3) fork, `mlua` supports Lua 5.4, 5.3, 5.2 and 5.1 including LuaJIT (2.0.5 and 2.1 beta) and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
|
||||
|
||||
`mlua` tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platform and cross-compilation to `aarch64` (other targes are also supported).
|
||||
`mlua` tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platform and cross-compilation to `aarch64` (other targets are also supported).
|
||||
|
||||
[GitHub Actions]: https://github.com/khvzak/mlua/actions
|
||||
|
||||
@@ -25,7 +25,7 @@ Started as [rlua](https://github.com/amethyst/rlua/tree/0.15.3) fork, `mlua` sup
|
||||
|
||||
### Feature flags
|
||||
|
||||
`mlua` uses feature flags to reduce the amount of depenendies, compiled code and allow to choose only required set of features.
|
||||
`mlua` uses feature flags to reduce the amount of dependencies, compiled code and allow to choose only required set of features.
|
||||
Below is a list of the available feature flags. By default `mlua` does not enable any features.
|
||||
|
||||
* `lua54`: activate Lua [5.4] support
|
||||
@@ -73,7 +73,7 @@ With `serialize` feature flag enabled, `mlua` allows you to serialize/deserializ
|
||||
|
||||
### Compiling
|
||||
|
||||
You have to enable one of the features `lua54`, `lua53`, `lua52`, `lua51` or `luajit`, according to the choosen Lua version.
|
||||
You have to enable one of the features `lua54`, `lua53`, `lua52`, `lua51` or `luajit`, according to the chosen Lua version.
|
||||
|
||||
By default `mlua` uses `pkg-config` tool to find lua includes and libraries for the chosen Lua version.
|
||||
In most cases it works as desired, although sometimes could be more preferable to use a custom lua library.
|
||||
@@ -85,7 +85,7 @@ An example how to use them:
|
||||
my_project $ LUA_INC=$HOME/tmp/lua-5.2.4/src LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static cargo build
|
||||
```
|
||||
|
||||
`mlua` also supports vendored lua/luajit using the auxilary crates [lua-src](https://crates.io/crates/lua-src) and
|
||||
`mlua` also supports vendored lua/luajit using the auxiliary crates [lua-src](https://crates.io/crates/lua-src) and
|
||||
[luajit-src](https://crates.io/crates/luajit-src).
|
||||
Just enable the `vendored` feature and cargo will automatically build and link specified lua/luajit version. This is the easiest way to get started with `mlua`.
|
||||
|
||||
@@ -233,7 +233,7 @@ If you encounter them, a bug report would be very welcome:
|
||||
|
||||
+ If your program panics with a message that contains the string "mlua internal error", this is a bug.
|
||||
|
||||
+ Lua C API errors are handled by lonjmp. All instances where the Lua C API would otherwise longjmp over calling stack frames should be guarded against, except in internal callbacks where this is intentional. If you detect that `mlua` is triggering a longjmp over your Rust stack frames, this is a bug!
|
||||
+ Lua C API errors are handled by longjmp. All instances where the Lua C API would otherwise longjmp over calling stack frames should be guarded against, except in internal callbacks where this is intentional. If you detect that `mlua` is triggering a longjmp over your Rust stack frames, this is a bug!
|
||||
|
||||
+ If you detect that, after catching a panic or during a Drop triggered from a panic, a `Lua` or handle method is triggering other bugs or there is a Lua stack space leak, this is a bug. `mlua` instances are supposed to remain fully usable in the face of user generated panics. This guarantee does not extend to panics marked with "mlua internal error" simply because that is already indicative of a separate bug.
|
||||
|
||||
|
||||
@@ -22,12 +22,12 @@ pub fn probe_lua() -> PathBuf {
|
||||
|
||||
let need_lua_lib = cfg!(any(not(feature = "module"), target_os = "windows"));
|
||||
|
||||
if include_dir != "" {
|
||||
if !include_dir.is_empty() {
|
||||
if need_lua_lib {
|
||||
if lib_dir == "" {
|
||||
if lib_dir.is_empty() {
|
||||
panic!("LUA_LIB is not set");
|
||||
}
|
||||
if lua_lib == "" {
|
||||
if lua_lib.is_empty() {
|
||||
panic!("LUA_LIB_NAME is not set");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0-beta.2"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2018"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
@@ -11,7 +11,14 @@ license = "MIT"
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
macros = ["proc-macro-error", "itertools", "regex", "once_cell"]
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
proc-macro2 = { version = "1.0", features = ["span-locations"] }
|
||||
proc-macro-error = { version = "1.0", optional = true }
|
||||
syn = { version = "1.0", features = ["full"] }
|
||||
itertools = { version = "0.10", optional = true }
|
||||
regex = { version = "1.4", optional = true }
|
||||
once_cell = { version = "1.5", optional = true }
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
+86
-3
@@ -1,16 +1,20 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use quote::quote_spanned;
|
||||
use syn::{parse_macro_input, spanned::Spanned, AttributeArgs, Error, ItemFn};
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
use {
|
||||
crate::chunk::Chunk, proc_macro::TokenTree, proc_macro2::TokenStream as TokenStream2,
|
||||
proc_macro_error::proc_macro_error, quote::quote,
|
||||
};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let args = parse_macro_input!(attr as AttributeArgs);
|
||||
let item = parse_macro_input!(item as ItemFn);
|
||||
|
||||
if args.len() > 0 {
|
||||
if !args.is_empty() {
|
||||
let err = Error::new(Span::call_site(), "the number of arguments must be zero")
|
||||
.to_compile_error();
|
||||
return err.into();
|
||||
@@ -35,3 +39,82 @@ pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
|
||||
wrapped.into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
fn to_ident(tt: &TokenTree) -> TokenStream2 {
|
||||
let s: TokenStream = tt.clone().into();
|
||||
s.into()
|
||||
}
|
||||
|
||||
#[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, Value};
|
||||
use ::std::marker::PhantomData;
|
||||
use ::std::sync::Mutex;
|
||||
|
||||
fn annotate<'a, F: FnOnce(&'a Lua) -> Result<Value<'a>>>(f: F) -> F { f }
|
||||
|
||||
struct InnerChunk<'a, F: FnOnce(&'a Lua) -> Result<Value<'a>>>(Mutex<Option<F>>, PhantomData<&'a ()>);
|
||||
|
||||
impl<'lua, F> AsChunk<'lua> for InnerChunk<'lua, F>
|
||||
where
|
||||
F: FnOnce(&'lua Lua) -> Result<Value<'lua>>,
|
||||
{
|
||||
fn source(&self) -> &[u8] {
|
||||
(#source).as_bytes()
|
||||
}
|
||||
|
||||
fn env(&self, lua: &'lua Lua) -> Option<Result<Value<'lua>>> {
|
||||
if #caps_len > 0 {
|
||||
if let Ok(mut make_env) = self.0.lock() {
|
||||
if let Some(make_env) = make_env.take() {
|
||||
return Some(make_env(lua));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn mode(&self) -> Option<ChunkMode> {
|
||||
Some(ChunkMode::Text)
|
||||
}
|
||||
}
|
||||
|
||||
let make_env = annotate(move |lua: &Lua| -> Result<Value> {
|
||||
let globals = lua.globals();
|
||||
let env = lua.create_table()?;
|
||||
let meta = lua.create_table()?;
|
||||
meta.raw_set("__index", globals.clone())?;
|
||||
meta.raw_set("__newindex", globals)?;
|
||||
|
||||
// Add captured variables
|
||||
#(#caps)*
|
||||
|
||||
env.set_metatable(Some(meta));
|
||||
Ok(Value::Table(env))
|
||||
});
|
||||
|
||||
&InnerChunk(Mutex::new(Some(make_env)), PhantomData)
|
||||
}};
|
||||
|
||||
wrapped_code.into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
mod chunk;
|
||||
#[cfg(feature = "macros")]
|
||||
mod token;
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
use std::{
|
||||
cmp::{Eq, PartialEq},
|
||||
fmt::{self, Display, Formatter},
|
||||
iter::IntoIterator,
|
||||
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 {
|
||||
pub(crate) line: usize,
|
||||
pub(crate) column: usize,
|
||||
}
|
||||
|
||||
impl Pos {
|
||||
fn new(line: usize, column: usize) -> Self {
|
||||
Self { line, column }
|
||||
}
|
||||
|
||||
fn left(&self) -> Self {
|
||||
Self {
|
||||
line: self.line,
|
||||
column: self.column.saturating_sub(1),
|
||||
}
|
||||
}
|
||||
|
||||
fn right(&self) -> Self {
|
||||
Self {
|
||||
line: self.line,
|
||||
column: self.column.saturating_add(1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn span_pos(span: &Span) -> (Pos, Pos) {
|
||||
let span2: Span2 = (*span).into();
|
||||
let start = span2.start();
|
||||
let end = span2.end();
|
||||
|
||||
// In stable, line/column information is not provided
|
||||
// and set to 0 (line is 1-indexed)
|
||||
if start.line == 0 || end.line == 0 {
|
||||
return fallback_span_pos(span);
|
||||
}
|
||||
|
||||
(
|
||||
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_error::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 {
|
||||
/// No attribute
|
||||
None,
|
||||
/// Starts with `$`
|
||||
Cap,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Token {
|
||||
source: String,
|
||||
tree: TokenTree,
|
||||
start: Pos,
|
||||
end: Pos,
|
||||
attr: TokenAttr,
|
||||
}
|
||||
|
||||
impl PartialEq for Token {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.source == other.source && self.attr == other.attr
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Token {}
|
||||
|
||||
impl Token {
|
||||
fn new(tree: TokenTree) -> Self {
|
||||
let (start, end) = span_pos(&tree.span());
|
||||
Self {
|
||||
source: tree.to_string(),
|
||||
start,
|
||||
end,
|
||||
tree,
|
||||
attr: TokenAttr::None,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_delim(source: String, tree: TokenTree, open: bool) -> Self {
|
||||
let (start, end) = span_pos(&tree.span());
|
||||
let (start, end) = if open {
|
||||
(start, start.right())
|
||||
} else {
|
||||
(end.left(), end)
|
||||
};
|
||||
|
||||
Self {
|
||||
source,
|
||||
tree,
|
||||
start,
|
||||
end,
|
||||
attr: TokenAttr::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn tree(&self) -> &TokenTree {
|
||||
&self.tree
|
||||
}
|
||||
|
||||
pub(crate) fn is_cap(&self) -> bool {
|
||||
self.attr == TokenAttr::Cap
|
||||
}
|
||||
|
||||
pub(crate) fn start(&self) -> Pos {
|
||||
self.start
|
||||
}
|
||||
|
||||
pub(crate) fn end(&self) -> Pos {
|
||||
self.end
|
||||
}
|
||||
|
||||
fn is(&self, s: &str) -> bool {
|
||||
self.source == s
|
||||
}
|
||||
|
||||
fn attr(mut self, attr: TokenAttr) -> Self {
|
||||
self.attr = attr;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Tokens(pub(crate) Vec<Token>);
|
||||
|
||||
impl Tokens {
|
||||
pub(crate) fn retokenize(tt: TokenStream) -> Tokens {
|
||||
Tokens(
|
||||
tt.into_iter()
|
||||
.map(Tokens::from)
|
||||
.flatten()
|
||||
.peekable()
|
||||
.batching(|iter| {
|
||||
// Find variable tokens
|
||||
let t = iter.next()?;
|
||||
if t.is("$") {
|
||||
// `$` + `ident` => `$ident`
|
||||
let t = iter.next().expect("$ must trail an identifier");
|
||||
Some(t.attr(TokenAttr::Cap))
|
||||
} else {
|
||||
Some(t)
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Tokens {
|
||||
type Item = Token;
|
||||
type IntoIter = IntoIter<Token>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TokenTree> for Tokens {
|
||||
fn from(tt: TokenTree) -> Self {
|
||||
let tts = match tt.clone() {
|
||||
TokenTree::Group(g) => {
|
||||
let (b, e) = match g.delimiter() {
|
||||
Delimiter::Parenthesis => ("(", ")"),
|
||||
Delimiter::Brace => ("{", "}"),
|
||||
Delimiter::Bracket => ("[", "]"),
|
||||
Delimiter::None => ("", ""),
|
||||
};
|
||||
let (b, e) = (b.into(), e.into());
|
||||
|
||||
vec![Token::new_delim(b, tt.clone(), true)]
|
||||
.into_iter()
|
||||
.chain(g.stream().into_iter().map(Tokens::from).flatten())
|
||||
.chain(vec![Token::new_delim(e, tt, false)])
|
||||
.collect()
|
||||
}
|
||||
_ => vec![Token::new(tt)],
|
||||
};
|
||||
Tokens(tts)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Token {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.source)
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
+5
-3
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::io::Error as IoError;
|
||||
@@ -88,7 +90,7 @@ pub enum Error {
|
||||
},
|
||||
/// [`Thread::resume`] was called on an inactive coroutine.
|
||||
///
|
||||
/// A coroutine is inactive if its main function has returned or if an error has occured inside
|
||||
/// A coroutine is inactive if its main function has returned or if an error has occurred inside
|
||||
/// the coroutine.
|
||||
///
|
||||
/// [`Thread::status`] can be used to check if the coroutine can be resumed without causing this
|
||||
@@ -152,7 +154,7 @@ pub enum Error {
|
||||
/// Original error returned by the Rust code.
|
||||
cause: Arc<Error>,
|
||||
},
|
||||
/// A Rust panic that was previosly resumed, returned again.
|
||||
/// A Rust panic that was previously resumed, returned again.
|
||||
///
|
||||
/// This error can occur only when a Rust panic resumed previously was recovered
|
||||
/// and returned again.
|
||||
@@ -246,7 +248,7 @@ impl fmt::Display for Error {
|
||||
write!(fmt, "RegistryKey used from different Lua state")
|
||||
}
|
||||
Error::CallbackError { ref traceback, .. } => {
|
||||
write!(fmt, "callback error: {}", traceback)
|
||||
write!(fmt, "callback error\n{}", traceback)
|
||||
}
|
||||
Error::PreviouslyResumedPanic => {
|
||||
write!(fmt, "previously resumed panic returned again")
|
||||
|
||||
+2
-1
@@ -538,6 +538,7 @@ pub unsafe fn lua_stringtonumber(L: *mut lua_State, s: *const c_char) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
#[allow(clippy::branches_sharing_code)]
|
||||
pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void {
|
||||
use super::glue::LUA_EXTRASPACE;
|
||||
|
||||
@@ -679,7 +680,7 @@ pub unsafe fn luaL_traceback(
|
||||
msg: *const c_char,
|
||||
mut level: c_int,
|
||||
) {
|
||||
let mut ar: lua_Debug = std::mem::zeroed();
|
||||
let mut ar: lua_Debug = mem::zeroed();
|
||||
let top = lua_gettop(L);
|
||||
let numlevels = compat53_countlevels(L1);
|
||||
let mark = if numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 {
|
||||
|
||||
+17
-1
@@ -156,8 +156,13 @@ extern "C" {
|
||||
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
|
||||
pub fn lua_close(L: *mut lua_State);
|
||||
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
pub fn lua_resetthread(L: *mut lua_State) -> c_int;
|
||||
#[link_name = "lua_resetthread"]
|
||||
pub fn lua_resetthread_54(L: *mut lua_State) -> c_int;
|
||||
#[cfg(all(feature = "luajit", feature = "vendored"))]
|
||||
#[link_name = "lua_resetthread"]
|
||||
pub fn lua_resetthread_jit(L: *mut lua_State, th: *mut lua_State);
|
||||
|
||||
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
|
||||
|
||||
@@ -216,6 +221,17 @@ extern "C" {
|
||||
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
pub unsafe fn lua_resetthread(_L: *mut lua_State, th: *mut lua_State) -> c_int {
|
||||
#[cfg(all(feature = "luajit", feature = "vendored"))]
|
||||
{
|
||||
lua_resetthread_jit(_L, th);
|
||||
LUA_OK
|
||||
}
|
||||
#[cfg(feature = "lua54")]
|
||||
lua_resetthread_54(th)
|
||||
}
|
||||
|
||||
// Comparison and arithmetic functions
|
||||
pub const LUA_OPADD: c_int = 0;
|
||||
pub const LUA_OPSUB: c_int = 1;
|
||||
|
||||
+9
-4
@@ -160,8 +160,8 @@ pub use self::lua::{
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
pub use self::lua::{
|
||||
lua_getiuservalue, lua_newuserdatauv, lua_resetthread, lua_setcstacklimit, lua_setiuservalue,
|
||||
lua_setwarnf, lua_toclose, lua_warning,
|
||||
lua_getiuservalue, lua_newuserdatauv, lua_setcstacklimit, lua_setiuservalue, lua_setwarnf,
|
||||
lua_toclose, lua_warning,
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
@@ -170,6 +170,9 @@ pub use self::lua::{lua_isyieldable, lua_version};
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
pub use self::lua::{lua_callk, lua_pcallk, lua_upvalueid, lua_upvaluejoin, lua_yieldk};
|
||||
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
pub use self::lua::lua_resetthread;
|
||||
|
||||
// auxiliary library types
|
||||
pub use self::lauxlib::luaL_Reg;
|
||||
|
||||
@@ -253,16 +256,18 @@ pub use self::lualib::{LUA_FFILIBNAME, LUA_JITLIBNAME};
|
||||
// Not actually defined in lua.h / luaconf.h
|
||||
pub const LUA_MAX_UPVALUES: c_int = 255;
|
||||
|
||||
// Copied from https://github.com/rust-lang/rust/blob/master/src/libstd/sys_common/alloc.rs
|
||||
// Copied from https://github.com/rust-lang/rust/blob/master/library/std/src/sys/common/alloc.rs
|
||||
#[cfg(all(any(
|
||||
target_arch = "x86",
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "sparc",
|
||||
target_arch = "asmjs",
|
||||
target_arch = "wasm32",
|
||||
target_arch = "hexagon"
|
||||
target_arch = "hexagon",
|
||||
target_arch = "riscv32"
|
||||
)))]
|
||||
pub const SYS_MIN_ALIGN: usize = 8;
|
||||
#[cfg(all(any(
|
||||
|
||||
+37
-31
@@ -31,21 +31,37 @@ size_t MLUA_WRAPPED_PANIC_SIZE = 0;
|
||||
const void *MLUA_WRAPPED_ERROR_KEY = NULL;
|
||||
const void *MLUA_WRAPPED_PANIC_KEY = NULL;
|
||||
|
||||
extern void wrapped_error_traceback(lua_State *L, int error_idx, void *error_ud,
|
||||
int has_traceback);
|
||||
extern void wrapped_error_traceback(lua_State *L, int error_idx,
|
||||
int traceback_idx);
|
||||
|
||||
extern int mlua_hook_proc(lua_State *L, lua_Debug *ar);
|
||||
|
||||
#define max(a, b) (a > b ? a : b)
|
||||
|
||||
// I believe luaL_traceback < 5.4 requires this much free stack to not error.
|
||||
// 5.4 uses luaL_Buffer
|
||||
const int LUA_TRACEBACK_STACK = 11;
|
||||
|
||||
typedef struct {
|
||||
const char *data;
|
||||
size_t len;
|
||||
} StringArg;
|
||||
|
||||
static void handle_wrapped_error(lua_State *L) {
|
||||
if (lua_checkstack(L, LUA_TRACEBACK_STACK) != 0) {
|
||||
luaL_traceback(L, L, NULL, 0);
|
||||
// Convert to CallbackError and attach traceback
|
||||
wrapped_error_traceback(L, -2, -1);
|
||||
lua_pop(L, 1);
|
||||
} else {
|
||||
// Convert to CallbackError with error message as a traceback
|
||||
wrapped_error_traceback(L, -1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// A wrapper around Rust function to protect from triggering longjmp in Rust.
|
||||
// Rust callback expected to return -1 in case of errors or number of output
|
||||
// values.
|
||||
// Rust callback expected to return positive number of output values or
|
||||
// -1 in case of error, -2 in case of panic.
|
||||
static int lua_call_rust(lua_State *L) {
|
||||
int nargs = lua_gettop(L);
|
||||
|
||||
@@ -67,7 +83,10 @@ static int lua_call_rust(lua_State *L) {
|
||||
lua_CFunction rust_callback = lua_touserdata(L, lua_upvalueindex(1));
|
||||
|
||||
int ret = rust_callback(L);
|
||||
if (ret == -1) {
|
||||
if (ret < 0) {
|
||||
if (ret == -1 /* WrappedError */) {
|
||||
handle_wrapped_error(L);
|
||||
}
|
||||
lua_error(L);
|
||||
}
|
||||
|
||||
@@ -79,7 +98,10 @@ void lua_call_mlua_hook_proc(lua_State *L, lua_Debug *ar) {
|
||||
lua_newuserdata(L, max(MLUA_WRAPPED_ERROR_SIZE, MLUA_WRAPPED_PANIC_SIZE));
|
||||
lua_rotate(L, 1, 1);
|
||||
int ret = mlua_hook_proc(L, ar);
|
||||
if (ret == -1) {
|
||||
if (ret < 0) {
|
||||
if (ret == -1 /* WrappedError */) {
|
||||
handle_wrapped_error(L);
|
||||
}
|
||||
lua_error(L);
|
||||
}
|
||||
}
|
||||
@@ -393,42 +415,26 @@ int is_wrapped_struct(lua_State *state, int index, const void *key) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// Takes an error at the top of the stack, and if it is a WrappedError, converts
|
||||
// it to an Error::CallbackError with a traceback, if it is some lua type,
|
||||
// prints the error along with a traceback, and if it is a WrappedPanic, does
|
||||
// Takes an error at the top of the stack and converts Lua errors into a string
|
||||
// with attached traceback. If the error is a WrappedError or WrappedPanic, does
|
||||
// not modify it. This function does its best to avoid triggering another error
|
||||
// and shadowing previous rust errors, but it may trigger Lua errors that shadow
|
||||
// rust errors under certain memory conditions. This function ensures that such
|
||||
// behavior will *never* occur with a rust panic, however.
|
||||
// and shadowing previous rust errors.
|
||||
int error_traceback(lua_State *state) {
|
||||
// I believe luaL_traceback < 5.4 requires this much free stack to not error.
|
||||
// 5.4 uses luaL_Buffer
|
||||
const int LUA_TRACEBACK_STACK = 11;
|
||||
|
||||
if (lua_checkstack(state, 2) == 0) {
|
||||
// If we don't have enough stack space to even check the error type, do
|
||||
// nothing so we don't risk shadowing a rust panic.
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (is_wrapped_struct(state, -1, MLUA_WRAPPED_ERROR_KEY) != 0) {
|
||||
int error_idx = lua_absindex(state, -1);
|
||||
// lua_newuserdata and luaL_traceback may error
|
||||
void *error_ud = lua_newuserdata(state, MLUA_WRAPPED_ERROR_SIZE);
|
||||
int has_traceback = 0;
|
||||
if (lua_checkstack(state, LUA_TRACEBACK_STACK) != 0) {
|
||||
luaL_traceback(state, state, NULL, 0);
|
||||
has_traceback = 1;
|
||||
}
|
||||
wrapped_error_traceback(state, error_idx, error_ud, has_traceback);
|
||||
if (MLUA_WRAPPED_PANIC_KEY == NULL ||
|
||||
is_wrapped_struct(state, -1, MLUA_WRAPPED_PANIC_KEY) ||
|
||||
is_wrapped_struct(state, -1, MLUA_WRAPPED_ERROR_KEY)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (MLUA_WRAPPED_PANIC_KEY != NULL &&
|
||||
!is_wrapped_struct(state, -1, MLUA_WRAPPED_PANIC_KEY) &&
|
||||
lua_checkstack(state, LUA_TRACEBACK_STACK) != 0) {
|
||||
const char *s = luaL_tolstring(state, -1, NULL);
|
||||
luaL_traceback(state, state, s, 0);
|
||||
const char *s = luaL_tolstring(state, -1, NULL);
|
||||
if (lua_checkstack(state, LUA_TRACEBACK_STACK) != 0) {
|
||||
luaL_traceback(state, state, s, 1);
|
||||
lua_remove(state, -2);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ impl<'lua> Function<'lua> {
|
||||
/// Returns a Feature that, when polled, calls `self`, passing `args` as function arguments,
|
||||
/// and drives the execution.
|
||||
///
|
||||
/// Internaly it wraps the function to an [`AsyncThread`].
|
||||
/// Internally it wraps the function to an [`AsyncThread`].
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ use crate::util::callback_error;
|
||||
/// The `Debug` structure is provided as a parameter to the hook function set with
|
||||
/// [`Lua::set_hook`]. You may call the methods on this structure to retrieve information about the
|
||||
/// Lua code executing at the time that the hook function was called. Further information can be
|
||||
/// found in the [Lua 5.3 documentaton][lua_doc].
|
||||
/// found in the [Lua 5.3 documentation][lua_doc].
|
||||
///
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#lua_Debug
|
||||
/// [`Lua::set_hook`]: struct.Lua.html#method.set_hook
|
||||
|
||||
+60
-8
@@ -72,7 +72,7 @@
|
||||
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||
|
||||
// mlua types in rustdoc of other crates get linked to here.
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.6.0-beta.1")]
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.6.0-beta.2")]
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
@@ -98,12 +98,13 @@ mod userdata;
|
||||
mod util;
|
||||
mod value;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use crate::ffi::lua_State;
|
||||
|
||||
pub use crate::error::{Error, ExternalError, ExternalResult, Result};
|
||||
pub use crate::function::Function;
|
||||
pub use crate::hook::{Debug, DebugNames, DebugSource, DebugStack, HookTriggers};
|
||||
pub use crate::lua::{Chunk, ChunkMode, GCMode, Lua, LuaOptions};
|
||||
pub use crate::lua::{AsChunk, Chunk, ChunkMode, GCMode, Lua, LuaOptions};
|
||||
pub use crate::multi::Variadic;
|
||||
pub use crate::scope::Scope;
|
||||
pub use crate::stdlib::StdLib;
|
||||
@@ -111,7 +112,9 @@ pub use crate::string::String;
|
||||
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
|
||||
pub use crate::thread::{Thread, ThreadStatus};
|
||||
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
|
||||
pub use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods};
|
||||
pub use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
|
||||
};
|
||||
pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -126,11 +129,60 @@ pub mod prelude;
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub mod serde;
|
||||
|
||||
// Re-export #[mlua_derive::lua_module].
|
||||
#[cfg(feature = "mlua_derive")]
|
||||
#[cfg(any(feature = "mlua_derive"))]
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate mlua_derive;
|
||||
#[cfg(feature = "mlua_derive")]
|
||||
#[doc(hidden)]
|
||||
pub use 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 Rust types needs to implement [`UserData`] or [`ToLua`] 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).
|
||||
///
|
||||
/// Other minor limitations:
|
||||
///
|
||||
/// - Certain escape codes in string literals.
|
||||
/// (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`]: trait.AsChunk.html
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
/// [`ToLua`]: trait.ToLua.html
|
||||
#[cfg(any(feature = "macros"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
pub use mlua_derive::chunk;
|
||||
|
||||
#[cfg(any(feature = "module"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
|
||||
pub use mlua_derive::lua_module;
|
||||
|
||||
+63
-19
@@ -97,7 +97,7 @@ pub enum GCMode {
|
||||
Generational,
|
||||
}
|
||||
|
||||
/// Controls Lua interpreter behaviour such as Rust panics handling.
|
||||
/// Controls Lua interpreter behavior such as Rust panics handling.
|
||||
#[derive(Clone, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub struct LuaOptions {
|
||||
@@ -107,7 +107,7 @@ pub struct LuaOptions {
|
||||
/// Also in Lua 5.1 adds ability to provide arguments to [`xpcall`] similar to Lua >= 5.2.
|
||||
///
|
||||
/// If enabled, keeps [`pcall`]/[`xpcall`] unmodified.
|
||||
/// Panics are still automatically resumed if returned back to the Rust side.
|
||||
/// Panics are still automatically resumed if returned to the Rust side.
|
||||
///
|
||||
/// Default: **true**
|
||||
///
|
||||
@@ -125,7 +125,7 @@ impl Default for LuaOptions {
|
||||
}
|
||||
|
||||
impl LuaOptions {
|
||||
/// Retruns a new instance of `LuaOptions` with default parameters.
|
||||
/// Returns a new instance of `LuaOptions` with default parameters.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
@@ -206,7 +206,7 @@ impl Lua {
|
||||
|
||||
/// Creates a new Lua state and loads the specified safe subset of the standard libraries.
|
||||
///
|
||||
/// Use the [`StdLib`] flags to specifiy the libraries you want to load.
|
||||
/// Use the [`StdLib`] flags to specify the libraries you want to load.
|
||||
///
|
||||
/// # Safety
|
||||
/// The created Lua state would have _some_ safety guarantees and would not allow to load unsafe
|
||||
@@ -243,7 +243,7 @@ impl Lua {
|
||||
|
||||
/// Creates a new Lua state and loads the specified subset of the standard libraries.
|
||||
///
|
||||
/// Use the [`StdLib`] flags to specifiy the libraries you want to load.
|
||||
/// Use the [`StdLib`] flags to specify the libraries you want to load.
|
||||
///
|
||||
/// # Safety
|
||||
/// The created Lua state will not have safety guarantees and allow to load C modules.
|
||||
@@ -300,7 +300,7 @@ impl Lua {
|
||||
if !new_ptr.is_null() {
|
||||
mem_info.used_memory += mem_diff;
|
||||
} else if !ptr.is_null() && nsize < osize {
|
||||
// Should not happend
|
||||
// Should not happen
|
||||
alloc::handle_alloc_error(new_layout);
|
||||
}
|
||||
|
||||
@@ -459,7 +459,7 @@ impl Lua {
|
||||
|
||||
/// Loads the specified subset of the standard libraries into an existing Lua state.
|
||||
///
|
||||
/// Use the [`StdLib`] flags to specifiy the libraries you want to load.
|
||||
/// Use the [`StdLib`] flags to specify the libraries you want to load.
|
||||
///
|
||||
/// [`StdLib`]: struct.StdLib.html
|
||||
pub fn load_from_std_lib(&self, libs: StdLib) -> Result<()> {
|
||||
@@ -769,14 +769,14 @@ impl Lua {
|
||||
/// [`Chunk::exec`]: struct.Chunk.html#method.exec
|
||||
pub fn load<'lua, 'a, S>(&'lua self, source: &'a S) -> Chunk<'lua, 'a>
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
S: AsChunk<'lua> + ?Sized,
|
||||
{
|
||||
Chunk {
|
||||
lua: self,
|
||||
source: source.as_ref(),
|
||||
name: None,
|
||||
env: None,
|
||||
mode: None,
|
||||
source: source.source(),
|
||||
name: source.name(),
|
||||
env: source.env(self),
|
||||
mode: source.mode(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1840,6 +1840,7 @@ impl Lua {
|
||||
}
|
||||
|
||||
let lua = &mut *lua;
|
||||
lua.state = state;
|
||||
|
||||
// Try to get an outer poll waker
|
||||
let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
@@ -2009,7 +2010,7 @@ pub struct Chunk<'lua, 'a> {
|
||||
lua: &'lua Lua,
|
||||
source: &'a [u8],
|
||||
name: Option<CString>,
|
||||
env: Option<Value<'lua>>,
|
||||
env: Option<Result<Value<'lua>>>,
|
||||
mode: Option<ChunkMode>,
|
||||
}
|
||||
|
||||
@@ -2020,6 +2021,32 @@ pub enum ChunkMode {
|
||||
Binary,
|
||||
}
|
||||
|
||||
/// Trait for types [loadable by Lua] and convertible to a [`Chunk`]
|
||||
///
|
||||
/// [loadable by Lua]: https://www.lua.org/manual/5.3/manual.html#3.3.2
|
||||
/// [`Chunk`]: struct.Chunk.html
|
||||
pub trait AsChunk<'lua> {
|
||||
/// Returns chunk data (can be text or binary)
|
||||
fn source(&self) -> &[u8];
|
||||
|
||||
/// Returns optional chunk name
|
||||
fn name(&self) -> Option<CString> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns optional chunk [environment]
|
||||
///
|
||||
/// [environment]: https://www.lua.org/manual/5.3/manual.html#2.2
|
||||
fn env(&self, _lua: &'lua Lua) -> Option<Result<Value<'lua>>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns optional chunk mode (text or binary)
|
||||
fn mode(&self) -> Option<ChunkMode> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// Sets the name of this chunk, which results in more informative error traces.
|
||||
pub fn set_name<S: AsRef<[u8]> + ?Sized>(mut self, name: &S) -> Result<Chunk<'lua, 'a>> {
|
||||
@@ -2045,7 +2072,8 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// necessary to populate the environment in order for scripts using custom environments to be
|
||||
/// useful.
|
||||
pub fn set_environment<V: ToLua<'lua>>(mut self, env: V) -> Result<Chunk<'lua, 'a>> {
|
||||
self.env = Some(env.to_lua(self.lua)?);
|
||||
// Prefer to propagate errors here and wrap to `Ok`
|
||||
self.env = Some(Ok(env.to_lua(self.lua)?));
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
@@ -2099,7 +2127,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
} else if let Ok(function) = self.lua.load_chunk(
|
||||
&self.expression_source(),
|
||||
self.name.as_ref(),
|
||||
self.env.clone(),
|
||||
self.env()?,
|
||||
self.mode,
|
||||
) {
|
||||
function.call(())
|
||||
@@ -2127,7 +2155,10 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
} else if let Ok(function) = self.lua.load_chunk(
|
||||
&self.expression_source(),
|
||||
self.name.as_ref(),
|
||||
self.env.clone(),
|
||||
match self.env() {
|
||||
Ok(env) => env,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
},
|
||||
self.mode,
|
||||
) {
|
||||
function.call_async(())
|
||||
@@ -2136,14 +2167,14 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Load the chunk function and call it with the given arguemnts.
|
||||
/// Load the chunk function and call it with the given arguments.
|
||||
///
|
||||
/// This is equivalent to `into_function` and calling the resulting function.
|
||||
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(self, args: A) -> Result<R> {
|
||||
self.into_function()?.call(args)
|
||||
}
|
||||
|
||||
/// Load the chunk function and asynchronously call it with the given arguemnts.
|
||||
/// Load the chunk function and asynchronously call it with the given arguments.
|
||||
///
|
||||
/// See [`Chunk::call`] for more details.
|
||||
///
|
||||
@@ -2169,7 +2200,14 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// This simply compiles the chunk without actually executing it.
|
||||
pub fn into_function(self) -> Result<Function<'lua>> {
|
||||
self.lua
|
||||
.load_chunk(self.source, self.name.as_ref(), self.env, self.mode)
|
||||
.load_chunk(self.source, self.name.as_ref(), self.env()?, self.mode)
|
||||
}
|
||||
|
||||
fn env(&self) -> Result<Option<Value<'lua>>> {
|
||||
match self.env {
|
||||
None => Ok(None),
|
||||
Some(ref env) => env.clone().map(Some),
|
||||
}
|
||||
}
|
||||
|
||||
fn expression_source(&self) -> Vec<u8> {
|
||||
@@ -2180,6 +2218,12 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: AsRef<[u8]> + ?Sized> AsChunk<'lua> for T {
|
||||
fn source(&self) -> &[u8] {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result<()> {
|
||||
#[cfg(feature = "luajit")]
|
||||
// Stop collector during library initialization
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
@@ -9,6 +9,7 @@ pub use crate::{
|
||||
Result as LuaResult, String as LuaString, Table as LuaTable, TableExt as LuaTableExt,
|
||||
TablePairs as LuaTablePairs, TableSequence as LuaTableSequence, Thread as LuaThread,
|
||||
ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti, UserData as LuaUserData,
|
||||
UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
|
||||
UserDataMethods as LuaUserDataMethods, Value as LuaValue,
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ pub struct Serializer<'lua> {
|
||||
options: Options,
|
||||
}
|
||||
|
||||
/// A struct with options to change default serializer behaviour.
|
||||
/// A struct with options to change default serializer behavior.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub struct Options {
|
||||
|
||||
+3
-3
@@ -243,10 +243,10 @@ impl<'lua> Table<'lua> {
|
||||
/// Removes a key from the table.
|
||||
///
|
||||
/// If `key` is an integer, mlua shifts down the elements from `table[key+1]`,
|
||||
/// and erases element `table[key]`. The complexity is O(n) in worst case,
|
||||
/// and erases element `table[key]`. The complexity is O(n) in the worst case,
|
||||
/// where n is the table length.
|
||||
///
|
||||
/// For othey key types this is equivalent to setting `table[key] = nil`.
|
||||
/// For other key types this is equivalent to setting `table[key] = nil`.
|
||||
pub fn raw_remove<K: ToLua<'lua>>(&self, key: K) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
@@ -378,7 +378,7 @@ impl<'lua> Table<'lua> {
|
||||
/// Consume this table and return an iterator over all values in the sequence part of the table.
|
||||
///
|
||||
/// The iterator will yield all values `t[1]`, `t[2]`, and so on, until a `nil` value is
|
||||
/// encountered. This mirrors the behaviour of Lua's `ipairs` function and will invoke the
|
||||
/// encountered. This mirrors the behavior of Lua's `ipairs` function and will invoke the
|
||||
/// `__index` metamethod according to the usual rules. However, the deprecated `__ipairs`
|
||||
/// metatable will not be called.
|
||||
///
|
||||
|
||||
@@ -7,6 +7,9 @@ use crate::types::LuaRef;
|
||||
use crate::util::{assert_stack, check_stack, pop_error, StackGuard};
|
||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
||||
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored"), doc))]
|
||||
use crate::function::Function;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::{
|
||||
@@ -170,6 +173,43 @@ impl<'lua> Thread<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// in closing methods.
|
||||
///
|
||||
/// In [LuaJIT]: resets to the initial state of a newly created Lua thread.
|
||||
/// Lua threads in arbitrary states (like yielded or errored) can be reset properly.
|
||||
///
|
||||
/// Sets a Lua function for the thread afterwards.
|
||||
///
|
||||
/// Requires `feature = "lua54"` OR `feature = "luajit,vendored"`
|
||||
///
|
||||
/// [Lua 5.4]: https://www.lua.org/manual/5.4/manual.html#lua_resetthread
|
||||
/// [LuaJIT]: https://github.com/openresty/luajit2#lua_resetthread
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored"), doc))]
|
||||
pub fn reset(&self, func: Function<'lua>) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 2)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let thread_state = ffi::lua_tothread(lua.state, -1);
|
||||
|
||||
let ret = ffi::lua_resetthread(lua.state, thread_state);
|
||||
if ret != ffi::LUA_OK {
|
||||
return Err(pop_error(thread_state, ret));
|
||||
}
|
||||
|
||||
lua.push_ref(&func.0);
|
||||
ffi::lua_xmove(lua.state, thread_state, 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts Thread to an AsyncThread which implements Future and Stream traits.
|
||||
///
|
||||
/// `args` are passed as arguments to the thread function for first call.
|
||||
|
||||
+5
-27
@@ -21,7 +21,10 @@ use crate::lua::Lua;
|
||||
use crate::table::{Table, TablePairs};
|
||||
use crate::types::{LuaRef, MaybeSend};
|
||||
use crate::util::{check_stack, get_destructed_userdata_metatable, get_userdata, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti, Value};
|
||||
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
|
||||
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
use crate::value::Value;
|
||||
|
||||
/// Kinds of metamethods that can be overridden.
|
||||
///
|
||||
@@ -758,7 +761,7 @@ impl<'lua> AnyUserData<'lua> {
|
||||
/// Returns a metatable of this `UserData`.
|
||||
///
|
||||
/// Returned [`UserDataMetatable`] object wraps the original metatable and
|
||||
/// provides safe access to it methods.
|
||||
/// provides safe access to its methods.
|
||||
///
|
||||
/// For `T: UserData + 'static` returned metatable is shared among all instances of type `T`.
|
||||
///
|
||||
@@ -767,31 +770,6 @@ impl<'lua> AnyUserData<'lua> {
|
||||
self.get_raw_metatable().map(UserDataMetatable)
|
||||
}
|
||||
|
||||
/// Checks for a metamethod in this `AnyUserData`.
|
||||
///
|
||||
/// This function is deprecated and will be removed in v0.7.
|
||||
/// Please use [`get_metatable`] function instead.
|
||||
///
|
||||
/// [`get_metatable`]: #method.get_metatable
|
||||
#[deprecated(
|
||||
since = "0.6.0",
|
||||
note = "Please use the get_metatable function instead"
|
||||
)]
|
||||
pub fn has_metamethod(&self, method: MetaMethod) -> Result<bool> {
|
||||
match self.get_raw_metatable() {
|
||||
Ok(mt) => {
|
||||
let name = self.0.lua.create_string(method.validate()?.name())?;
|
||||
if let Value::Nil = mt.raw_get(name)? {
|
||||
Ok(false)
|
||||
} else {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
Err(Error::UserDataTypeMismatch) => Ok(false),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_raw_metatable(&self) -> Result<Table<'lua>> {
|
||||
unsafe {
|
||||
let lua = self.0.lua;
|
||||
|
||||
+37
-28
@@ -1,5 +1,6 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt::Write;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||
@@ -44,7 +45,7 @@ pub struct StackGuard {
|
||||
}
|
||||
|
||||
impl StackGuard {
|
||||
// Creates a StackGuard instance with wa record of the stack size, and on Drop will check the
|
||||
// Creates a StackGuard instance with record of the stack size, and on Drop will check the
|
||||
// stack size and drop any extra elements. If the stack size at the end is *smaller* than at
|
||||
// the beginning, this is considered a fatal logic error and will result in a panic.
|
||||
pub unsafe fn new(state: *mut ffi::lua_State) -> StackGuard {
|
||||
@@ -323,45 +324,30 @@ where
|
||||
ptr::write(error_ud as *mut WrappedPanic, WrappedPanic(Some(p)));
|
||||
get_gc_metatable_for::<WrappedPanic>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
-1
|
||||
-2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A part of the C shim (error_traceback).
|
||||
// Receives absolute index of error in the stack, a pointer to pre-allocated WrappedError memory,
|
||||
// and optional boolean flag if a traceback value is on top of the stack.
|
||||
// A part of the C shim for errors handling.
|
||||
// Receives indexes of error and traceback (optional) in the stack.
|
||||
// Converts error into a `CallbackError` and attaches the traceback provided.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wrapped_error_traceback(
|
||||
state: *mut ffi::lua_State,
|
||||
error_idx: c_int,
|
||||
error_ud: *mut c_void,
|
||||
has_traceback: c_int,
|
||||
traceback_idx: c_int,
|
||||
) {
|
||||
let error = mlua_expect!(
|
||||
get_wrapped_error(state, error_idx).as_ref(),
|
||||
let wrapped_error = mlua_expect!(
|
||||
get_gc_userdata::<WrappedError>(state, error_idx).as_mut(),
|
||||
"cannot get <WrappedError>"
|
||||
);
|
||||
let traceback = if has_traceback != 0 {
|
||||
let traceback = to_string(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
traceback
|
||||
} else {
|
||||
"<not enough stack space for traceback>".to_owned()
|
||||
let traceback = match traceback_idx {
|
||||
0 => "<not enough stack space for traceback>".to_string(),
|
||||
_ => to_string(state, traceback_idx),
|
||||
};
|
||||
|
||||
let error = error.clone();
|
||||
ffi::lua_remove(state, -2); // Remove original error
|
||||
|
||||
ptr::write(
|
||||
error_ud as *mut WrappedError,
|
||||
WrappedError(Error::CallbackError {
|
||||
traceback,
|
||||
cause: Arc::new(error),
|
||||
}),
|
||||
);
|
||||
get_gc_metatable_for::<WrappedError>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
let cause = Arc::new(wrapped_error.0.clone());
|
||||
wrapped_error.0 = Error::CallbackError { traceback, cause };
|
||||
}
|
||||
|
||||
// Returns Lua main thread for Lua >= 5.2 or checks that the passed thread is main for Lua 5.1.
|
||||
@@ -475,6 +461,29 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const
|
||||
// be possible to make this consume arbitrary amounts of memory (for example, some
|
||||
// kind of recursive error structure?)
|
||||
let _ = write!(&mut (*err_buf), "{}", error);
|
||||
// Find first two sources that caused the error
|
||||
let mut source1 = error.source();
|
||||
let mut source0 = source1.and_then(|s| s.source());
|
||||
while let Some(source) = source0.and_then(|s| s.source()) {
|
||||
source1 = source0;
|
||||
source0 = Some(source);
|
||||
}
|
||||
match (source1, source0) {
|
||||
(_, Some(error0)) if error0.to_string().contains("\nstack traceback:\n") => {
|
||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error0);
|
||||
}
|
||||
(Some(error1), Some(error0)) => {
|
||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error0);
|
||||
let s = error1.to_string();
|
||||
if let Some(traceback) = s.splitn(2, "\nstack traceback:\n").nth(1) {
|
||||
let _ = write!(&mut (*err_buf), "\nstack traceback:\n{}", traceback);
|
||||
}
|
||||
}
|
||||
(Some(error1), None) => {
|
||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error1);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(err_buf)
|
||||
} else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_ref() {
|
||||
if let Some(ref p) = (*panic).0 {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#![cfg(feature = "macros")]
|
||||
|
||||
use mlua::{chunk, Lua, Result};
|
||||
|
||||
#[test]
|
||||
fn test_chunk_macro() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let name = "Rustacean";
|
||||
let table = vec![1];
|
||||
|
||||
let data = lua.create_table()?;
|
||||
data.raw_set("num", 1)?;
|
||||
|
||||
lua.globals().set("g", 123)?;
|
||||
|
||||
lua.load(chunk! {
|
||||
assert($name == "Rustacean")
|
||||
assert($table[1] == 1)
|
||||
assert($data.num == 1)
|
||||
assert(g == 123)
|
||||
s = 321
|
||||
})
|
||||
.exec()?;
|
||||
|
||||
assert_eq!(lua.globals().get::<_, i32>("s")?, 321);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -93,6 +93,49 @@ fn test_thread() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
#[test]
|
||||
fn test_thread_reset() -> Result<()> {
|
||||
use mlua::{AnyUserData, UserData};
|
||||
use std::sync::Arc;
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserData(Arc<()>);
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
let arc = Arc::new(());
|
||||
|
||||
let func: Function = lua.load(r#"function(ud) coroutine.yield(ud) end"#).eval()?;
|
||||
let thread = lua.create_thread(func.clone())?;
|
||||
|
||||
for _ in 0..2 {
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
let _ = thread.resume::<_, AnyUserData>(MyUserData(arc.clone()))?;
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert_eq!(Arc::strong_count(&arc), 2);
|
||||
thread.resume::<_, ()>(())?;
|
||||
assert_eq!(thread.status(), ThreadStatus::Unresumable);
|
||||
thread.reset(func.clone())?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&arc), 1);
|
||||
}
|
||||
|
||||
// Check for errors (Lua 5.4 only)
|
||||
#[cfg(feature = "lua54")]
|
||||
{
|
||||
let func: Function = lua.load(r#"function(ud) error("test error") end"#).eval()?;
|
||||
let thread = lua.create_thread(func.clone())?;
|
||||
let _ = thread.resume::<_, AnyUserData>(MyUserData(arc.clone()));
|
||||
assert_eq!(thread.status(), ThreadStatus::Error);
|
||||
assert_eq!(Arc::strong_count(&arc), 2);
|
||||
assert!(thread.reset(func.clone()).is_err());
|
||||
assert_eq!(thread.status(), ThreadStatus::Error);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coroutine_from_closure() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
Reference in New Issue
Block a user