Compare commits
28 commits
Author | SHA1 | Date | |
---|---|---|---|
delta | aede7c726c | ||
6807dfa22e | |||
f27c49f931 | |||
693a808b6e | |||
a6ca65aa74 | |||
a7278cab78 | |||
e1bbd00a33 | |||
e98998d6ac | |||
cca177df5b | |||
65396a910f | |||
de69d10d73 | |||
959e61b97c | |||
1040c0a347 | |||
9045419586 | |||
fcd162f3eb | |||
0354703dbf | |||
9831d0e397 | |||
7f14d93c2b | |||
29c6c9cb58 | |||
e01af22bac | |||
cfa959e599 | |||
e523fb2c86 | |||
81a9998559 | |||
050ac4f5ad | |||
967cbb53b0 | |||
44a8d86d8a | |||
33278d4a64 | |||
20a16839aa |
8
.github/workflows/coverage.yml
vendored
8
.github/workflows/coverage.yml
vendored
|
@ -10,14 +10,14 @@ jobs:
|
|||
options: --security-opt seccomp=unconfined
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Generate code coverage
|
||||
- name: Generate coverage report
|
||||
run: |
|
||||
cargo tarpaulin --verbose --features lua54,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
|
||||
- name: Upload report to codecov.io
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{secrets.CODECOV_TOKEN}}
|
||||
fail_ci_if_error: false
|
||||
|
|
277
.github/workflows/main.yml
vendored
277
.github/workflows/main.yml
vendored
|
@ -7,35 +7,34 @@ jobs:
|
|||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-latest, windows-latest]
|
||||
os: [ubuntu-22.04, macos-latest, windows-latest]
|
||||
rust: [stable]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --features "${{ matrix.lua }},vendored"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-20.04' && matrix.lua != 'lua54' }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
|
||||
cargo build --features "${{ matrix.lua }}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --features "${{ matrix.lua }},vendored"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-22.04' }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends liblua5.4-dev liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
|
||||
cargo build --features "${{ matrix.lua }}"
|
||||
|
||||
build_aarch64_cross_macos:
|
||||
name: Cross-compile to aarch64-apple-darwin
|
||||
|
@ -45,60 +44,57 @@ jobs:
|
|||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
target: aarch64-apple-darwin
|
||||
override: true
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
target: aarch64-apple-darwin
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
|
||||
build_aarch64_cross_ubuntu:
|
||||
name: Cross-compile to aarch64-unknown-linux-gnu
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
target: aarch64-unknown-linux-gnu
|
||||
override: true
|
||||
- name: Install ARM compiler toolchain
|
||||
run: |
|
||||
sudo apt-get update
|
||||
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 }},vendored,async,send,serialize,macros,parking_lot"
|
||||
shell: bash
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
target: aarch64-unknown-linux-gnu
|
||||
- name: Install ARM compiler toolchain
|
||||
run: |
|
||||
sudo apt-get update
|
||||
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 }},vendored,async,send,serialize,macros,parking_lot"
|
||||
shell: bash
|
||||
|
||||
build_armv7_cross_ubuntu:
|
||||
name: Cross-compile to armv7-unknown-linux-gnueabihf
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
target: armv7-unknown-linux-gnueabihf
|
||||
override: true
|
||||
- name: Install ARM compiler toolchain
|
||||
run: |
|
||||
sudo apt-get update
|
||||
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 }},vendored,async,send,serialize,macros,parking_lot"
|
||||
shell: bash
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
target: armv7-unknown-linux-gnueabihf
|
||||
- name: Install ARM compiler toolchain
|
||||
run: |
|
||||
sudo apt-get update
|
||||
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 }},vendored,async,send,serialize,macros,parking_lot"
|
||||
shell: bash
|
||||
|
||||
test:
|
||||
name: Test
|
||||
|
@ -106,35 +102,34 @@ jobs:
|
|||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-latest, windows-latest]
|
||||
os: [ubuntu-22.04, macos-latest, windows-latest]
|
||||
rust: [stable, nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau]
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --features "${{ matrix.lua }},vendored"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua54)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
|
||||
run: |
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" -- --ignored
|
||||
shell: bash
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --features "${{ matrix.lua }},vendored"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua54)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
|
||||
run: |
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" -- --ignored
|
||||
shell: bash
|
||||
|
||||
test_with_sanitizer:
|
||||
name: Test with address sanitizer
|
||||
|
@ -142,25 +137,24 @@ jobs:
|
|||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04]
|
||||
os: [ubuntu-22.04]
|
||||
rust: [nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} tests with address sanitizer
|
||||
run: |
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
shell: bash
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} tests with address sanitizer
|
||||
run: |
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
shell: bash
|
||||
|
||||
test_modules:
|
||||
name: Test modules
|
||||
|
@ -168,27 +162,26 @@ jobs:
|
|||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-latest]
|
||||
os: [ubuntu-22.04, macos-latest]
|
||||
rust: [stable]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} module tests
|
||||
run: |
|
||||
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
|
||||
(cd tests/module/loader && cargo test --release --features "${{ matrix.lua }},vendored")
|
||||
shell: bash
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} module tests
|
||||
run: |
|
||||
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
|
||||
(cd tests/module/loader && cargo test --release --features "${{ matrix.lua }},vendored")
|
||||
shell: bash
|
||||
|
||||
test_modules_windows:
|
||||
name: Test modules on Windows
|
||||
|
@ -201,41 +194,39 @@ jobs:
|
|||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: msys2/setup-msys2@v2
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust & Lua
|
||||
run: |
|
||||
pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-luajit mingw-w64-x86_64-pkg-config
|
||||
- name: Run ${{ matrix.lua }} module tests
|
||||
run: |
|
||||
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
|
||||
(cd tests/module/loader && cargo test --release --features "${{ matrix.lua }}")
|
||||
- uses: msys2/setup-msys2@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Rust & Lua
|
||||
run: |
|
||||
pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-luajit mingw-w64-x86_64-pkg-config
|
||||
- name: Run ${{ matrix.lua }} module tests
|
||||
run: |
|
||||
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
|
||||
(cd tests/module/loader && cargo test --release --features "${{ matrix.lua }}")
|
||||
|
||||
rustfmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
components: rustfmt
|
||||
override: true
|
||||
- run: cargo fmt -- --check
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
components: rustfmt
|
||||
- run: cargo fmt -- --check
|
||||
|
||||
clippy:
|
||||
name: Clippy check
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: nightly
|
||||
components: clippy
|
||||
override: true
|
||||
toolchain: nightly
|
||||
components: clippy
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -1,3 +1,20 @@
|
|||
## v0.8.6
|
||||
|
||||
- Fixed bug when recycled Registry slot can be set to Nil
|
||||
|
||||
## v0.8.5
|
||||
|
||||
- Fixed potential unsoundness when using `Layout::from_size_align_unchecked` and Rust 1.65+
|
||||
- Performance optimizations around string and table creation in standalone mode
|
||||
- Added fast track path to Table `get`/`set`/`len` methods without metatable
|
||||
- Added new methods `push`/`pop`/`raw_push`/`raw_pop` to Table
|
||||
- Fix getting caller information from `Lua::load`
|
||||
- Better checks and tests when trying to modify a Luau readonly table
|
||||
|
||||
## v0.8.4
|
||||
|
||||
- Minimal Luau updated to 0.548
|
||||
|
||||
## v0.8.3
|
||||
|
||||
- Close to-be-closed variables for Lua 5.4 when using call_async functions (#192)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "mlua"
|
||||
version = "0.8.3" # remember to update html_root_url and mlua_derive
|
||||
version = "0.8.6" # remember to update html_root_url and mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||
edition = "2021"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
|
@ -58,7 +58,7 @@ cc = { version = "1.0" }
|
|||
pkg-config = { version = "0.3.17" }
|
||||
lua-src = { version = ">= 544.0.0, < 550.0.0", optional = true }
|
||||
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
|
||||
luau0-src = { version = "0.3.6", optional = true }
|
||||
luau0-src = { version = "0.4.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rustyline = "10.0"
|
||||
|
|
|
@ -249,7 +249,7 @@ impl fmt::Display for Error {
|
|||
write!(fmt, "RegistryKey used from different Lua state")
|
||||
}
|
||||
Error::CallbackError { ref cause, ref traceback } => {
|
||||
writeln!(fmt, "callback error")?;
|
||||
writeln!(fmt, "{}", cause)?;
|
||||
// Trace errors down to the root
|
||||
let (mut cause, mut full_traceback) = (cause, None);
|
||||
while let Error::CallbackError { cause: ref cause2, traceback: ref traceback2 } = **cause {
|
||||
|
@ -269,7 +269,9 @@ impl fmt::Display for Error {
|
|||
} else {
|
||||
writeln!(fmt, "{}", traceback.trim_end())?;
|
||||
}
|
||||
write!(fmt, "caused by: {}", cause)
|
||||
|
||||
Ok(())
|
||||
// write!(fmt, "caused by: {}", cause)
|
||||
}
|
||||
Error::PreviouslyResumedPanic => {
|
||||
write!(fmt, "previously resumed panic returned again")
|
||||
|
|
|
@ -341,7 +341,7 @@ pub unsafe fn luaL_loadbufferx(
|
|||
fn free(p: *mut c_void);
|
||||
}
|
||||
|
||||
let chunk_is_text = (*data as u8) >= b'\n';
|
||||
let chunk_is_text = size == 0 || (*data as u8) >= b'\n';
|
||||
if !mode.is_null() {
|
||||
let modeb = CStr::from_ptr(mode).to_bytes();
|
||||
if !chunk_is_text && !modeb.contains(&b'b') {
|
||||
|
@ -423,7 +423,7 @@ pub unsafe fn luaL_traceback(
|
|||
level = numlevels - COMPAT53_LEVELS2; // and skip to last ones
|
||||
} else {
|
||||
lua_getinfo(L1, level, cstr!("sln"), &mut ar);
|
||||
lua_pushfstring(L, cstr!("\n\t%s:"), ar.short_src.as_ptr());
|
||||
lua_pushfstring(L, cstr!("\n\t%s:"), ar.short_src);
|
||||
if ar.currentline > 0 {
|
||||
lua_pushfstring(L, cstr!("%d:"), ar.currentline);
|
||||
}
|
||||
|
|
|
@ -257,6 +257,7 @@ extern "C" {
|
|||
extern "C" {
|
||||
pub fn lua_error(L: *mut lua_State) -> !;
|
||||
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_rawiter(L: *mut lua_State, idx: c_int, iter: c_int) -> c_int;
|
||||
pub fn lua_concat(L: *mut lua_State, n: c_int);
|
||||
// TODO: lua_encodepointer
|
||||
pub fn lua_clock() -> c_double;
|
||||
|
@ -267,6 +268,7 @@ extern "C" {
|
|||
dtor: Option<unsafe extern "C" fn(*mut lua_State, *mut c_void)>,
|
||||
);
|
||||
pub fn lua_clonefunction(L: *mut lua_State, idx: c_int);
|
||||
pub fn lua_cleartable(L: *mut lua_State, idx: c_int);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -460,13 +462,14 @@ pub struct lua_Debug {
|
|||
pub name: *const c_char,
|
||||
pub what: *const c_char,
|
||||
pub source: *const c_char,
|
||||
pub short_src: *const c_char,
|
||||
pub linedefined: c_int,
|
||||
pub currentline: c_int,
|
||||
pub nupvals: u8,
|
||||
pub nparams: u8,
|
||||
pub isvararg: c_char,
|
||||
pub short_src: [c_char; LUA_IDSIZE],
|
||||
pub userdata: *mut c_void,
|
||||
pub ssbuf: [c_char; LUA_IDSIZE],
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -79,9 +79,11 @@ pub(crate) fn keep_lua_symbols() {
|
|||
symbols.push(lua_tocfunction as _);
|
||||
symbols.push(luaL_loadstring as _);
|
||||
symbols.push(luaL_openlibs as _);
|
||||
if cfg!(any(feature = "lua54", feature = "lua53", feature = "lua52")) {
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
symbols.push(lua_getglobal as _);
|
||||
symbols.push(lua_setglobal as _);
|
||||
symbols.push(luaL_setfuncs as _);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -114,7 +114,7 @@ impl<'lua> Function<'lua> {
|
|||
R::from_lua_multi(results, lua)
|
||||
}
|
||||
|
||||
/// Returns a Feature that, when polled, calls `self`, passing `args` as function arguments,
|
||||
/// Returns a future that, when polled, calls `self`, passing `args` as function arguments,
|
||||
/// and drives the execution.
|
||||
///
|
||||
/// Internally it wraps the function to an [`AsyncThread`].
|
||||
|
@ -273,10 +273,13 @@ impl<'lua> Function<'lua> {
|
|||
name_what: None,
|
||||
what: ptr_to_cstr_bytes(ar.what).map(|s| s.to_vec()),
|
||||
source: ptr_to_cstr_bytes(ar.source).map(|s| s.to_vec()),
|
||||
short_src: ptr_to_cstr_bytes(&ar.short_src as *const _).map(|s| s.to_vec()),
|
||||
line_defined: ar.linedefined as i32,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
last_line_defined: ar.lastlinedefined as i32,
|
||||
short_src: ptr_to_cstr_bytes(ar.short_src.as_ptr()).map(|s| s.to_vec()),
|
||||
#[cfg(feature = "luau")]
|
||||
short_src: ptr_to_cstr_bytes(ar.short_src).map(|s| s.to_vec()),
|
||||
line_defined: ar.linedefined,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
last_line_defined: ar.lastlinedefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -312,8 +315,7 @@ impl<'lua> Function<'lua> {
|
|||
|
||||
lua.push_ref(&self.0);
|
||||
let data_ptr = &mut data as *mut Vec<u8> as *mut c_void;
|
||||
let strip = if strip { 1 } else { 0 };
|
||||
ffi::lua_dump(lua.state, writer, data_ptr, strip);
|
||||
ffi::lua_dump(lua.state, writer, data_ptr, strip as i32);
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
}
|
||||
|
||||
|
|
11
src/hook.rs
11
src/hook.rs
|
@ -102,10 +102,13 @@ impl<'lua> Debug<'lua> {
|
|||
|
||||
DebugSource {
|
||||
source: ptr_to_cstr_bytes((*self.ar.get()).source),
|
||||
short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src.as_ptr()),
|
||||
line_defined: (*self.ar.get()).linedefined as i32,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
last_line_defined: (*self.ar.get()).lastlinedefined as i32,
|
||||
short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src.as_ptr()),
|
||||
#[cfg(feature = "luau")]
|
||||
short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src),
|
||||
line_defined: (*self.ar.get()).linedefined,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
last_line_defined: (*self.ar.get()).lastlinedefined,
|
||||
what: ptr_to_cstr_bytes((*self.ar.get()).what),
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +128,7 @@ impl<'lua> Debug<'lua> {
|
|||
"lua_getinfo failed with `l`"
|
||||
);
|
||||
|
||||
(*self.ar.get()).currentline as i32
|
||||
(*self.ar.get()).currentline
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.8.3")]
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.8.6")]
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
|
|
137
src/lua.rs
137
src/lua.rs
|
@ -88,6 +88,8 @@ pub(crate) struct ExtraData {
|
|||
|
||||
registered_userdata: FxHashMap<TypeId, c_int>,
|
||||
registered_userdata_mt: FxHashMap<*const c_void, Option<TypeId>>,
|
||||
|
||||
// When Lua instance dropped, setting `None` would prevent collecting `RegistryKey`s
|
||||
registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
|
@ -402,6 +404,11 @@ impl Lua {
|
|||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
// Do not allocate more than isize::MAX
|
||||
if nsize > isize::MAX as usize {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
// Are we fit to the memory limits?
|
||||
let mut mem_diff = nsize as isize;
|
||||
if !ptr.is_null() {
|
||||
|
@ -411,12 +418,14 @@ impl Lua {
|
|||
if mem_info.memory_limit > 0 && new_used_memory > mem_info.memory_limit {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let new_layout = Layout::from_size_align_unchecked(nsize, ffi::SYS_MIN_ALIGN);
|
||||
mem_info.used_memory += mem_diff;
|
||||
|
||||
if ptr.is_null() {
|
||||
// Allocate new memory
|
||||
let new_layout = match Layout::from_size_align(nsize, ffi::SYS_MIN_ALIGN) {
|
||||
Ok(layout) => layout,
|
||||
Err(_) => return ptr::null_mut(),
|
||||
};
|
||||
let new_ptr = alloc::alloc(new_layout) as *mut c_void;
|
||||
if new_ptr.is_null() {
|
||||
alloc::handle_alloc_error(new_layout);
|
||||
|
@ -428,7 +437,7 @@ impl Lua {
|
|||
let old_layout = Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
|
||||
let new_ptr = alloc::realloc(ptr as *mut u8, old_layout, nsize) as *mut c_void;
|
||||
if new_ptr.is_null() {
|
||||
alloc::handle_alloc_error(new_layout);
|
||||
alloc::handle_alloc_error(old_layout);
|
||||
}
|
||||
new_ptr
|
||||
}
|
||||
|
@ -882,10 +891,8 @@ impl Lua {
|
|||
ffi::luaL_sandboxthread(state);
|
||||
} else {
|
||||
// Restore original `LUA_GLOBALSINDEX`
|
||||
self.ref_thread_exec(|ref_thread| {
|
||||
ffi::lua_xpush(ref_thread, state, ffi::LUA_GLOBALSINDEX);
|
||||
ffi::lua_replace(state, ffi::LUA_GLOBALSINDEX);
|
||||
});
|
||||
ffi::lua_xpush(self.ref_thread(), state, ffi::LUA_GLOBALSINDEX);
|
||||
ffi::lua_replace(state, ffi::LUA_GLOBALSINDEX);
|
||||
ffi::luaL_sandbox(state, 0);
|
||||
}
|
||||
})?;
|
||||
|
@ -1124,7 +1131,7 @@ impl Lua {
|
|||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||
pub fn warning<S: Into<Vec<u8>>>(&self, msg: S, tocont: bool) -> Result<()> {
|
||||
let msg = CString::new(msg).map_err(|err| Error::RuntimeError(err.to_string()))?;
|
||||
unsafe { ffi::lua_warning(self.state, msg.as_ptr(), if tocont { 1 } else { 0 }) };
|
||||
unsafe { ffi::lua_warning(self.state, msg.as_ptr(), tocont as c_int) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1366,9 +1373,8 @@ impl Lua {
|
|||
where
|
||||
S: AsChunk<'lua> + ?Sized,
|
||||
{
|
||||
let name = chunk
|
||||
.name()
|
||||
.unwrap_or_else(|| Location::caller().to_string());
|
||||
let caller = Location::caller();
|
||||
let name = chunk.name().unwrap_or_else(|| caller.to_string());
|
||||
|
||||
Chunk {
|
||||
lua: self,
|
||||
|
@ -1428,11 +1434,15 @@ impl Lua {
|
|||
S: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
unsafe {
|
||||
if self.unlikely_memory_error() {
|
||||
push_string(self.ref_thread(), s.as_ref(), false)?;
|
||||
return Ok(String(self.pop_ref_thread()));
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 3)?;
|
||||
|
||||
let protect = !self.unlikely_memory_error();
|
||||
push_string(self.state, s, protect)?;
|
||||
push_string(self.state, s.as_ref(), true)?;
|
||||
Ok(String(self.pop_ref()))
|
||||
}
|
||||
}
|
||||
|
@ -1448,11 +1458,15 @@ impl Lua {
|
|||
/// Lua may use these hints to preallocate memory for the new table.
|
||||
pub fn create_table_with_capacity(&self, narr: c_int, nrec: c_int) -> Result<Table> {
|
||||
unsafe {
|
||||
if self.unlikely_memory_error() {
|
||||
push_table(self.ref_thread(), narr, nrec, false)?;
|
||||
return Ok(Table(self.pop_ref_thread()));
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 3)?;
|
||||
|
||||
let protect = !self.unlikely_memory_error();
|
||||
push_table(self.state, narr, nrec, protect)?;
|
||||
push_table(self.state, narr, nrec, true)?;
|
||||
Ok(Table(self.pop_ref()))
|
||||
}
|
||||
}
|
||||
|
@ -2016,7 +2030,7 @@ impl Lua {
|
|||
check_stack(self.state, 3)?;
|
||||
|
||||
let protect = !self.unlikely_memory_error();
|
||||
push_string(self.state, name, protect)?;
|
||||
push_string(self.state, name.as_ref(), protect)?;
|
||||
ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX);
|
||||
|
||||
self.pop_value()
|
||||
|
@ -2048,6 +2062,12 @@ impl Lua {
|
|||
/// [`RegistryKey`]: crate::RegistryKey
|
||||
pub fn create_registry_value<'lua, T: ToLua<'lua>>(&'lua self, t: T) -> Result<RegistryKey> {
|
||||
let t = t.to_lua(self)?;
|
||||
if t == Value::Nil {
|
||||
// Special case to skip calling `luaL_ref` and use `LUA_REFNIL` instead
|
||||
let unref_list = unsafe { (*self.extra.get()).registry_unref_list.clone() };
|
||||
return Ok(RegistryKey::new(ffi::LUA_REFNIL, unref_list));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 4)?;
|
||||
|
@ -2055,27 +2075,20 @@ impl Lua {
|
|||
let unref_list = (*self.extra.get()).registry_unref_list.clone();
|
||||
self.push_value(t)?;
|
||||
|
||||
// Try to reuse previously allocated RegistryKey
|
||||
// Try to reuse previously allocated slot
|
||||
let unref_list2 = unref_list.clone();
|
||||
let mut unref_list2 = mlua_expect!(unref_list2.lock(), "unref list poisoned");
|
||||
if let Some(registry_id) = unref_list2.as_mut().and_then(|x| x.pop()) {
|
||||
// It must be safe to replace the value without triggering memory error
|
||||
ffi::lua_rawseti(self.state, ffi::LUA_REGISTRYINDEX, registry_id as Integer);
|
||||
return Ok(RegistryKey {
|
||||
registry_id,
|
||||
unref_list,
|
||||
});
|
||||
return Ok(RegistryKey::new(registry_id, unref_list));
|
||||
}
|
||||
|
||||
// Allocate a new RegistryKey
|
||||
let registry_id = protect_lua!(self.state, 1, 0, |state| {
|
||||
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
|
||||
})?;
|
||||
|
||||
Ok(RegistryKey {
|
||||
registry_id,
|
||||
unref_list,
|
||||
})
|
||||
Ok(RegistryKey::new(registry_id, unref_list))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2090,16 +2103,16 @@ impl Lua {
|
|||
return Err(Error::MismatchedRegistryKey);
|
||||
}
|
||||
|
||||
let value = unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 1)?;
|
||||
let value = match key.is_nil() {
|
||||
true => Value::Nil,
|
||||
false => unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 1)?;
|
||||
|
||||
ffi::lua_rawgeti(
|
||||
self.state,
|
||||
ffi::LUA_REGISTRYINDEX,
|
||||
key.registry_id as Integer,
|
||||
);
|
||||
self.pop_value()
|
||||
let id = key.registry_id as Integer;
|
||||
ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, id);
|
||||
self.pop_value()
|
||||
},
|
||||
};
|
||||
T::from_lua(value, self)
|
||||
}
|
||||
|
@ -2138,20 +2151,31 @@ impl Lua {
|
|||
}
|
||||
|
||||
let t = t.to_lua(self)?;
|
||||
if t == Value::Nil && key.is_nil() {
|
||||
// Nothing to replace
|
||||
return Ok(());
|
||||
} else if t != Value::Nil && key.registry_id == ffi::LUA_REFNIL {
|
||||
// We cannot update `LUA_REFNIL` slot
|
||||
let err = "cannot replace nil value with non-nil".to_string();
|
||||
return Err(Error::RuntimeError(err));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 2)?;
|
||||
|
||||
self.push_value(t)?;
|
||||
let id = key.registry_id as Integer;
|
||||
if t == Value::Nil {
|
||||
self.push_value(Value::Integer(id))?;
|
||||
key.set_nil(true);
|
||||
} else {
|
||||
self.push_value(t)?;
|
||||
key.set_nil(false);
|
||||
}
|
||||
// It must be safe to replace the value without triggering memory error
|
||||
ffi::lua_rawseti(
|
||||
self.state,
|
||||
ffi::LUA_REGISTRYINDEX,
|
||||
key.registry_id as Integer,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
ffi::lua_rawseti(self.state, ffi::LUA_REGISTRYINDEX, id);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns true if the given `RegistryKey` was created by a `Lua` which shares the underlying
|
||||
|
@ -2263,7 +2287,7 @@ impl Lua {
|
|||
}
|
||||
|
||||
Value::Boolean(b) => {
|
||||
ffi::lua_pushboolean(self.state, if b { 1 } else { 0 });
|
||||
ffi::lua_pushboolean(self.state, b as c_int);
|
||||
}
|
||||
|
||||
Value::LightUserData(ud) => {
|
||||
|
@ -2439,6 +2463,13 @@ impl Lua {
|
|||
LuaRef { lua: self, index }
|
||||
}
|
||||
|
||||
// Same as `pop_ref` but assumes the value is already on the reference thread
|
||||
pub(crate) unsafe fn pop_ref_thread(&self) -> LuaRef {
|
||||
let extra = &mut *self.extra.get();
|
||||
let index = ref_stack_pop(extra);
|
||||
LuaRef { lua: self, index }
|
||||
}
|
||||
|
||||
pub(crate) fn clone_ref<'lua>(&'lua self, lref: &LuaRef<'lua>) -> LuaRef<'lua> {
|
||||
unsafe {
|
||||
let extra = &mut *self.extra.get();
|
||||
|
@ -2457,16 +2488,6 @@ impl Lua {
|
|||
}
|
||||
}
|
||||
|
||||
/// Executes the function provided on the ref thread
|
||||
#[inline]
|
||||
pub(crate) unsafe fn ref_thread_exec<F, R>(&self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(*mut ffi::lua_State) -> R,
|
||||
{
|
||||
let ref_thread = (*self.extra.get()).ref_thread;
|
||||
f(ref_thread)
|
||||
}
|
||||
|
||||
unsafe fn push_userdata_metatable<T: 'static + UserData>(&self) -> Result<()> {
|
||||
let extra = &mut *self.extra.get();
|
||||
|
||||
|
@ -2591,6 +2612,7 @@ impl Lua {
|
|||
pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef) -> Result<Option<TypeId>> {
|
||||
self.push_ref(lref);
|
||||
if ffi::lua_getmetatable(self.state, -1) == 0 {
|
||||
ffi::lua_pop(self.state, 1);
|
||||
return Err(Error::UserDataTypeMismatch);
|
||||
}
|
||||
let mt_ptr = ffi::lua_topointer(self.state, -1);
|
||||
|
@ -2968,6 +2990,13 @@ impl Lua {
|
|||
}
|
||||
}
|
||||
|
||||
impl LuaInner {
|
||||
#[inline(always)]
|
||||
pub(crate) fn ref_thread(&self) -> *mut ffi::lua_State {
|
||||
unsafe { (*self.extra.get()).ref_thread }
|
||||
}
|
||||
}
|
||||
|
||||
struct StateGuard<'a>(&'a mut LuaInner, *mut ffi::lua_State);
|
||||
|
||||
impl<'a> StateGuard<'a> {
|
||||
|
|
|
@ -71,7 +71,7 @@ impl Options {
|
|||
///
|
||||
/// [`deny_recursive_tables`]: #structfield.deny_recursive_tables
|
||||
#[must_use]
|
||||
pub fn deny_recursive_tables(mut self, enabled: bool) -> Self {
|
||||
pub const fn deny_recursive_tables(mut self, enabled: bool) -> Self {
|
||||
self.deny_recursive_tables = enabled;
|
||||
self
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::ffi;
|
|||
use crate::lua::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::types::LightUserData;
|
||||
use crate::util::{assert_stack, check_stack, StackGuard};
|
||||
use crate::util::check_stack;
|
||||
use crate::value::Value;
|
||||
|
||||
/// Trait for serializing/deserializing Lua values using Serde.
|
||||
|
@ -205,12 +205,8 @@ impl<'lua> LuaSerdeExt<'lua> for Lua {
|
|||
|
||||
fn array_metatable(&'lua self) -> Table<'lua> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
assert_stack(self.state, 1);
|
||||
|
||||
push_array_metatable(self.state);
|
||||
|
||||
Table(self.pop_ref())
|
||||
push_array_metatable(self.ref_thread());
|
||||
Table(self.pop_ref_thread())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ use {
|
|||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::types::LuaRef;
|
||||
use crate::util::{assert_stack, StackGuard};
|
||||
|
||||
/// Handle to an internal Lua string.
|
||||
///
|
||||
|
@ -40,6 +39,7 @@ impl<'lua> String<'lua> {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_str(&self) -> Result<&str> {
|
||||
str::from_utf8(self.as_bytes()).map_err(|e| Error::FromLuaConversionError {
|
||||
from: "string",
|
||||
|
@ -66,6 +66,7 @@ impl<'lua> String<'lua> {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_string_lossy(&self) -> Cow<'_, str> {
|
||||
StdString::from_utf8_lossy(self.as_bytes())
|
||||
}
|
||||
|
@ -87,6 +88,7 @@ impl<'lua> String<'lua> {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
let nulled = self.as_bytes_with_nul();
|
||||
&nulled[..nulled.len() - 1]
|
||||
|
@ -94,21 +96,17 @@ impl<'lua> String<'lua> {
|
|||
|
||||
/// Get the bytes that make up this string, including the trailing nul byte.
|
||||
pub fn as_bytes_with_nul(&self) -> &[u8] {
|
||||
let lua = self.0.lua;
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
mlua_debug_assert!(
|
||||
ffi::lua_type(lua.state, -1) == ffi::LUA_TSTRING,
|
||||
ffi::lua_type(ref_thread, self.0.index) == ffi::LUA_TSTRING,
|
||||
"string ref is not string type"
|
||||
);
|
||||
|
||||
let mut size = 0;
|
||||
// This will not trigger a 'm' error, because the reference is guaranteed to be of
|
||||
// string type
|
||||
let data = ffi::lua_tolstring(lua.state, -1, &mut size);
|
||||
let data = ffi::lua_tolstring(ref_thread, self.0.index, &mut size);
|
||||
|
||||
slice::from_raw_parts(data as *const u8, size + 1)
|
||||
}
|
||||
|
@ -121,8 +119,8 @@ impl<'lua> String<'lua> {
|
|||
/// Typically this function is used only for hashing and debug information.
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
let lua = self.0.lua;
|
||||
unsafe { lua.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, self.0.index)) }
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe { ffi::lua_topointer(ref_thread, self.0.index) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
177
src/table.rs
177
src/table.rs
|
@ -58,6 +58,11 @@ impl<'lua> Table<'lua> {
|
|||
///
|
||||
/// [`raw_set`]: #method.raw_set
|
||||
pub fn set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> {
|
||||
// Fast track
|
||||
if !self.has_metatable() {
|
||||
return self.raw_set(key, value);
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
let value = value.to_lua(lua)?;
|
||||
|
@ -98,6 +103,11 @@ impl<'lua> Table<'lua> {
|
|||
///
|
||||
/// [`raw_get`]: #method.raw_get
|
||||
pub fn get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||
// Fast track
|
||||
if !self.has_metatable() {
|
||||
return self.raw_get(key);
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
|
||||
|
@ -116,18 +126,54 @@ impl<'lua> Table<'lua> {
|
|||
|
||||
/// Checks whether the table contains a non-nil value for `key`.
|
||||
pub fn contains_key<K: ToLua<'lua>>(&self, key: K) -> Result<bool> {
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
Ok(self.get::<_, Value>(key)? != Value::Nil)
|
||||
}
|
||||
|
||||
/// Appends a value to the back of the table.
|
||||
pub fn push<V: ToLua<'lua>>(&self, value: V) -> Result<()> {
|
||||
// Fast track
|
||||
if !self.has_metatable() {
|
||||
return self.raw_push(value);
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let value = value.to_lua(lua)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
protect_lua!(lua.state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
|
||||
Ok(ffi::lua_isnil(lua.state, -1) == 0)
|
||||
lua.push_value(value)?;
|
||||
protect_lua!(lua.state, 2, 0, fn(state) {
|
||||
let len = ffi::luaL_len(state, -2) as Integer;
|
||||
ffi::lua_seti(state, -2, len + 1);
|
||||
})?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes the last element from the table and returns it.
|
||||
pub fn pop<V: FromLua<'lua>>(&self) -> Result<V> {
|
||||
// Fast track
|
||||
if !self.has_metatable() {
|
||||
return self.raw_pop();
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let value = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
protect_lua!(lua.state, 1, 1, fn(state) {
|
||||
let len = ffi::luaL_len(state, -1) as Integer;
|
||||
ffi::lua_geti(state, -1, len);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_seti(state, -3, len);
|
||||
})?;
|
||||
lua.pop_value()
|
||||
};
|
||||
V::from_lua(value, lua)
|
||||
}
|
||||
|
||||
/// Compares two tables for equality.
|
||||
|
@ -188,6 +234,9 @@ impl<'lua> Table<'lua> {
|
|||
|
||||
/// Sets a key-value pair without invoking metamethods.
|
||||
pub fn raw_set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
let value = value.to_lua(lua)?;
|
||||
|
@ -199,6 +248,7 @@ impl<'lua> Table<'lua> {
|
|||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
lua.push_value(value)?;
|
||||
|
||||
if lua.unlikely_memory_error() {
|
||||
ffi::lua_rawset(lua.state, -3);
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
|
@ -254,6 +304,56 @@ impl<'lua> Table<'lua> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Appends a value to the back of the table without invoking metamethods.
|
||||
pub fn raw_push<V: ToLua<'lua>>(&self, value: V) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua;
|
||||
let value = value.to_lua(lua)?;
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(value)?;
|
||||
|
||||
unsafe fn callback(state: *mut ffi::lua_State) {
|
||||
let len = ffi::lua_rawlen(state, -2) as Integer;
|
||||
ffi::lua_rawseti(state, -2, len + 1);
|
||||
}
|
||||
|
||||
if lua.unlikely_memory_error() {
|
||||
callback(lua.state);
|
||||
} else {
|
||||
protect_lua!(lua.state, 2, 0, fn(state) callback(state))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes the last element from the table and returns it, without invoking metamethods.
|
||||
pub fn raw_pop<V: FromLua<'lua>>(&self) -> Result<V> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua;
|
||||
let value = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let len = ffi::lua_rawlen(lua.state, -1) as Integer;
|
||||
ffi::lua_rawgeti(lua.state, -1, len);
|
||||
// Set slot to nil (it must be safe to do)
|
||||
ffi::lua_pushnil(lua.state);
|
||||
ffi::lua_rawseti(lua.state, -3, len);
|
||||
lua.pop_value()
|
||||
};
|
||||
V::from_lua(value, lua)
|
||||
}
|
||||
|
||||
/// Removes a key from the table.
|
||||
///
|
||||
/// If `key` is an integer, mlua shifts down the elements from `table[key+1]`,
|
||||
|
@ -295,6 +395,11 @@ impl<'lua> Table<'lua> {
|
|||
///
|
||||
/// [`raw_len`]: #method.raw_len
|
||||
pub fn len(&self) -> Result<Integer> {
|
||||
// Fast track
|
||||
if !self.has_metatable() {
|
||||
return Ok(self.raw_len());
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
|
@ -307,14 +412,8 @@ impl<'lua> Table<'lua> {
|
|||
|
||||
/// Returns the result of the Lua `#` operator, without invoking the `__len` metamethod.
|
||||
pub fn raw_len(&self) -> Integer {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
ffi::lua_rawlen(lua.state, -1) as Integer
|
||||
}
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe { ffi::lua_rawlen(ref_thread, self.0.index) as Integer }
|
||||
}
|
||||
|
||||
/// Returns a reference to the metatable of this table, or `None` if no metatable is set.
|
||||
|
@ -340,6 +439,12 @@ impl<'lua> Table<'lua> {
|
|||
/// If `metatable` is `None`, the metatable is removed (if no metatable is set, this does
|
||||
/// nothing).
|
||||
pub fn set_metatable(&self, metatable: Option<Table<'lua>>) {
|
||||
// Workaround to throw readonly error without returning Result
|
||||
#[cfg(feature = "luau")]
|
||||
if self.is_readonly() {
|
||||
panic!("attempt to modify a readonly table");
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
|
@ -355,21 +460,33 @@ impl<'lua> Table<'lua> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns true if the table has metatable attached.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn has_metatable(&self) -> bool {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe {
|
||||
if ffi::lua_getmetatable(ref_thread, self.0.index) != 0 {
|
||||
ffi::lua_pop(ref_thread, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Sets `readonly` attribute on the table.
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn set_readonly(&self, enabled: bool) {
|
||||
let lua = self.0.lua;
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe {
|
||||
lua.ref_thread_exec(|refthr| {
|
||||
ffi::lua_setreadonly(refthr, self.0.index, enabled as _);
|
||||
if !enabled {
|
||||
// Reset "safeenv" flag
|
||||
ffi::lua_setsafeenv(refthr, self.0.index, 0);
|
||||
}
|
||||
});
|
||||
ffi::lua_setreadonly(ref_thread, self.0.index, enabled as _);
|
||||
if !enabled {
|
||||
// Reset "safeenv" flag
|
||||
ffi::lua_setsafeenv(ref_thread, self.0.index, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -379,8 +496,8 @@ impl<'lua> Table<'lua> {
|
|||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn is_readonly(&self) -> bool {
|
||||
let lua = self.0.lua;
|
||||
unsafe { lua.ref_thread_exec(|refthr| ffi::lua_getreadonly(refthr, self.0.index) != 0) }
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe { ffi::lua_getreadonly(ref_thread, self.0.index) != 0 }
|
||||
}
|
||||
|
||||
/// Converts the table to a generic C pointer.
|
||||
|
@ -391,8 +508,8 @@ impl<'lua> Table<'lua> {
|
|||
/// Typically this function is used only for hashing and debug information.
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
let lua = self.0.lua;
|
||||
unsafe { lua.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, self.0.index)) }
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe { ffi::lua_topointer(ref_thread, self.0.index) }
|
||||
}
|
||||
|
||||
/// Consume this table and return an iterator over the pairs of the table.
|
||||
|
@ -532,6 +649,16 @@ impl<'lua> Table<'lua> {
|
|||
ffi::lua_rawequal(lua.state, -1, -2) != 0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline(always)]
|
||||
pub(crate) fn check_readonly_write(&self) -> Result<()> {
|
||||
if self.is_readonly() {
|
||||
let err = "attempt to modify a readonly table".to_string();
|
||||
return Err(Error::RuntimeError(err));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Table<'lua> {
|
||||
|
|
|
@ -118,8 +118,7 @@ impl<'lua> Thread<'lua> {
|
|||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, cmp::max(nargs + 1, 3))?;
|
||||
|
||||
let thread_state =
|
||||
lua.ref_thread_exec(|ref_thread| ffi::lua_tothread(ref_thread, self.0.index));
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
|
||||
let status = ffi::lua_status(thread_state);
|
||||
if status != ffi::LUA_YIELD && ffi::lua_gettop(thread_state) == 0 {
|
||||
|
@ -160,8 +159,7 @@ impl<'lua> Thread<'lua> {
|
|||
pub fn status(&self) -> ThreadStatus {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let thread_state =
|
||||
lua.ref_thread_exec(|ref_thread| ffi::lua_tothread(ref_thread, self.0.index));
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
|
||||
let status = ffi::lua_status(thread_state);
|
||||
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
|
||||
|
@ -326,7 +324,7 @@ impl<'lua> Thread<'lua> {
|
|||
pub fn sandbox(&self) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let thread = lua.ref_thread_exec(|t| ffi::lua_tothread(t, self.0.index));
|
||||
let thread = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
check_stack(thread, 1)?;
|
||||
check_stack(lua.state, 3)?;
|
||||
// Inherit `LUA_GLOBALSINDEX` from the caller
|
||||
|
@ -366,8 +364,7 @@ impl<'lua, R> Drop for AsyncThread<'lua, R> {
|
|||
if !lua.recycle_thread(&mut self.thread) {
|
||||
#[cfg(feature = "lua54")]
|
||||
if self.thread.status() == ThreadStatus::Error {
|
||||
let thread_state =
|
||||
lua.ref_thread_exec(|t| ffi::lua_tothread(t, self.thread.0.index));
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.thread.0.index);
|
||||
ffi::lua_resetthread(thread_state);
|
||||
}
|
||||
}
|
||||
|
|
37
src/types.rs
37
src/types.rs
|
@ -1,6 +1,7 @@
|
|||
use std::cell::UnsafeCell;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
|
@ -104,6 +105,7 @@ pub(crate) struct DestructedUserdata;
|
|||
/// [`AnyUserData::get_user_value`]: crate::AnyUserData::get_user_value
|
||||
pub struct RegistryKey {
|
||||
pub(crate) registry_id: c_int,
|
||||
pub(crate) is_nil: AtomicBool,
|
||||
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
}
|
||||
|
||||
|
@ -129,15 +131,27 @@ impl Eq for RegistryKey {}
|
|||
|
||||
impl Drop for RegistryKey {
|
||||
fn drop(&mut self) {
|
||||
let mut unref_list = mlua_expect!(self.unref_list.lock(), "unref list poisoned");
|
||||
if let Some(list) = unref_list.as_mut() {
|
||||
list.push(self.registry_id);
|
||||
// We don't need to collect nil slot
|
||||
if self.registry_id > ffi::LUA_REFNIL {
|
||||
let mut unref_list = mlua_expect!(self.unref_list.lock(), "unref list poisoned");
|
||||
if let Some(list) = unref_list.as_mut() {
|
||||
list.push(self.registry_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RegistryKey {
|
||||
// Destroys the RegistryKey without adding to the drop list
|
||||
// Creates a new instance of `RegistryKey`
|
||||
pub(crate) const fn new(id: c_int, unref_list: Arc<Mutex<Option<Vec<c_int>>>>) -> Self {
|
||||
RegistryKey {
|
||||
registry_id: id,
|
||||
is_nil: AtomicBool::new(id == ffi::LUA_REFNIL),
|
||||
unref_list,
|
||||
}
|
||||
}
|
||||
|
||||
// Destroys the `RegistryKey` without adding to the unref list
|
||||
pub(crate) fn take(self) -> c_int {
|
||||
let registry_id = self.registry_id;
|
||||
unsafe {
|
||||
|
@ -146,6 +160,21 @@ impl RegistryKey {
|
|||
}
|
||||
registry_id
|
||||
}
|
||||
|
||||
// Returns true if this `RegistryKey` holds a nil value
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_nil(&self) -> bool {
|
||||
self.is_nil.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
// Marks value of this `RegistryKey` as `Nil`
|
||||
#[inline(always)]
|
||||
pub(crate) fn set_nil(&self, enabled: bool) {
|
||||
// We cannot replace previous value with nil in as this will break
|
||||
// Lua mechanism to find free keys.
|
||||
// Instead, we set a special flag to mark value as nil.
|
||||
self.is_nil.store(enabled, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LuaRef<'lua> {
|
||||
|
|
19
src/util.rs
19
src/util.rs
|
@ -242,14 +242,9 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
|||
}
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces, does not call checkstack.
|
||||
#[inline]
|
||||
pub unsafe fn push_string<S: AsRef<[u8]> + ?Sized>(
|
||||
state: *mut ffi::lua_State,
|
||||
s: &S,
|
||||
protect: bool,
|
||||
) -> Result<()> {
|
||||
let s = s.as_ref();
|
||||
// Uses 3 (or 1 if unprotected) stack spaces, does not call checkstack.
|
||||
#[inline(always)]
|
||||
pub unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -> Result<()> {
|
||||
if protect {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
|
||||
|
@ -313,7 +308,7 @@ pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool)
|
|||
ptr::drop_in_place(ud as *mut T);
|
||||
}
|
||||
|
||||
let size = mem::size_of::<T>() + 1;
|
||||
let size = mem::size_of::<T>();
|
||||
let ud = if protect {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdatadtor(state, size, destructor::<T>) as *mut T
|
||||
|
@ -548,7 +543,7 @@ pub unsafe fn init_userdata_metatable<T>(
|
|||
// Push `__index` generator function
|
||||
init_userdata_metatable_index(state)?;
|
||||
|
||||
push_string(state, "__index", true)?;
|
||||
push_string(state, b"__index", true)?;
|
||||
let index_type = ffi::lua_rawget(state, -3);
|
||||
match index_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
|
@ -573,7 +568,7 @@ pub unsafe fn init_userdata_metatable<T>(
|
|||
// Push `__newindex` generator function
|
||||
init_userdata_metatable_newindex(state)?;
|
||||
|
||||
push_string(state, "__newindex", true)?;
|
||||
push_string(state, b"__newindex", true)?;
|
||||
let newindex_type = ffi::lua_rawget(state, -3);
|
||||
match newindex_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
|
@ -893,7 +888,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
|||
}
|
||||
}?;
|
||||
|
||||
push_string(state, &*err_buf, true)?;
|
||||
push_string(state, (*err_buf).as_bytes(), true)?;
|
||||
(*err_buf).clear();
|
||||
|
||||
Ok(1)
|
||||
|
|
10
src/value.rs
10
src/value.rs
|
@ -111,11 +111,11 @@ impl<'lua> Value<'lua> {
|
|||
Value::LightUserData(ud) => ud.0,
|
||||
Value::Table(t) => t.to_pointer(),
|
||||
Value::String(s) => s.to_pointer(),
|
||||
Value::Function(Function(v))
|
||||
| Value::Thread(Thread(v))
|
||||
| Value::UserData(AnyUserData(v)) => v
|
||||
.lua
|
||||
.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, v.index)),
|
||||
Value::Function(Function(r))
|
||||
| Value::Thread(Thread(r))
|
||||
| Value::UserData(AnyUserData(r)) => {
|
||||
ffi::lua_topointer(r.lua.ref_thread(), r.index)
|
||||
}
|
||||
_ => ptr::null(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use mlua::{Lua, UserData, UserDataMethods};
|
||||
use mlua::{UserData, UserDataMethods};
|
||||
|
||||
fn main() {
|
||||
let ref lua = Lua::new();
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MyUserData<'a>(&'a i64);
|
||||
|
||||
|
|
|
@ -1,41 +1,14 @@
|
|||
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
|
||||
--> tests/compile/async_nonstatic_userdata.rs:11:72
|
||||
|
|
||||
11 | methods.add_async_method("print", |_, data, ()| async move {
|
||||
| ________________________________________________________________________^
|
||||
12 | | println!("{}", data.0);
|
||||
13 | | Ok(())
|
||||
14 | | });
|
||||
| |_____________^
|
||||
|
|
||||
note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
|
||||
--> tests/compile/async_nonstatic_userdata.rs:9:10
|
||||
|
|
||||
9 | impl<'a> UserData for MyUserData<'a> {
|
||||
| ^^
|
||||
note: ...so that the types are compatible
|
||||
--> tests/compile/async_nonstatic_userdata.rs:11:72
|
||||
|
|
||||
11 | methods.add_async_method("print", |_, data, ()| async move {
|
||||
| ________________________________________________________________________^
|
||||
12 | | println!("{}", data.0);
|
||||
13 | | Ok(())
|
||||
14 | | });
|
||||
| |_____________^
|
||||
= note: expected `(MyUserData<'_>,)`
|
||||
found `(MyUserData<'a>,)`
|
||||
note: but, the lifetime must be valid for the lifetime `'lua` as defined here...
|
||||
--> tests/compile/async_nonstatic_userdata.rs:10:24
|
||||
|
|
||||
10 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
| ^^^^
|
||||
note: ...so that the type `impl Future<Output = [async output]>` will meet its required lifetime bounds...
|
||||
--> tests/compile/async_nonstatic_userdata.rs:11:21
|
||||
|
|
||||
11 | methods.add_async_method("print", |_, data, ()| async move {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
note: ...that is required by this bound
|
||||
--> src/userdata.rs
|
||||
|
|
||||
| MR: 'lua + Future<Output = Result<R>>;
|
||||
| ^^^^
|
||||
error: lifetime may not live long enough
|
||||
--> tests/compile/async_nonstatic_userdata.rs:9:13
|
||||
|
|
||||
7 | impl<'a> UserData for MyUserData<'a> {
|
||||
| -- lifetime `'a` defined here
|
||||
8 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
| ---- lifetime `'lua` defined here
|
||||
9 | / methods.add_async_method("print", |_, data, ()| async move {
|
||||
10 | | println!("{}", data.0);
|
||||
11 | | Ok(())
|
||||
12 | | });
|
||||
| |______________^ argument requires that `'a` must outlive `'lua`
|
||||
|
|
||||
= help: consider adding the following bound: `'a: 'lua`
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
error[E0373]: closure may outlive the current function, but it borrows `test`, which is owned by the current function
|
||||
error[E0373]: closure may outlive the current function, but it borrows `test.0`, which is owned by the current function
|
||||
--> tests/compile/function_borrow.rs:9:33
|
||||
|
|
||||
9 | let _ = lua.create_function(|_, ()| -> Result<i32> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `test`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `test.0`
|
||||
10 | Ok(test.0)
|
||||
| ------ `test` is borrowed here
|
||||
| ------ `test.0` is borrowed here
|
||||
|
|
||||
note: function requires argument type to outlive `'static`
|
||||
--> tests/compile/function_borrow.rs:9:13
|
||||
|
@ -14,7 +14,7 @@ note: function requires argument type to outlive `'static`
|
|||
10 | | Ok(test.0)
|
||||
11 | | });
|
||||
| |______^
|
||||
help: to force the closure to take ownership of `test` (and any other referenced variables), use the `move` keyword
|
||||
help: to force the closure to take ownership of `test.0` (and any other referenced variables), use the `move` keyword
|
||||
|
|
||||
9 | let _ = lua.create_function(move |_, ()| -> Result<i32> {
|
||||
| ++++
|
||||
|
|
|
@ -10,7 +10,11 @@ error[E0277]: the type `UnsafeCell<mlua::lua::LuaInner>` may contain interior mu
|
|||
= note: required because it appears within the type `Arc<UnsafeCell<mlua::lua::LuaInner>>`
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:48]`
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: required by a bound in `catch_unwind`
|
||||
--> $RUST/std/src/panic.rs
|
||||
|
|
||||
|
|
|
@ -10,7 +10,14 @@ error[E0277]: `Rc<Cell<i32>>` cannot be sent between threads safely
|
|||
| |_____- within this `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
|
||||
|
|
||||
= help: within `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/non_send.rs:11:25
|
||||
|
|
||||
11 | lua.create_function(move |_, ()| {
|
||||
| _________________________^
|
||||
12 | | Ok(data.get())
|
||||
13 | | })?
|
||||
| |_____^
|
||||
= note: required because of the requirements on the impl of `mlua::types::MaybeSend` for `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
|
||||
note: required by a bound in `Lua::create_function`
|
||||
--> src/lua.rs
|
||||
|
|
|
@ -12,7 +12,11 @@ error[E0277]: the type `UnsafeCell<mlua::lua::LuaInner>` may contain interior mu
|
|||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
|
||||
= note: required because it appears within the type `LuaTable<'_>`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:54]`
|
||||
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 `catch_unwind`
|
||||
--> $RUST/std/src/panic.rs
|
||||
|
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
error[E0373]: closure may outlive the current function, but it borrows `test`, which is owned by the current function
|
||||
error[E0373]: closure may outlive the current function, but it borrows `test.field`, which is owned by the current function
|
||||
--> tests/compile/scope_invariance.rs:14:38
|
||||
|
|
||||
9 | lua.scope(|scope| {
|
||||
| ----- has type `&mlua::Scope<'_, '1>`
|
||||
...
|
||||
14 | .create_function_mut(|_, ()| {
|
||||
| ^^^^^^^ may outlive borrowed value `test`
|
||||
| ^^^^^^^ may outlive borrowed value `test.field`
|
||||
15 | test.field = 42;
|
||||
| ---------- `test` is borrowed here
|
||||
| ---------- `test.field` is borrowed here
|
||||
|
|
||||
note: function requires argument type to outlive `'1`
|
||||
--> tests/compile/scope_invariance.rs:13:13
|
||||
|
@ -19,7 +19,7 @@ note: function requires argument type to outlive `'1`
|
|||
17 | | Ok(())
|
||||
18 | | })?
|
||||
| |__________________^
|
||||
help: to force the closure to take ownership of `test` (and any other referenced variables), use the `move` keyword
|
||||
help: to force the closure to take ownership of `test.field` (and any other referenced variables), use the `move` keyword
|
||||
|
|
||||
14 | .create_function_mut(move |_, ()| {
|
||||
| ++++
|
||||
|
|
|
@ -1,33 +1,31 @@
|
|||
error[E0597]: `lua` does not live long enough
|
||||
--> tests/compile/static_callback_args.rs:12:5
|
||||
|
|
||||
12 | / lua.create_function(|_, table: Table| {
|
||||
13 | | BAD_TIME.with(|bt| {
|
||||
14 | | *bt.borrow_mut() = Some(table);
|
||||
15 | | });
|
||||
16 | | Ok(())
|
||||
17 | | })?
|
||||
| | ^
|
||||
| | |
|
||||
| |______borrowed value does not live long enough
|
||||
| argument requires that `lua` is borrowed for `'static`
|
||||
12 | / lua.create_function(|_, table: Table| {
|
||||
13 | | BAD_TIME.with(|bt| {
|
||||
| |_________-
|
||||
14 | || *bt.borrow_mut() = Some(table);
|
||||
15 | || });
|
||||
| ||__________- argument requires that `lua` is borrowed for `'static`
|
||||
16 | | Ok(())
|
||||
17 | | })?
|
||||
| |______^ borrowed value does not live long enough
|
||||
...
|
||||
32 | }
|
||||
| - `lua` dropped here while still borrowed
|
||||
32 | }
|
||||
| - `lua` dropped here while still borrowed
|
||||
|
||||
error[E0505]: cannot move out of `lua` because it is borrowed
|
||||
--> tests/compile/static_callback_args.rs:22:10
|
||||
|
|
||||
12 | / lua.create_function(|_, table: Table| {
|
||||
13 | | BAD_TIME.with(|bt| {
|
||||
14 | | *bt.borrow_mut() = Some(table);
|
||||
15 | | });
|
||||
16 | | Ok(())
|
||||
17 | | })?
|
||||
| | -
|
||||
| | |
|
||||
| |______borrow of `lua` occurs here
|
||||
| argument requires that `lua` is borrowed for `'static`
|
||||
12 | / lua.create_function(|_, table: Table| {
|
||||
13 | | BAD_TIME.with(|bt| {
|
||||
| |_________-
|
||||
14 | || *bt.borrow_mut() = Some(table);
|
||||
15 | || });
|
||||
| ||__________- argument requires that `lua` is borrowed for `'static`
|
||||
16 | | Ok(())
|
||||
17 | | })?
|
||||
| |______- borrow of `lua` occurs here
|
||||
...
|
||||
22 | drop(lua);
|
||||
| ^^^ move out of `lua` occurs here
|
||||
22 | drop(lua);
|
||||
| ^^^ move out of `lua` occurs here
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#![cfg(feature = "luau")]
|
||||
|
||||
use std::env;
|
||||
use std::fmt::Debug;
|
||||
use std::fs;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -75,18 +77,33 @@ fn test_vectors() -> Result<()> {
|
|||
fn test_readonly_table() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let t = lua.create_table()?;
|
||||
let t = lua.create_sequence_from([1])?;
|
||||
assert!(!t.is_readonly());
|
||||
t.set_readonly(true);
|
||||
assert!(t.is_readonly());
|
||||
|
||||
match t.set("key", "value") {
|
||||
Err(Error::RuntimeError(err)) if err.contains("Attempt to modify a readonly table") => {}
|
||||
r => panic!(
|
||||
"expected RuntimeError(...) with a specific message, got {:?}",
|
||||
r
|
||||
),
|
||||
};
|
||||
#[track_caller]
|
||||
fn check_readonly_error<T: Debug>(res: Result<T>) {
|
||||
match res {
|
||||
Err(Error::RuntimeError(e)) if e.contains("attempt to modify a readonly table") => {}
|
||||
r => panic!("expected RuntimeError(...) with a specific message, got {r:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
check_readonly_error(t.set("key", "value"));
|
||||
check_readonly_error(t.raw_set("key", "value"));
|
||||
check_readonly_error(t.raw_insert(1, "value"));
|
||||
check_readonly_error(t.raw_remove(1));
|
||||
check_readonly_error(t.push("value"));
|
||||
check_readonly_error(t.pop::<Value>());
|
||||
check_readonly_error(t.raw_push("value"));
|
||||
check_readonly_error(t.raw_pop::<Value>());
|
||||
|
||||
// Special case
|
||||
match catch_unwind(AssertUnwindSafe(|| t.set_metatable(None))) {
|
||||
Ok(_) => panic!("expected panic, got nothing"),
|
||||
Err(_) => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -220,7 +237,7 @@ fn test_interrupts() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_coverate() -> Result<()> {
|
||||
fn test_coverage() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.set_compiler(Compiler::default().set_coverage_level(1));
|
||||
|
|
|
@ -109,6 +109,49 @@ fn test_table() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_push_pop() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Test raw access
|
||||
let table1 = lua.create_sequence_from(vec![123])?;
|
||||
table1.raw_push(321)?;
|
||||
assert_eq!(
|
||||
table1
|
||||
.clone()
|
||||
.raw_sequence_values::<i64>()
|
||||
.collect::<Result<Vec<_>>>()?,
|
||||
vec![123, 321]
|
||||
);
|
||||
assert_eq!(table1.raw_pop::<i64>()?, 321);
|
||||
assert_eq!(table1.raw_pop::<i64>()?, 123);
|
||||
assert_eq!(table1.raw_pop::<Value>()?, Value::Nil); // An extra pop should do nothing
|
||||
assert_eq!(table1.raw_len(), 0);
|
||||
|
||||
// Test access through metamethods
|
||||
let table2 = lua
|
||||
.load(
|
||||
r#"
|
||||
local proxy_table = {234}
|
||||
table2 = setmetatable({}, {
|
||||
__len = function() return #proxy_table end,
|
||||
__index = proxy_table,
|
||||
__newindex = proxy_table,
|
||||
})
|
||||
return table2
|
||||
"#,
|
||||
)
|
||||
.eval::<Table>()?;
|
||||
table2.push(345)?;
|
||||
assert_eq!(table2.len()?, 2);
|
||||
assert_eq!(table2.pop::<i64>()?, 345);
|
||||
assert_eq!(table2.pop::<i64>()?, 234);
|
||||
assert_eq!(table2.pop::<Value>()?, Value::Nil);
|
||||
assert_eq!(table2.len()?, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_sequence_from() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
|
|
@ -73,6 +73,7 @@ fn test_load() -> Result<()> {
|
|||
let result: i32 = func.call(())?;
|
||||
assert_eq!(result, 3);
|
||||
|
||||
assert!(lua.load("").exec().is_ok());
|
||||
assert!(lua.load("§$%§&$%&").exec().is_err());
|
||||
|
||||
Ok(())
|
||||
|
@ -792,6 +793,17 @@ fn test_replace_registry_value() -> Result<()> {
|
|||
let key = lua.create_registry_value::<i32>(42)?;
|
||||
lua.replace_registry_value(&key, "new value")?;
|
||||
assert_eq!(lua.registry_value::<String>(&key)?, "new value");
|
||||
lua.replace_registry_value(&key, Value::Nil)?;
|
||||
assert_eq!(lua.registry_value::<Value>(&key)?, Value::Nil);
|
||||
lua.replace_registry_value(&key, 123)?;
|
||||
assert_eq!(lua.registry_value::<i32>(&key)?, 123);
|
||||
|
||||
// It should be impossible to replace (initial) nil value with non-nil
|
||||
let key2 = lua.create_registry_value(Value::Nil)?;
|
||||
match lua.replace_registry_value(&key2, "abc") {
|
||||
Err(Error::RuntimeError(_)) => {}
|
||||
r => panic!("expected RuntimeError, got {r:?}"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -843,6 +855,28 @@ fn test_mismatched_registry_key() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_registry_value_reuse() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let r1 = lua.create_registry_value("value1")?;
|
||||
let r1_slot = format!("{r1:?}");
|
||||
drop(r1);
|
||||
|
||||
// Previous slot must not be reused by nil value
|
||||
let r2 = lua.create_registry_value(Value::Nil)?;
|
||||
let r2_slot = format!("{r2:?}");
|
||||
assert_ne!(r1_slot, r2_slot);
|
||||
drop(r2);
|
||||
|
||||
// But should be reused by non-nil value
|
||||
let r3 = lua.create_registry_value("value3")?;
|
||||
let r3_slot = format!("{r3:?}");
|
||||
assert_eq!(r1_slot, r3_slot);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_application_data() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
|
Loading…
Reference in a new issue