2018-02-10 17:55:08 -05:00
|
|
|
use std::{mem, ptr};
|
Big API incompatible error change, remove dependency on error_chain
The current situation with error_chain is less than ideal, and there are lots of
conflicting interests that are impossible to meet at once. Here is an
unorganized brain dump of the current situation, stay awhile and listen!
This change was triggered ultimately by the desire to make LuaError implement
Clone, and this is currently impossible with error_chain. LuaError must
implement Clone to be a proper lua citizen that can live as userdata within a
lua runtime, because there is no way to limit what the lua runtime can do with a
received error. Currently, this is solved by there being a rule that the error
will "expire" if the error is passed back into rust, and this is very
sub-optimal. In fact, one could easily imagine a scenario where lua is for
example memoizing some function, and if the function has ever errored in the
past the function should continue returning the same error, and this situation
immediately fails with this restriciton in place.
Additionally, there are other more minor problems with error_chain which make
the API less good than it could be, or limit how we can use error_chain. This
change has already solved a small bug in a Chucklefish project, where the
conversion from an external error type (Borrow[Mut]Error) was allowed but not
intended for user code, and was accidentally used. Additionally, pattern
matching on error_chain errors, which should be common when dealing with Lua, is
less convenient than a hand rolled error type.
So, if we decide not to use error_chain, we now have a new set of problems if we
decide interoperability with error_chain is important. The first problem we run
into is that there are two natural bounds for wrapped errors that we would
pick, (Error + Send + Sync), or just Error, and neither of them will
interoperate well with error_chain. (Error + Send + Sync) means we can't wrap
error chain errors into LuaError::ExternalError (they're missing the Sync
bound), and having the bounds be just Error means the opposite, that we can't
hold a LuaError inside an error_chain error.
We could just decide that interoperability with error_chain is the most
important qualification, and pick (Error + Send), but this causes a DIFFERENT
set of problems. The rust ecosystem has the two primary error bounds as Error
or (Error + Send + Sync), and there are Into impls from &str / String to
Box<Error + Send + Sync> for example, but NOT (Error + Send). This means that
we are forced to manually recreate the conversions from &str / String to
LuaError rather than relying on a single Into<Box<Error + Send + Sync>> bound,
but this means that string conversions have a different set of methods than
other error types for external error conversion. I have not been able to figure
out an API that I am happy with that uses the (Error + Send) bound. Box<Error>
is obnoxious because not having errors implement Send causes needless problems
in a multithreaded context, so that leaves (Error + Send + Sync). This is
actually a completely reasonable bound for external errors, and has the nice
String Into impls that we would want, the ONLY problem is that it is a pain to
interoperate with the current version of error_chain.
It would be nice to be able to specify the traits that an error generated by the
error_chain macro would implement, and this is apparently in progress in the
error_chain library. This would solve both the problem with not being able to
implement Clone and the problems with (Error + Send) bounds. I am not convinced
that this library should go back to using error_chain when that functionality is
in stable error_chain though, because of the other minor usability problems with
using error_chain.
In that theoretical situation, the downside of NOT using error_chain is simply
that there would not be automatic stacktraces of LuaError. This is not a huge
problem, because stack traces of lua errors are not extremely useful, and for
external errors it is not too hard to create a different version of the
LuaExternalResult / LuaExternalError traits and do conversion from an
error_chain type into a type that will print the stacktrace on display, or
use downcasting in the error causes.
So in summary, this library is no longer using error_chain, and probably will
not use it again in the future. Currently this means that to interoperate with
error_chain, you should use error_chain 0.8.1, which derives Sync on errors, or
wait for a version that supports user defined trait derives. In the future
when error_chain supports user defined trait derives, users may have to take an
extra step to make wrapped external errors print the stacktrace that they
capture.
This change works, but is not entirely complete. There is no error
documentation yet, and the change brought to a head an ugly module organization
problem. There will be more commits for documentation and reorganization, then
a new stable version of rlua.
2017-06-24 18:11:56 -04:00
|
|
|
use std::sync::Arc;
|
2017-05-21 19:50:59 -04:00
|
|
|
use std::ffi::CStr;
|
|
|
|
use std::any::Any;
|
|
|
|
use std::os::raw::{c_char, c_int, c_void};
|
|
|
|
use std::panic::{catch_unwind, resume_unwind, UnwindSafe};
|
|
|
|
|
|
|
|
use ffi;
|
2017-10-23 16:42:20 -04:00
|
|
|
use error::{Error, Result};
|
2017-05-21 19:50:59 -04:00
|
|
|
|
2018-02-09 23:35:29 -05:00
|
|
|
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
|
|
|
|
// clear the stack and panic.
|
Another major API change, out of stack space is not an Err
It, ahem "should not" be possible to exhaust lua stack space in normal usage,
and causing stack errors to be Err is slightly obnoxious. I have been wanting
to make this change for a while, and removing the callback API from tables makes
this sensible *I think*.
I can think of a couple of ways that this is not technically true, but I think
that they are acceptable, or should be handled differently.
One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a
bug already, because there is an argument limit in Lua which is lower than the
stack limit. I'm not sure what happens there, but if it is a stack based panic,
(or any panic?) it is a bug.
Two, I believe that if you recurse over and over between lua -> rust -> lua ->
rust etc, and call rlua API functions, you might get a stack panic. I think for
trusted lua code, this is morally equivalent to a regular stack overflow in
plain rust, which is already.. well it's not a panic but it's some kind of safe
crash I'm not sure, so I think this is acceptable. For *untrusted* lua code,
this could theoretically be a problem if the API provided a callback that would
call back into lua, then some lua script could force a stack based panic. There
are so many concerns with untrusted lua code, and this library is NOT safe
enough yet for untrusted code (it doesn't even provide an option to limit lua to
the safe API subset yet!), so this is not currently an issue. When the library
provides support for "safe lua", it should come with big warnings anyway, and
being able to force a stack panic is pretty minor in comparison.
I think if there are other ways to cause unbounded stack usage, that it is a
bug, or there can be an error just for that situation, like argument count
limits.
This commit also fixes several stupid bugs with tests, stack checking, and
panics.
2017-06-25 16:52:32 -04:00
|
|
|
pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) {
|
2018-02-07 17:05:00 -05:00
|
|
|
lua_internal_assert!(
|
Another major API change, out of stack space is not an Err
It, ahem "should not" be possible to exhaust lua stack space in normal usage,
and causing stack errors to be Err is slightly obnoxious. I have been wanting
to make this change for a while, and removing the callback API from tables makes
this sensible *I think*.
I can think of a couple of ways that this is not technically true, but I think
that they are acceptable, or should be handled differently.
One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a
bug already, because there is an argument limit in Lua which is lower than the
stack limit. I'm not sure what happens there, but if it is a stack based panic,
(or any panic?) it is a bug.
Two, I believe that if you recurse over and over between lua -> rust -> lua ->
rust etc, and call rlua API functions, you might get a stack panic. I think for
trusted lua code, this is morally equivalent to a regular stack overflow in
plain rust, which is already.. well it's not a panic but it's some kind of safe
crash I'm not sure, so I think this is acceptable. For *untrusted* lua code,
this could theoretically be a problem if the API provided a callback that would
call back into lua, then some lua script could force a stack based panic. There
are so many concerns with untrusted lua code, and this library is NOT safe
enough yet for untrusted code (it doesn't even provide an option to limit lua to
the safe API subset yet!), so this is not currently an issue. When the library
provides support for "safe lua", it should come with big warnings anyway, and
being able to force a stack panic is pretty minor in comparison.
I think if there are other ways to cause unbounded stack usage, that it is a
bug, or there can be an error just for that situation, like argument count
limits.
This commit also fixes several stupid bugs with tests, stack checking, and
panics.
2017-06-25 16:52:32 -04:00
|
|
|
state,
|
|
|
|
ffi::lua_checkstack(state, amount) != 0,
|
|
|
|
"out of stack space"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-02-09 23:35:29 -05:00
|
|
|
// Similar to `check_stack`, but returns `Error::StackError` on failure. Useful for user controlled
|
|
|
|
// sizes, which should not cause a panic.
|
|
|
|
pub unsafe fn check_stack_err(state: *mut ffi::lua_State, amount: c_int) -> Result<()> {
|
|
|
|
if ffi::lua_checkstack(state, amount) == 0 {
|
|
|
|
Err(Error::StackError)
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Another major API change, out of stack space is not an Err
It, ahem "should not" be possible to exhaust lua stack space in normal usage,
and causing stack errors to be Err is slightly obnoxious. I have been wanting
to make this change for a while, and removing the callback API from tables makes
this sensible *I think*.
I can think of a couple of ways that this is not technically true, but I think
that they are acceptable, or should be handled differently.
One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a
bug already, because there is an argument limit in Lua which is lower than the
stack limit. I'm not sure what happens there, but if it is a stack based panic,
(or any panic?) it is a bug.
Two, I believe that if you recurse over and over between lua -> rust -> lua ->
rust etc, and call rlua API functions, you might get a stack panic. I think for
trusted lua code, this is morally equivalent to a regular stack overflow in
plain rust, which is already.. well it's not a panic but it's some kind of safe
crash I'm not sure, so I think this is acceptable. For *untrusted* lua code,
this could theoretically be a problem if the API provided a callback that would
call back into lua, then some lua script could force a stack based panic. There
are so many concerns with untrusted lua code, and this library is NOT safe
enough yet for untrusted code (it doesn't even provide an option to limit lua to
the safe API subset yet!), so this is not currently an issue. When the library
provides support for "safe lua", it should come with big warnings anyway, and
being able to force a stack panic is pretty minor in comparison.
I think if there are other ways to cause unbounded stack usage, that it is a
bug, or there can be an error just for that situation, like argument count
limits.
This commit also fixes several stupid bugs with tests, stack checking, and
panics.
2017-06-25 16:52:32 -04:00
|
|
|
// Run an operation on a lua_State and check that the stack change is what is
|
|
|
|
// expected. If the stack change does not match, clears the stack and panics.
|
|
|
|
pub unsafe fn stack_guard<F, R>(state: *mut ffi::lua_State, change: c_int, op: F) -> R
|
|
|
|
where
|
|
|
|
F: FnOnce() -> R,
|
|
|
|
{
|
2018-02-09 21:21:29 -05:00
|
|
|
let expected = ffi::lua_gettop(state) + change;
|
|
|
|
lua_internal_assert!(
|
|
|
|
state,
|
|
|
|
expected >= 0,
|
|
|
|
"too many stack values would be popped"
|
|
|
|
);
|
Another major API change, out of stack space is not an Err
It, ahem "should not" be possible to exhaust lua stack space in normal usage,
and causing stack errors to be Err is slightly obnoxious. I have been wanting
to make this change for a while, and removing the callback API from tables makes
this sensible *I think*.
I can think of a couple of ways that this is not technically true, but I think
that they are acceptable, or should be handled differently.
One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a
bug already, because there is an argument limit in Lua which is lower than the
stack limit. I'm not sure what happens there, but if it is a stack based panic,
(or any panic?) it is a bug.
Two, I believe that if you recurse over and over between lua -> rust -> lua ->
rust etc, and call rlua API functions, you might get a stack panic. I think for
trusted lua code, this is morally equivalent to a regular stack overflow in
plain rust, which is already.. well it's not a panic but it's some kind of safe
crash I'm not sure, so I think this is acceptable. For *untrusted* lua code,
this could theoretically be a problem if the API provided a callback that would
call back into lua, then some lua script could force a stack based panic. There
are so many concerns with untrusted lua code, and this library is NOT safe
enough yet for untrusted code (it doesn't even provide an option to limit lua to
the safe API subset yet!), so this is not currently an issue. When the library
provides support for "safe lua", it should come with big warnings anyway, and
being able to force a stack panic is pretty minor in comparison.
I think if there are other ways to cause unbounded stack usage, that it is a
bug, or there can be an error just for that situation, like argument count
limits.
This commit also fixes several stupid bugs with tests, stack checking, and
panics.
2017-06-25 16:52:32 -04:00
|
|
|
|
2018-02-09 21:21:29 -05:00
|
|
|
let res = op();
|
Another major API change, out of stack space is not an Err
It, ahem "should not" be possible to exhaust lua stack space in normal usage,
and causing stack errors to be Err is slightly obnoxious. I have been wanting
to make this change for a while, and removing the callback API from tables makes
this sensible *I think*.
I can think of a couple of ways that this is not technically true, but I think
that they are acceptable, or should be handled differently.
One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a
bug already, because there is an argument limit in Lua which is lower than the
stack limit. I'm not sure what happens there, but if it is a stack based panic,
(or any panic?) it is a bug.
Two, I believe that if you recurse over and over between lua -> rust -> lua ->
rust etc, and call rlua API functions, you might get a stack panic. I think for
trusted lua code, this is morally equivalent to a regular stack overflow in
plain rust, which is already.. well it's not a panic but it's some kind of safe
crash I'm not sure, so I think this is acceptable. For *untrusted* lua code,
this could theoretically be a problem if the API provided a callback that would
call back into lua, then some lua script could force a stack based panic. There
are so many concerns with untrusted lua code, and this library is NOT safe
enough yet for untrusted code (it doesn't even provide an option to limit lua to
the safe API subset yet!), so this is not currently an issue. When the library
provides support for "safe lua", it should come with big warnings anyway, and
being able to force a stack panic is pretty minor in comparison.
I think if there are other ways to cause unbounded stack usage, that it is a
bug, or there can be an error just for that situation, like argument count
limits.
This commit also fixes several stupid bugs with tests, stack checking, and
panics.
2017-06-25 16:52:32 -04:00
|
|
|
|
2018-02-09 21:21:29 -05:00
|
|
|
let top = ffi::lua_gettop(state);
|
|
|
|
lua_internal_assert!(
|
|
|
|
state,
|
|
|
|
ffi::lua_gettop(state) == expected,
|
|
|
|
"expected stack to be {}, got {}",
|
|
|
|
expected,
|
|
|
|
top
|
|
|
|
);
|
Another major API change, out of stack space is not an Err
It, ahem "should not" be possible to exhaust lua stack space in normal usage,
and causing stack errors to be Err is slightly obnoxious. I have been wanting
to make this change for a while, and removing the callback API from tables makes
this sensible *I think*.
I can think of a couple of ways that this is not technically true, but I think
that they are acceptable, or should be handled differently.
One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a
bug already, because there is an argument limit in Lua which is lower than the
stack limit. I'm not sure what happens there, but if it is a stack based panic,
(or any panic?) it is a bug.
Two, I believe that if you recurse over and over between lua -> rust -> lua ->
rust etc, and call rlua API functions, you might get a stack panic. I think for
trusted lua code, this is morally equivalent to a regular stack overflow in
plain rust, which is already.. well it's not a panic but it's some kind of safe
crash I'm not sure, so I think this is acceptable. For *untrusted* lua code,
this could theoretically be a problem if the API provided a callback that would
call back into lua, then some lua script could force a stack based panic. There
are so many concerns with untrusted lua code, and this library is NOT safe
enough yet for untrusted code (it doesn't even provide an option to limit lua to
the safe API subset yet!), so this is not currently an issue. When the library
provides support for "safe lua", it should come with big warnings anyway, and
being able to force a stack panic is pretty minor in comparison.
I think if there are other ways to cause unbounded stack usage, that it is a
bug, or there can be an error just for that situation, like argument count
limits.
This commit also fixes several stupid bugs with tests, stack checking, and
panics.
2017-06-25 16:52:32 -04:00
|
|
|
|
2018-02-09 21:21:29 -05:00
|
|
|
res
|
2017-06-05 00:03:39 -04:00
|
|
|
}
|
|
|
|
|
Another major API change, out of stack space is not an Err
It, ahem "should not" be possible to exhaust lua stack space in normal usage,
and causing stack errors to be Err is slightly obnoxious. I have been wanting
to make this change for a while, and removing the callback API from tables makes
this sensible *I think*.
I can think of a couple of ways that this is not technically true, but I think
that they are acceptable, or should be handled differently.
One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a
bug already, because there is an argument limit in Lua which is lower than the
stack limit. I'm not sure what happens there, but if it is a stack based panic,
(or any panic?) it is a bug.
Two, I believe that if you recurse over and over between lua -> rust -> lua ->
rust etc, and call rlua API functions, you might get a stack panic. I think for
trusted lua code, this is morally equivalent to a regular stack overflow in
plain rust, which is already.. well it's not a panic but it's some kind of safe
crash I'm not sure, so I think this is acceptable. For *untrusted* lua code,
this could theoretically be a problem if the API provided a callback that would
call back into lua, then some lua script could force a stack based panic. There
are so many concerns with untrusted lua code, and this library is NOT safe
enough yet for untrusted code (it doesn't even provide an option to limit lua to
the safe API subset yet!), so this is not currently an issue. When the library
provides support for "safe lua", it should come with big warnings anyway, and
being able to force a stack panic is pretty minor in comparison.
I think if there are other ways to cause unbounded stack usage, that it is a
bug, or there can be an error just for that situation, like argument count
limits.
This commit also fixes several stupid bugs with tests, stack checking, and
panics.
2017-06-25 16:52:32 -04:00
|
|
|
// Run an operation on a lua_State and automatically clean up the stack before
|
|
|
|
// returning. Takes the lua_State, the expected stack size change, and an
|
|
|
|
// operation to run. If the operation results in success, then the stack is
|
|
|
|
// inspected to make sure the change in stack size matches the expected change
|
|
|
|
// and otherwise this is a logic error and will panic. If the operation results
|
|
|
|
// in an error, the stack is shrunk to the value before the call. If the
|
|
|
|
// operation results in an error and the stack is smaller than the value before
|
|
|
|
// the call, then this is unrecoverable and this will panic. If this function
|
|
|
|
// panics, it will clear the stack before panicking.
|
2017-07-24 10:40:00 -04:00
|
|
|
pub unsafe fn stack_err_guard<F, R>(state: *mut ffi::lua_State, change: c_int, op: F) -> Result<R>
|
2017-06-15 10:26:39 -04:00
|
|
|
where
|
2017-07-18 16:21:12 -04:00
|
|
|
F: FnOnce() -> Result<R>,
|
2017-05-21 19:50:59 -04:00
|
|
|
{
|
2018-02-09 21:21:29 -05:00
|
|
|
let expected = ffi::lua_gettop(state) + change;
|
|
|
|
lua_internal_assert!(
|
|
|
|
state,
|
|
|
|
expected >= 0,
|
|
|
|
"too many stack values would be popped"
|
|
|
|
);
|
|
|
|
|
|
|
|
let res = op();
|
|
|
|
|
|
|
|
let top = ffi::lua_gettop(state);
|
|
|
|
if res.is_ok() {
|
2018-02-07 17:05:00 -05:00
|
|
|
lua_internal_assert!(
|
Another major API change, out of stack space is not an Err
It, ahem "should not" be possible to exhaust lua stack space in normal usage,
and causing stack errors to be Err is slightly obnoxious. I have been wanting
to make this change for a while, and removing the callback API from tables makes
this sensible *I think*.
I can think of a couple of ways that this is not technically true, but I think
that they are acceptable, or should be handled differently.
One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a
bug already, because there is an argument limit in Lua which is lower than the
stack limit. I'm not sure what happens there, but if it is a stack based panic,
(or any panic?) it is a bug.
Two, I believe that if you recurse over and over between lua -> rust -> lua ->
rust etc, and call rlua API functions, you might get a stack panic. I think for
trusted lua code, this is morally equivalent to a regular stack overflow in
plain rust, which is already.. well it's not a panic but it's some kind of safe
crash I'm not sure, so I think this is acceptable. For *untrusted* lua code,
this could theoretically be a problem if the API provided a callback that would
call back into lua, then some lua script could force a stack based panic. There
are so many concerns with untrusted lua code, and this library is NOT safe
enough yet for untrusted code (it doesn't even provide an option to limit lua to
the safe API subset yet!), so this is not currently an issue. When the library
provides support for "safe lua", it should come with big warnings anyway, and
being able to force a stack panic is pretty minor in comparison.
I think if there are other ways to cause unbounded stack usage, that it is a
bug, or there can be an error just for that situation, like argument count
limits.
This commit also fixes several stupid bugs with tests, stack checking, and
panics.
2017-06-25 16:52:32 -04:00
|
|
|
state,
|
2018-02-09 21:21:29 -05:00
|
|
|
ffi::lua_gettop(state) == expected,
|
|
|
|
"expected stack to be {}, got {}",
|
|
|
|
expected,
|
|
|
|
top
|
2017-06-15 10:26:39 -04:00
|
|
|
);
|
2017-05-21 19:50:59 -04:00
|
|
|
} else {
|
2018-02-09 21:21:29 -05:00
|
|
|
lua_internal_assert!(
|
|
|
|
state,
|
|
|
|
top >= expected,
|
|
|
|
"{} too many stack values popped",
|
|
|
|
top - expected
|
|
|
|
);
|
|
|
|
if top > expected {
|
|
|
|
ffi::lua_settop(state, expected);
|
2017-05-21 19:50:59 -04:00
|
|
|
}
|
|
|
|
}
|
2018-02-09 21:21:29 -05:00
|
|
|
res
|
2017-05-21 19:50:59 -04:00
|
|
|
}
|
|
|
|
|
2017-12-03 20:10:45 -05:00
|
|
|
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
|
|
|
|
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
|
|
|
|
// limited lua stack. `nargs` and `nresults` are similar to the parameters of `lua_pcall`, but the
|
|
|
|
// given function return type is not the return value count, instead the inner function return
|
|
|
|
// values are assumed to match the `nresults` param. Internally uses 3 extra stack spaces, and does
|
2018-02-15 21:39:35 -05:00
|
|
|
// not call checkstack. Provided function must *not* panic.
|
2017-12-03 23:01:03 -05:00
|
|
|
pub unsafe fn protect_lua_call<F, R>(
|
|
|
|
state: *mut ffi::lua_State,
|
|
|
|
nargs: c_int,
|
|
|
|
nresults: c_int,
|
|
|
|
f: F,
|
|
|
|
) -> Result<R>
|
|
|
|
where
|
Simplify handling of userdata __gc and resurrected userdata.
Now, simply remove the userdata table immediately before dropping the userdata.
This does two things, it prevents __gc from double dropping the userdata, and
after the first call to __gc, it prevents the userdata from being identified as
any particular userdata type, so it cannot be misused after being finalized.
This change thus removes the userdata invalidation error, and simplifies a lot
of userdata handling code.
It also fixes a panic bug. Because there is no predictable order for
finalizers, it is possible to run a userdata finalizer that does not resurrect
itself before a lua table finalizer that accesses that userdata, and this means
that there were several asserts that were possible to trigger in normal Lua code
in util.rs related to `WrappedError`.
Now, finalized userdata is simply a userdata with no methods, so any use of
finalized userdata becomes a normal script runtime error (though, with a
potentially confusing error message). As a future improvement, we could set
a metatable on finalized userdata that provides a better error message.
2018-01-27 18:12:39 -05:00
|
|
|
F: FnOnce(*mut ffi::lua_State) -> R,
|
2017-12-03 20:10:45 -05:00
|
|
|
{
|
|
|
|
struct Params<F, R> {
|
|
|
|
function: F,
|
Simplify handling of userdata __gc and resurrected userdata.
Now, simply remove the userdata table immediately before dropping the userdata.
This does two things, it prevents __gc from double dropping the userdata, and
after the first call to __gc, it prevents the userdata from being identified as
any particular userdata type, so it cannot be misused after being finalized.
This change thus removes the userdata invalidation error, and simplifies a lot
of userdata handling code.
It also fixes a panic bug. Because there is no predictable order for
finalizers, it is possible to run a userdata finalizer that does not resurrect
itself before a lua table finalizer that accesses that userdata, and this means
that there were several asserts that were possible to trigger in normal Lua code
in util.rs related to `WrappedError`.
Now, finalized userdata is simply a userdata with no methods, so any use of
finalized userdata becomes a normal script runtime error (though, with a
potentially confusing error message). As a future improvement, we could set
a metatable on finalized userdata that provides a better error message.
2018-01-27 18:12:39 -05:00
|
|
|
result: R,
|
2017-12-03 20:10:45 -05:00
|
|
|
nresults: c_int,
|
2017-12-02 18:37:17 -05:00
|
|
|
}
|
|
|
|
|
2018-02-15 21:39:35 -05:00
|
|
|
#[cfg_attr(feature = "unwind", unwind)]
|
2017-12-03 20:10:45 -05:00
|
|
|
unsafe extern "C" fn do_call<F, R>(state: *mut ffi::lua_State) -> c_int
|
2017-12-03 23:01:03 -05:00
|
|
|
where
|
Simplify handling of userdata __gc and resurrected userdata.
Now, simply remove the userdata table immediately before dropping the userdata.
This does two things, it prevents __gc from double dropping the userdata, and
after the first call to __gc, it prevents the userdata from being identified as
any particular userdata type, so it cannot be misused after being finalized.
This change thus removes the userdata invalidation error, and simplifies a lot
of userdata handling code.
It also fixes a panic bug. Because there is no predictable order for
finalizers, it is possible to run a userdata finalizer that does not resurrect
itself before a lua table finalizer that accesses that userdata, and this means
that there were several asserts that were possible to trigger in normal Lua code
in util.rs related to `WrappedError`.
Now, finalized userdata is simply a userdata with no methods, so any use of
finalized userdata becomes a normal script runtime error (though, with a
potentially confusing error message). As a future improvement, we could set
a metatable on finalized userdata that provides a better error message.
2018-01-27 18:12:39 -05:00
|
|
|
F: FnOnce(*mut ffi::lua_State) -> R,
|
2017-12-03 20:10:45 -05:00
|
|
|
{
|
|
|
|
let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>;
|
|
|
|
ffi::lua_pop(state, 1);
|
Simplify handling of userdata __gc and resurrected userdata.
Now, simply remove the userdata table immediately before dropping the userdata.
This does two things, it prevents __gc from double dropping the userdata, and
after the first call to __gc, it prevents the userdata from being identified as
any particular userdata type, so it cannot be misused after being finalized.
This change thus removes the userdata invalidation error, and simplifies a lot
of userdata handling code.
It also fixes a panic bug. Because there is no predictable order for
finalizers, it is possible to run a userdata finalizer that does not resurrect
itself before a lua table finalizer that accesses that userdata, and this means
that there were several asserts that were possible to trigger in normal Lua code
in util.rs related to `WrappedError`.
Now, finalized userdata is simply a userdata with no methods, so any use of
finalized userdata becomes a normal script runtime error (though, with a
potentially confusing error message). As a future improvement, we could set
a metatable on finalized userdata that provides a better error message.
2018-01-27 18:12:39 -05:00
|
|
|
|
|
|
|
let function = mem::replace(&mut (*params).function, mem::uninitialized());
|
2018-01-27 18:37:06 -05:00
|
|
|
ptr::write(&mut (*params).result, function(state));
|
Simplify handling of userdata __gc and resurrected userdata.
Now, simply remove the userdata table immediately before dropping the userdata.
This does two things, it prevents __gc from double dropping the userdata, and
after the first call to __gc, it prevents the userdata from being identified as
any particular userdata type, so it cannot be misused after being finalized.
This change thus removes the userdata invalidation error, and simplifies a lot
of userdata handling code.
It also fixes a panic bug. Because there is no predictable order for
finalizers, it is possible to run a userdata finalizer that does not resurrect
itself before a lua table finalizer that accesses that userdata, and this means
that there were several asserts that were possible to trigger in normal Lua code
in util.rs related to `WrappedError`.
Now, finalized userdata is simply a userdata with no methods, so any use of
finalized userdata becomes a normal script runtime error (though, with a
potentially confusing error message). As a future improvement, we could set
a metatable on finalized userdata that provides a better error message.
2018-01-27 18:12:39 -05:00
|
|
|
// params now has function uninitialied and result initialized
|
|
|
|
|
2017-12-03 20:10:45 -05:00
|
|
|
if (*params).nresults == ffi::LUA_MULTRET {
|
|
|
|
ffi::lua_gettop(state)
|
|
|
|
} else {
|
|
|
|
(*params).nresults
|
|
|
|
}
|
|
|
|
}
|
2017-12-02 18:37:17 -05:00
|
|
|
|
2017-12-03 20:10:45 -05:00
|
|
|
let stack_start = ffi::lua_gettop(state) - nargs;
|
2017-12-02 18:37:17 -05:00
|
|
|
|
2017-12-03 20:10:45 -05:00
|
|
|
ffi::lua_pushcfunction(state, error_traceback);
|
|
|
|
ffi::lua_pushcfunction(state, do_call::<F, R>);
|
|
|
|
ffi::lua_rotate(state, stack_start + 1, 2);
|
2017-12-02 18:37:17 -05:00
|
|
|
|
Simplify handling of userdata __gc and resurrected userdata.
Now, simply remove the userdata table immediately before dropping the userdata.
This does two things, it prevents __gc from double dropping the userdata, and
after the first call to __gc, it prevents the userdata from being identified as
any particular userdata type, so it cannot be misused after being finalized.
This change thus removes the userdata invalidation error, and simplifies a lot
of userdata handling code.
It also fixes a panic bug. Because there is no predictable order for
finalizers, it is possible to run a userdata finalizer that does not resurrect
itself before a lua table finalizer that accesses that userdata, and this means
that there were several asserts that were possible to trigger in normal Lua code
in util.rs related to `WrappedError`.
Now, finalized userdata is simply a userdata with no methods, so any use of
finalized userdata becomes a normal script runtime error (though, with a
potentially confusing error message). As a future improvement, we could set
a metatable on finalized userdata that provides a better error message.
2018-01-27 18:12:39 -05:00
|
|
|
// We are about to do some really scary stuff with the Params structure, both because
|
|
|
|
// protect_lua_call is very hot, and becuase we would like to allow the function type to be
|
|
|
|
// FnOnce rather than FnMut. We are using Params here both to pass data to the callback and
|
|
|
|
// return data from the callback.
|
|
|
|
//
|
|
|
|
// params starts out with function initialized and result uninitialized, nresults is Copy so we
|
|
|
|
// don't care about it.
|
2017-12-03 20:10:45 -05:00
|
|
|
let mut params = Params {
|
|
|
|
function: f,
|
Simplify handling of userdata __gc and resurrected userdata.
Now, simply remove the userdata table immediately before dropping the userdata.
This does two things, it prevents __gc from double dropping the userdata, and
after the first call to __gc, it prevents the userdata from being identified as
any particular userdata type, so it cannot be misused after being finalized.
This change thus removes the userdata invalidation error, and simplifies a lot
of userdata handling code.
It also fixes a panic bug. Because there is no predictable order for
finalizers, it is possible to run a userdata finalizer that does not resurrect
itself before a lua table finalizer that accesses that userdata, and this means
that there were several asserts that were possible to trigger in normal Lua code
in util.rs related to `WrappedError`.
Now, finalized userdata is simply a userdata with no methods, so any use of
finalized userdata becomes a normal script runtime error (though, with a
potentially confusing error message). As a future improvement, we could set
a metatable on finalized userdata that provides a better error message.
2018-01-27 18:12:39 -05:00
|
|
|
result: mem::uninitialized(),
|
2017-12-03 20:10:45 -05:00
|
|
|
nresults,
|
|
|
|
};
|
2017-12-02 18:37:17 -05:00
|
|
|
|
2017-12-03 20:10:45 -05:00
|
|
|
ffi::lua_pushlightuserdata(state, &mut params as *mut Params<F, R> as *mut c_void);
|
2017-12-02 18:37:17 -05:00
|
|
|
|
2017-12-03 20:10:45 -05:00
|
|
|
let ret = ffi::lua_pcall(state, nargs + 1, nresults, stack_start + 1);
|
Simplify handling of userdata __gc and resurrected userdata.
Now, simply remove the userdata table immediately before dropping the userdata.
This does two things, it prevents __gc from double dropping the userdata, and
after the first call to __gc, it prevents the userdata from being identified as
any particular userdata type, so it cannot be misused after being finalized.
This change thus removes the userdata invalidation error, and simplifies a lot
of userdata handling code.
It also fixes a panic bug. Because there is no predictable order for
finalizers, it is possible to run a userdata finalizer that does not resurrect
itself before a lua table finalizer that accesses that userdata, and this means
that there were several asserts that were possible to trigger in normal Lua code
in util.rs related to `WrappedError`.
Now, finalized userdata is simply a userdata with no methods, so any use of
finalized userdata becomes a normal script runtime error (though, with a
potentially confusing error message). As a future improvement, we could set
a metatable on finalized userdata that provides a better error message.
2018-01-27 18:12:39 -05:00
|
|
|
let result = mem::replace(&mut params.result, mem::uninitialized());
|
|
|
|
|
|
|
|
// params now has both function and result uninitialized, so we need to forget it so Drop isn't
|
|
|
|
// run.
|
|
|
|
mem::forget(params);
|
2017-12-02 18:37:17 -05:00
|
|
|
|
2017-12-03 20:10:45 -05:00
|
|
|
ffi::lua_remove(state, stack_start + 1);
|
2017-12-02 18:37:17 -05:00
|
|
|
|
2017-12-03 20:10:45 -05:00
|
|
|
if ret == ffi::LUA_OK {
|
Simplify handling of userdata __gc and resurrected userdata.
Now, simply remove the userdata table immediately before dropping the userdata.
This does two things, it prevents __gc from double dropping the userdata, and
after the first call to __gc, it prevents the userdata from being identified as
any particular userdata type, so it cannot be misused after being finalized.
This change thus removes the userdata invalidation error, and simplifies a lot
of userdata handling code.
It also fixes a panic bug. Because there is no predictable order for
finalizers, it is possible to run a userdata finalizer that does not resurrect
itself before a lua table finalizer that accesses that userdata, and this means
that there were several asserts that were possible to trigger in normal Lua code
in util.rs related to `WrappedError`.
Now, finalized userdata is simply a userdata with no methods, so any use of
finalized userdata becomes a normal script runtime error (though, with a
potentially confusing error message). As a future improvement, we could set
a metatable on finalized userdata that provides a better error message.
2018-01-27 18:12:39 -05:00
|
|
|
Ok(result)
|
2017-12-03 20:10:45 -05:00
|
|
|
} else {
|
|
|
|
Err(pop_error(state, ret))
|
2017-12-02 18:37:17 -05:00
|
|
|
}
|
2017-12-03 20:10:45 -05:00
|
|
|
}
|
2017-12-02 18:37:17 -05:00
|
|
|
|
2017-12-03 18:15:31 -05:00
|
|
|
// Pops an error off of the stack and returns it. If the error is actually a WrappedPanic, clears
|
|
|
|
// the current lua stack and continues the panic. If the error on the top of the stack is actually
|
|
|
|
// a WrappedError, just returns it. Otherwise, interprets the error as the appropriate lua error.
|
2018-02-12 13:42:13 -05:00
|
|
|
// Uses 2 stack spaces, does not call lua_checkstack.
|
2017-12-03 18:15:31 -05:00
|
|
|
pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
2018-02-07 17:05:00 -05:00
|
|
|
lua_internal_assert!(
|
2017-12-03 18:15:31 -05:00
|
|
|
state,
|
|
|
|
err_code != ffi::LUA_OK && err_code != ffi::LUA_YIELD,
|
|
|
|
"pop_error called with non-error return code"
|
|
|
|
);
|
|
|
|
|
|
|
|
if let Some(err) = pop_wrapped_error(state) {
|
|
|
|
err
|
2017-10-24 16:15:57 -04:00
|
|
|
} else if is_wrapped_panic(state, -1) {
|
Simplify handling of userdata __gc and resurrected userdata.
Now, simply remove the userdata table immediately before dropping the userdata.
This does two things, it prevents __gc from double dropping the userdata, and
after the first call to __gc, it prevents the userdata from being identified as
any particular userdata type, so it cannot be misused after being finalized.
This change thus removes the userdata invalidation error, and simplifies a lot
of userdata handling code.
It also fixes a panic bug. Because there is no predictable order for
finalizers, it is possible to run a userdata finalizer that does not resurrect
itself before a lua table finalizer that accesses that userdata, and this means
that there were several asserts that were possible to trigger in normal Lua code
in util.rs related to `WrappedError`.
Now, finalized userdata is simply a userdata with no methods, so any use of
finalized userdata becomes a normal script runtime error (though, with a
potentially confusing error message). As a future improvement, we could set
a metatable on finalized userdata that provides a better error message.
2018-01-27 18:12:39 -05:00
|
|
|
let panic = get_userdata::<WrappedPanic>(state, -1);
|
2017-10-24 16:15:57 -04:00
|
|
|
if let Some(p) = (*panic).0.take() {
|
|
|
|
ffi::lua_settop(state, 0);
|
|
|
|
resume_unwind(p);
|
|
|
|
} else {
|
2018-02-07 17:05:00 -05:00
|
|
|
lua_internal_panic!(state, "panic was resumed twice")
|
2017-10-24 16:15:57 -04:00
|
|
|
}
|
2017-06-24 20:57:04 -04:00
|
|
|
} else {
|
2017-12-03 23:01:03 -05:00
|
|
|
let err_string = gc_guard(state, || {
|
|
|
|
if let Some(s) = ffi::lua_tostring(state, -1).as_ref() {
|
|
|
|
CStr::from_ptr(s).to_string_lossy().into_owned()
|
|
|
|
} else {
|
|
|
|
"<unprintable error>".to_owned()
|
|
|
|
}
|
|
|
|
});
|
|
|
|
ffi::lua_pop(state, 1);
|
2017-10-24 16:15:57 -04:00
|
|
|
|
2017-12-03 18:15:31 -05:00
|
|
|
match err_code {
|
2017-10-24 16:15:57 -04:00
|
|
|
ffi::LUA_ERRRUN => Error::RuntimeError(err_string),
|
|
|
|
ffi::LUA_ERRSYNTAX => {
|
|
|
|
Error::SyntaxError {
|
|
|
|
// This seems terrible, but as far as I can tell, this is exactly what the
|
|
|
|
// stock Lua REPL does.
|
|
|
|
incomplete_input: err_string.ends_with("<eof>"),
|
|
|
|
message: err_string,
|
2017-06-24 22:26:35 -04:00
|
|
|
}
|
2017-10-24 16:15:57 -04:00
|
|
|
}
|
|
|
|
ffi::LUA_ERRERR => {
|
|
|
|
// The Lua manual documents this error wrongly: It is not raised when a message
|
|
|
|
// handler errors, but rather when some specific situations regarding stack
|
|
|
|
// overflow handling occurs. Since it is not very useful do differentiate
|
|
|
|
// between that and "ordinary" runtime errors, we handle them the same way.
|
|
|
|
Error::RuntimeError(err_string)
|
|
|
|
}
|
|
|
|
ffi::LUA_ERRMEM => {
|
|
|
|
// This should be impossible, as we set the lua allocator to one that aborts
|
|
|
|
// instead of failing.
|
2018-02-10 17:49:54 -05:00
|
|
|
lua_internal_abort!("impossible Lua allocation error, aborting!")
|
2017-10-24 16:15:57 -04:00
|
|
|
}
|
2017-12-04 00:57:39 -05:00
|
|
|
ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string),
|
2018-02-07 17:05:00 -05:00
|
|
|
_ => lua_internal_panic!(state, "unrecognized lua error code"),
|
2017-12-03 18:15:31 -05:00
|
|
|
}
|
2017-06-05 00:41:48 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-16 17:44:13 -05:00
|
|
|
// Internally uses 4 stack spaces, does not call checkstack
|
2017-12-04 00:15:20 -05:00
|
|
|
pub unsafe fn push_string(state: *mut ffi::lua_State, s: &str) -> Result<()> {
|
|
|
|
protect_lua_call(state, 0, 1, |state| {
|
|
|
|
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
|
|
|
|
})
|
2017-05-21 19:50:59 -04:00
|
|
|
}
|
|
|
|
|
2017-12-16 17:44:13 -05:00
|
|
|
// Internally uses 4 stack spaces, does not call checkstack
|
2017-12-04 00:15:20 -05:00
|
|
|
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
|
Simplify handling of userdata __gc and resurrected userdata.
Now, simply remove the userdata table immediately before dropping the userdata.
This does two things, it prevents __gc from double dropping the userdata, and
after the first call to __gc, it prevents the userdata from being identified as
any particular userdata type, so it cannot be misused after being finalized.
This change thus removes the userdata invalidation error, and simplifies a lot
of userdata handling code.
It also fixes a panic bug. Because there is no predictable order for
finalizers, it is possible to run a userdata finalizer that does not resurrect
itself before a lua table finalizer that accesses that userdata, and this means
that there were several asserts that were possible to trigger in normal Lua code
in util.rs related to `WrappedError`.
Now, finalized userdata is simply a userdata with no methods, so any use of
finalized userdata becomes a normal script runtime error (though, with a
potentially confusing error message). As a future improvement, we could set
a metatable on finalized userdata that provides a better error message.
2018-01-27 18:12:39 -05:00
|
|
|
protect_lua_call(state, 0, 1, move |state| {
|
|
|
|
let ud = ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T;
|
|
|
|
ptr::write(ud, t);
|
2017-12-04 00:15:20 -05:00
|
|
|
})
|
2017-07-23 01:00:33 -04:00
|
|
|
}
|
|
|
|
|
Simplify handling of userdata __gc and resurrected userdata.
Now, simply remove the userdata table immediately before dropping the userdata.
This does two things, it prevents __gc from double dropping the userdata, and
after the first call to __gc, it prevents the userdata from being identified as
any particular userdata type, so it cannot be misused after being finalized.
This change thus removes the userdata invalidation error, and simplifies a lot
of userdata handling code.
It also fixes a panic bug. Because there is no predictable order for
finalizers, it is possible to run a userdata finalizer that does not resurrect
itself before a lua table finalizer that accesses that userdata, and this means
that there were several asserts that were possible to trigger in normal Lua code
in util.rs related to `WrappedError`.
Now, finalized userdata is simply a userdata with no methods, so any use of
finalized userdata becomes a normal script runtime error (though, with a
potentially confusing error message). As a future improvement, we could set
a metatable on finalized userdata that provides a better error message.
2018-01-27 18:12:39 -05:00
|
|
|
pub unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
|
|
|
|
let ud = ffi::lua_touserdata(state, index) as *mut T;
|
2018-02-07 17:05:00 -05:00
|
|
|
lua_internal_assert!(state, !ud.is_null(), "userdata pointer is null");
|
Simplify handling of userdata __gc and resurrected userdata.
Now, simply remove the userdata table immediately before dropping the userdata.
This does two things, it prevents __gc from double dropping the userdata, and
after the first call to __gc, it prevents the userdata from being identified as
any particular userdata type, so it cannot be misused after being finalized.
This change thus removes the userdata invalidation error, and simplifies a lot
of userdata handling code.
It also fixes a panic bug. Because there is no predictable order for
finalizers, it is possible to run a userdata finalizer that does not resurrect
itself before a lua table finalizer that accesses that userdata, and this means
that there were several asserts that were possible to trigger in normal Lua code
in util.rs related to `WrappedError`.
Now, finalized userdata is simply a userdata with no methods, so any use of
finalized userdata becomes a normal script runtime error (though, with a
potentially confusing error message). As a future improvement, we could set
a metatable on finalized userdata that provides a better error message.
2018-01-27 18:12:39 -05:00
|
|
|
ud
|
2017-07-23 01:00:33 -04:00
|
|
|
}
|
|
|
|
|
2018-02-07 16:42:03 -05:00
|
|
|
// Pops the userdata off of the top of the stack and returns it to rust, invalidating the lua
|
|
|
|
// userdata.
|
|
|
|
pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
|
2018-02-06 20:23:16 -05:00
|
|
|
// We set the metatable of userdata on __gc to a special table with no __gc method and with
|
|
|
|
// metamethods that trigger an error on access. We do this so that it will not be double
|
|
|
|
// dropped, and also so that it cannot be used or identified as any particular userdata type
|
|
|
|
// after the first call to __gc.
|
|
|
|
get_destructed_userdata_metatable(state);
|
|
|
|
ffi::lua_setmetatable(state, -2);
|
2018-02-09 21:21:29 -05:00
|
|
|
let ud = ffi::lua_touserdata(state, -1) as *mut T;
|
|
|
|
lua_internal_assert!(state, !ud.is_null(), "userdata pointer is null");
|
2018-02-06 20:23:16 -05:00
|
|
|
ffi::lua_pop(state, 1);
|
2018-02-09 21:21:29 -05:00
|
|
|
ptr::read(ud)
|
2018-02-07 16:42:03 -05:00
|
|
|
}
|
|
|
|
|
2018-02-15 21:39:35 -05:00
|
|
|
#[cfg_attr(feature = "unwind", unwind)]
|
2018-02-07 16:42:03 -05:00
|
|
|
pub unsafe extern "C" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
|
|
|
|
callback_error(state, || {
|
|
|
|
take_userdata::<T>(state);
|
|
|
|
Ok(0)
|
|
|
|
})
|
2018-02-06 20:23:16 -05:00
|
|
|
}
|
|
|
|
|
2017-06-24 22:26:35 -04:00
|
|
|
// In the context of a lua callback, this will call the given function and if the given function
|
|
|
|
// returns an error, *or if the given function panics*, this will result in a call to lua_error (a
|
2017-12-03 18:15:31 -05:00
|
|
|
// longjmp). The error or panic is wrapped in such a way that when calling pop_error back on
|
2017-06-24 22:26:35 -04:00
|
|
|
// the rust side, it will resume the panic.
|
2017-05-21 19:50:59 -04:00
|
|
|
pub unsafe fn callback_error<R, F>(state: *mut ffi::lua_State, f: F) -> R
|
2017-06-15 10:26:39 -04:00
|
|
|
where
|
2017-07-18 16:21:12 -04:00
|
|
|
F: FnOnce() -> Result<R> + UnwindSafe,
|
2017-05-21 19:50:59 -04:00
|
|
|
{
|
|
|
|
match catch_unwind(f) {
|
|
|
|
Ok(Ok(r)) => r,
|
|
|
|
Ok(Err(err)) => {
|
2018-02-09 23:35:29 -05:00
|
|
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
2017-06-24 20:57:04 -04:00
|
|
|
push_wrapped_error(state, err);
|
2017-05-21 19:50:59 -04:00
|
|
|
ffi::lua_error(state)
|
|
|
|
}
|
|
|
|
Err(p) => {
|
2018-02-09 23:35:29 -05:00
|
|
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
2017-06-24 20:57:04 -04:00
|
|
|
push_wrapped_panic(state, p);
|
2017-05-21 19:50:59 -04:00
|
|
|
ffi::lua_error(state)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-02 19:16:57 -05:00
|
|
|
// Takes an error at the top of the stack, and if it is a WrappedError, converts it to an
|
2017-12-03 18:15:31 -05:00
|
|
|
// 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 not modify it.
|
2018-02-15 21:39:35 -05:00
|
|
|
#[cfg_attr(feature = "unwind", unwind)]
|
2017-12-02 19:16:57 -05:00
|
|
|
pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
2018-02-09 23:35:29 -05:00
|
|
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
|
|
|
|
2017-12-02 19:16:57 -05:00
|
|
|
if let Some(error) = pop_wrapped_error(state) {
|
|
|
|
ffi::luaL_traceback(state, state, ptr::null(), 0);
|
2017-12-03 21:19:32 -05:00
|
|
|
let traceback = CStr::from_ptr(ffi::lua_tostring(state, -1))
|
2017-12-02 19:16:57 -05:00
|
|
|
.to_string_lossy()
|
|
|
|
.into_owned();
|
|
|
|
push_wrapped_error(
|
|
|
|
state,
|
|
|
|
Error::CallbackError {
|
|
|
|
traceback,
|
|
|
|
cause: Arc::new(error),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
ffi::lua_remove(state, -2);
|
|
|
|
} else if !is_wrapped_panic(state, 1) {
|
2017-12-03 21:19:32 -05:00
|
|
|
let s = ffi::lua_tostring(state, 1);
|
2017-12-02 19:16:57 -05:00
|
|
|
let s = if s.is_null() {
|
|
|
|
cstr!("<unprintable Rust panic>")
|
|
|
|
} else {
|
|
|
|
s
|
|
|
|
};
|
|
|
|
ffi::luaL_traceback(state, state, s, 0);
|
|
|
|
ffi::lua_remove(state, -2);
|
|
|
|
}
|
|
|
|
1
|
|
|
|
}
|
|
|
|
|
2017-05-21 19:50:59 -04:00
|
|
|
// A variant of pcall that does not allow lua to catch panic errors from callback_error
|
2018-02-15 21:39:35 -05:00
|
|
|
#[cfg_attr(feature = "unwind", unwind)]
|
2017-05-21 19:50:59 -04:00
|
|
|
pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
|
2018-02-12 13:42:13 -05:00
|
|
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
|
|
|
|
2017-08-02 10:42:18 -04:00
|
|
|
let top = ffi::lua_gettop(state);
|
|
|
|
if top == 0 {
|
2017-12-03 23:01:03 -05:00
|
|
|
ffi::lua_pushstring(state, cstr!("not enough arguments to pcall"));
|
2017-08-02 10:42:18 -04:00
|
|
|
ffi::lua_error(state);
|
|
|
|
} else if ffi::lua_pcall(state, top - 1, ffi::LUA_MULTRET, 0) != ffi::LUA_OK {
|
2017-06-24 22:26:35 -04:00
|
|
|
if is_wrapped_panic(state, -1) {
|
2017-05-21 19:50:59 -04:00
|
|
|
ffi::lua_error(state);
|
|
|
|
}
|
2017-06-25 02:40:09 -04:00
|
|
|
ffi::lua_pushboolean(state, 0);
|
|
|
|
ffi::lua_insert(state, -2);
|
|
|
|
2
|
|
|
|
} else {
|
|
|
|
ffi::lua_pushboolean(state, 1);
|
|
|
|
ffi::lua_insert(state, 1);
|
|
|
|
ffi::lua_gettop(state)
|
2017-05-21 19:50:59 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A variant of xpcall that does not allow lua to catch panic errors from callback_error
|
2018-02-15 21:39:35 -05:00
|
|
|
#[cfg_attr(feature = "unwind", unwind)]
|
2017-05-21 19:50:59 -04:00
|
|
|
pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
|
2018-02-15 21:39:35 -05:00
|
|
|
#[cfg_attr(feature = "unwind", unwind)]
|
2017-05-21 19:50:59 -04:00
|
|
|
unsafe extern "C" fn xpcall_msgh(state: *mut ffi::lua_State) -> c_int {
|
2018-02-12 13:42:13 -05:00
|
|
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
|
|
|
|
2017-06-24 22:26:35 -04:00
|
|
|
if is_wrapped_panic(state, -1) {
|
2017-05-21 19:50:59 -04:00
|
|
|
1
|
|
|
|
} else {
|
|
|
|
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
|
|
|
|
ffi::lua_insert(state, 1);
|
|
|
|
ffi::lua_call(state, ffi::lua_gettop(state) - 1, ffi::LUA_MULTRET);
|
|
|
|
ffi::lua_gettop(state)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-12 13:42:13 -05:00
|
|
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
|
|
|
|
2017-08-02 10:42:18 -04:00
|
|
|
let top = ffi::lua_gettop(state);
|
|
|
|
if top < 2 {
|
2017-12-03 23:01:03 -05:00
|
|
|
ffi::lua_pushstring(state, cstr!("not enough arguments to xpcall"));
|
2017-08-02 10:42:18 -04:00
|
|
|
ffi::lua_error(state);
|
|
|
|
}
|
|
|
|
|
2017-05-21 19:50:59 -04:00
|
|
|
ffi::lua_pushvalue(state, 2);
|
|
|
|
ffi::lua_pushcclosure(state, xpcall_msgh, 1);
|
|
|
|
ffi::lua_copy(state, 1, 2);
|
2017-08-02 10:42:18 -04:00
|
|
|
ffi::lua_replace(state, 1);
|
2017-05-21 19:50:59 -04:00
|
|
|
|
|
|
|
let res = ffi::lua_pcall(state, ffi::lua_gettop(state) - 2, ffi::LUA_MULTRET, 1);
|
|
|
|
if res != ffi::LUA_OK {
|
2017-06-24 22:26:35 -04:00
|
|
|
if is_wrapped_panic(state, -1) {
|
2017-05-21 19:50:59 -04:00
|
|
|
ffi::lua_error(state);
|
|
|
|
}
|
2017-06-25 02:40:09 -04:00
|
|
|
ffi::lua_pushboolean(state, 0);
|
|
|
|
ffi::lua_insert(state, -2);
|
|
|
|
2
|
|
|
|
} else {
|
|
|
|
ffi::lua_pushboolean(state, 1);
|
2017-10-25 14:51:10 -04:00
|
|
|
ffi::lua_insert(state, 2);
|
|
|
|
ffi::lua_gettop(state) - 1
|
2017-05-21 19:50:59 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-12 13:56:23 -05:00
|
|
|
// Does not call lua_checkstack, uses 1 stack space.
|
2017-06-05 05:03:18 -04:00
|
|
|
pub unsafe fn main_state(state: *mut ffi::lua_State) -> *mut ffi::lua_State {
|
|
|
|
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_MAINTHREAD);
|
2017-07-27 16:47:06 -04:00
|
|
|
let main_state = ffi::lua_tothread(state, -1);
|
2017-06-05 05:03:18 -04:00
|
|
|
ffi::lua_pop(state, 1);
|
2017-07-27 16:47:06 -04:00
|
|
|
main_state
|
2017-06-05 05:03:18 -04:00
|
|
|
}
|
|
|
|
|
2018-02-09 23:35:29 -05:00
|
|
|
// Pushes a WrappedError::Error to the top of the stack. Uses two stack spaces and does not call
|
|
|
|
// lua_checkstack.
|
2017-07-18 16:21:12 -04:00
|
|
|
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) {
|
2017-12-03 23:01:03 -05:00
|
|
|
gc_guard(state, || {
|
Simplify handling of userdata __gc and resurrected userdata.
Now, simply remove the userdata table immediately before dropping the userdata.
This does two things, it prevents __gc from double dropping the userdata, and
after the first call to __gc, it prevents the userdata from being identified as
any particular userdata type, so it cannot be misused after being finalized.
This change thus removes the userdata invalidation error, and simplifies a lot
of userdata handling code.
It also fixes a panic bug. Because there is no predictable order for
finalizers, it is possible to run a userdata finalizer that does not resurrect
itself before a lua table finalizer that accesses that userdata, and this means
that there were several asserts that were possible to trigger in normal Lua code
in util.rs related to `WrappedError`.
Now, finalized userdata is simply a userdata with no methods, so any use of
finalized userdata becomes a normal script runtime error (though, with a
potentially confusing error message). As a future improvement, we could set
a metatable on finalized userdata that provides a better error message.
2018-01-27 18:12:39 -05:00
|
|
|
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError;
|
|
|
|
ptr::write(ud, WrappedError(err))
|
2017-12-03 23:01:03 -05:00
|
|
|
});
|
|
|
|
|
2017-05-21 19:50:59 -04:00
|
|
|
get_error_metatable(state);
|
|
|
|
ffi::lua_setmetatable(state, -2);
|
|
|
|
}
|
|
|
|
|
2018-02-12 13:42:13 -05:00
|
|
|
// Pops a WrappedError off of the top of the stack, if it is a WrappedError. If it is not a
|
|
|
|
// WrappedError, returns None and does not pop anything. Uses 2 stack spaces and does not call
|
2018-02-12 13:56:23 -05:00
|
|
|
// lua_checkstack.
|
2017-07-18 16:21:12 -04:00
|
|
|
pub unsafe fn pop_wrapped_error(state: *mut ffi::lua_State) -> Option<Error> {
|
2018-02-12 13:42:13 -05:00
|
|
|
if !is_wrapped_error(state, -1) {
|
2017-06-24 22:26:35 -04:00
|
|
|
None
|
|
|
|
} else {
|
Simplify handling of userdata __gc and resurrected userdata.
Now, simply remove the userdata table immediately before dropping the userdata.
This does two things, it prevents __gc from double dropping the userdata, and
after the first call to __gc, it prevents the userdata from being identified as
any particular userdata type, so it cannot be misused after being finalized.
This change thus removes the userdata invalidation error, and simplifies a lot
of userdata handling code.
It also fixes a panic bug. Because there is no predictable order for
finalizers, it is possible to run a userdata finalizer that does not resurrect
itself before a lua table finalizer that accesses that userdata, and this means
that there were several asserts that were possible to trigger in normal Lua code
in util.rs related to `WrappedError`.
Now, finalized userdata is simply a userdata with no methods, so any use of
finalized userdata becomes a normal script runtime error (though, with a
potentially confusing error message). As a future improvement, we could set
a metatable on finalized userdata that provides a better error message.
2018-01-27 18:12:39 -05:00
|
|
|
let err = &*get_userdata::<WrappedError>(state, -1);
|
2018-02-12 13:56:23 -05:00
|
|
|
// We are assuming here that Error::clone() cannot panic.
|
2017-06-25 02:40:09 -04:00
|
|
|
let err = err.0.clone();
|
|
|
|
ffi::lua_pop(state, 1);
|
|
|
|
Some(err)
|
2017-06-24 22:26:35 -04:00
|
|
|
}
|
2017-06-24 21:44:27 -04:00
|
|
|
}
|
|
|
|
|
2017-12-17 00:19:59 -05:00
|
|
|
// Runs the given function with the Lua garbage collector disabled. `rlua` assumes that all
|
|
|
|
// allocation failures are aborts, so when the garbage collector is disabled, 'm' functions that can
|
|
|
|
// cause either an allocation error or a a `__gc` metamethod error are prevented from causing errors
|
|
|
|
// at all. The given function should never panic or longjmp, because this could inadverntently
|
|
|
|
// disable the gc. This is useful when error handling must allocate, and `__gc` errors at that time
|
|
|
|
// would shadow more important errors, or be extremely difficult to handle safely.
|
|
|
|
pub unsafe fn gc_guard<R, F: FnOnce() -> R>(state: *mut ffi::lua_State, f: F) -> R {
|
|
|
|
if ffi::lua_gc(state, ffi::LUA_GCISRUNNING, 0) != 0 {
|
|
|
|
ffi::lua_gc(state, ffi::LUA_GCSTOP, 0);
|
|
|
|
let r = f();
|
|
|
|
ffi::lua_gc(state, ffi::LUA_GCRESTART, 0);
|
|
|
|
r
|
|
|
|
} else {
|
|
|
|
f()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-02 19:16:57 -05:00
|
|
|
struct WrappedError(pub Error);
|
|
|
|
struct WrappedPanic(pub Option<Box<Any + Send>>);
|
|
|
|
|
2018-02-09 23:35:29 -05:00
|
|
|
// Pushes a WrappedError::Panic to the top of the stack. Uses two stack spaces and does not call
|
|
|
|
// lua_checkstack.
|
2017-12-02 19:16:57 -05:00
|
|
|
unsafe fn push_wrapped_panic(state: *mut ffi::lua_State, panic: Box<Any + Send>) {
|
2017-12-03 23:01:03 -05:00
|
|
|
gc_guard(state, || {
|
Simplify handling of userdata __gc and resurrected userdata.
Now, simply remove the userdata table immediately before dropping the userdata.
This does two things, it prevents __gc from double dropping the userdata, and
after the first call to __gc, it prevents the userdata from being identified as
any particular userdata type, so it cannot be misused after being finalized.
This change thus removes the userdata invalidation error, and simplifies a lot
of userdata handling code.
It also fixes a panic bug. Because there is no predictable order for
finalizers, it is possible to run a userdata finalizer that does not resurrect
itself before a lua table finalizer that accesses that userdata, and this means
that there were several asserts that were possible to trigger in normal Lua code
in util.rs related to `WrappedError`.
Now, finalized userdata is simply a userdata with no methods, so any use of
finalized userdata becomes a normal script runtime error (though, with a
potentially confusing error message). As a future improvement, we could set
a metatable on finalized userdata that provides a better error message.
2018-01-27 18:12:39 -05:00
|
|
|
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedPanic>()) as *mut WrappedPanic;
|
|
|
|
ptr::write(ud, WrappedPanic(Some(panic)))
|
2017-12-03 23:01:03 -05:00
|
|
|
});
|
2017-12-02 19:16:57 -05:00
|
|
|
|
|
|
|
get_panic_metatable(state);
|
|
|
|
ffi::lua_setmetatable(state, -2);
|
|
|
|
}
|
|
|
|
|
2018-02-12 13:42:13 -05:00
|
|
|
// Checks if the value at the given index is a WrappedError, uses 2 stack spaces and does not call
|
|
|
|
// lua_checkstack.
|
2017-12-02 17:47:00 -05:00
|
|
|
unsafe fn is_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> bool {
|
2017-05-21 19:50:59 -04:00
|
|
|
let userdata = ffi::lua_touserdata(state, index);
|
|
|
|
if userdata.is_null() {
|
2017-06-24 22:26:35 -04:00
|
|
|
return false;
|
2017-05-21 19:50:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if ffi::lua_getmetatable(state, index) == 0 {
|
2017-06-24 22:26:35 -04:00
|
|
|
return false;
|
2017-05-21 19:50:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
get_error_metatable(state);
|
2017-06-24 22:26:35 -04:00
|
|
|
let res = ffi::lua_rawequal(state, -1, -2) != 0;
|
2017-05-21 19:50:59 -04:00
|
|
|
ffi::lua_pop(state, 2);
|
2017-06-24 22:26:35 -04:00
|
|
|
res
|
|
|
|
}
|
|
|
|
|
2018-02-12 13:42:13 -05:00
|
|
|
// Checks if the value at the given index is a WrappedPanic. Uses 2 stack spaces and does not call
|
|
|
|
// lua_checkstack.
|
2017-12-02 17:47:00 -05:00
|
|
|
unsafe fn is_wrapped_panic(state: *mut ffi::lua_State, index: c_int) -> bool {
|
2017-06-24 22:26:35 -04:00
|
|
|
let userdata = ffi::lua_touserdata(state, index);
|
|
|
|
if userdata.is_null() {
|
|
|
|
return false;
|
|
|
|
}
|
Big API incompatible error change, remove dependency on error_chain
The current situation with error_chain is less than ideal, and there are lots of
conflicting interests that are impossible to meet at once. Here is an
unorganized brain dump of the current situation, stay awhile and listen!
This change was triggered ultimately by the desire to make LuaError implement
Clone, and this is currently impossible with error_chain. LuaError must
implement Clone to be a proper lua citizen that can live as userdata within a
lua runtime, because there is no way to limit what the lua runtime can do with a
received error. Currently, this is solved by there being a rule that the error
will "expire" if the error is passed back into rust, and this is very
sub-optimal. In fact, one could easily imagine a scenario where lua is for
example memoizing some function, and if the function has ever errored in the
past the function should continue returning the same error, and this situation
immediately fails with this restriciton in place.
Additionally, there are other more minor problems with error_chain which make
the API less good than it could be, or limit how we can use error_chain. This
change has already solved a small bug in a Chucklefish project, where the
conversion from an external error type (Borrow[Mut]Error) was allowed but not
intended for user code, and was accidentally used. Additionally, pattern
matching on error_chain errors, which should be common when dealing with Lua, is
less convenient than a hand rolled error type.
So, if we decide not to use error_chain, we now have a new set of problems if we
decide interoperability with error_chain is important. The first problem we run
into is that there are two natural bounds for wrapped errors that we would
pick, (Error + Send + Sync), or just Error, and neither of them will
interoperate well with error_chain. (Error + Send + Sync) means we can't wrap
error chain errors into LuaError::ExternalError (they're missing the Sync
bound), and having the bounds be just Error means the opposite, that we can't
hold a LuaError inside an error_chain error.
We could just decide that interoperability with error_chain is the most
important qualification, and pick (Error + Send), but this causes a DIFFERENT
set of problems. The rust ecosystem has the two primary error bounds as Error
or (Error + Send + Sync), and there are Into impls from &str / String to
Box<Error + Send + Sync> for example, but NOT (Error + Send). This means that
we are forced to manually recreate the conversions from &str / String to
LuaError rather than relying on a single Into<Box<Error + Send + Sync>> bound,
but this means that string conversions have a different set of methods than
other error types for external error conversion. I have not been able to figure
out an API that I am happy with that uses the (Error + Send) bound. Box<Error>
is obnoxious because not having errors implement Send causes needless problems
in a multithreaded context, so that leaves (Error + Send + Sync). This is
actually a completely reasonable bound for external errors, and has the nice
String Into impls that we would want, the ONLY problem is that it is a pain to
interoperate with the current version of error_chain.
It would be nice to be able to specify the traits that an error generated by the
error_chain macro would implement, and this is apparently in progress in the
error_chain library. This would solve both the problem with not being able to
implement Clone and the problems with (Error + Send) bounds. I am not convinced
that this library should go back to using error_chain when that functionality is
in stable error_chain though, because of the other minor usability problems with
using error_chain.
In that theoretical situation, the downside of NOT using error_chain is simply
that there would not be automatic stacktraces of LuaError. This is not a huge
problem, because stack traces of lua errors are not extremely useful, and for
external errors it is not too hard to create a different version of the
LuaExternalResult / LuaExternalError traits and do conversion from an
error_chain type into a type that will print the stacktrace on display, or
use downcasting in the error causes.
So in summary, this library is no longer using error_chain, and probably will
not use it again in the future. Currently this means that to interoperate with
error_chain, you should use error_chain 0.8.1, which derives Sync on errors, or
wait for a version that supports user defined trait derives. In the future
when error_chain supports user defined trait derives, users may have to take an
extra step to make wrapped external errors print the stacktrace that they
capture.
This change works, but is not entirely complete. There is no error
documentation yet, and the change brought to a head an ugly module organization
problem. There will be more commits for documentation and reorganization, then
a new stable version of rlua.
2017-06-24 18:11:56 -04:00
|
|
|
|
2017-06-24 22:26:35 -04:00
|
|
|
if ffi::lua_getmetatable(state, index) == 0 {
|
|
|
|
return false;
|
2017-05-21 19:50:59 -04:00
|
|
|
}
|
2017-06-24 22:26:35 -04:00
|
|
|
|
|
|
|
get_panic_metatable(state);
|
|
|
|
let res = ffi::lua_rawequal(state, -1, -2) != 0;
|
|
|
|
ffi::lua_pop(state, 2);
|
2017-10-24 16:15:57 -04:00
|
|
|
res
|
2017-05-21 19:50:59 -04:00
|
|
|
}
|
|
|
|
|
2017-12-02 17:47:00 -05:00
|
|
|
unsafe fn get_error_metatable(state: *mut ffi::lua_State) -> c_int {
|
2017-12-03 21:19:32 -05:00
|
|
|
static ERROR_METATABLE_REGISTRY_KEY: u8 = 0;
|
|
|
|
|
2018-02-15 21:39:35 -05:00
|
|
|
#[cfg_attr(feature = "unwind", unwind)]
|
2017-12-02 17:13:46 -05:00
|
|
|
unsafe extern "C" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
|
2018-02-12 13:42:13 -05:00
|
|
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
|
|
|
|
2017-12-02 18:37:17 -05:00
|
|
|
callback_error(state, || {
|
|
|
|
if is_wrapped_error(state, -1) {
|
Simplify handling of userdata __gc and resurrected userdata.
Now, simply remove the userdata table immediately before dropping the userdata.
This does two things, it prevents __gc from double dropping the userdata, and
after the first call to __gc, it prevents the userdata from being identified as
any particular userdata type, so it cannot be misused after being finalized.
This change thus removes the userdata invalidation error, and simplifies a lot
of userdata handling code.
It also fixes a panic bug. Because there is no predictable order for
finalizers, it is possible to run a userdata finalizer that does not resurrect
itself before a lua table finalizer that accesses that userdata, and this means
that there were several asserts that were possible to trigger in normal Lua code
in util.rs related to `WrappedError`.
Now, finalized userdata is simply a userdata with no methods, so any use of
finalized userdata becomes a normal script runtime error (though, with a
potentially confusing error message). As a future improvement, we could set
a metatable on finalized userdata that provides a better error message.
2018-01-27 18:12:39 -05:00
|
|
|
let error = get_userdata::<WrappedError>(state, -1);
|
2017-12-03 23:01:03 -05:00
|
|
|
let error_str = (*error).0.to_string();
|
|
|
|
gc_guard(state, || {
|
|
|
|
ffi::lua_pushlstring(
|
|
|
|
state,
|
|
|
|
error_str.as_ptr() as *const c_char,
|
|
|
|
error_str.len(),
|
|
|
|
)
|
|
|
|
});
|
2017-12-02 18:37:17 -05:00
|
|
|
ffi::lua_remove(state, -2);
|
2017-12-02 17:13:46 -05:00
|
|
|
|
2017-12-02 18:37:17 -05:00
|
|
|
Ok(1)
|
|
|
|
} else {
|
2017-12-04 02:46:57 -05:00
|
|
|
panic!("userdata mismatch in Error metamethod");
|
2017-12-02 18:37:17 -05:00
|
|
|
}
|
2017-12-02 17:13:46 -05:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-06-15 10:26:39 -04:00
|
|
|
ffi::lua_pushlightuserdata(
|
|
|
|
state,
|
|
|
|
&ERROR_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
|
|
|
);
|
2018-02-06 20:23:16 -05:00
|
|
|
let t = ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
|
2017-12-02 17:13:46 -05:00
|
|
|
|
|
|
|
if t != ffi::LUA_TTABLE {
|
|
|
|
ffi::lua_pop(state, 1);
|
|
|
|
|
|
|
|
ffi::luaL_checkstack(state, 8, ptr::null());
|
|
|
|
|
2017-12-03 23:01:03 -05:00
|
|
|
gc_guard(state, || {
|
|
|
|
ffi::lua_newtable(state);
|
|
|
|
ffi::lua_pushlightuserdata(
|
|
|
|
state,
|
|
|
|
&ERROR_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
|
|
|
);
|
|
|
|
ffi::lua_pushvalue(state, -2);
|
2017-12-02 17:13:46 -05:00
|
|
|
|
2017-12-03 23:01:03 -05:00
|
|
|
ffi::lua_pushstring(state, cstr!("__gc"));
|
|
|
|
ffi::lua_pushcfunction(state, userdata_destructor::<WrappedError>);
|
2017-12-04 00:15:20 -05:00
|
|
|
ffi::lua_rawset(state, -3);
|
2017-12-02 17:13:46 -05:00
|
|
|
|
2017-12-03 23:01:03 -05:00
|
|
|
ffi::lua_pushstring(state, cstr!("__tostring"));
|
|
|
|
ffi::lua_pushcfunction(state, error_tostring);
|
2017-12-04 00:15:20 -05:00
|
|
|
ffi::lua_rawset(state, -3);
|
2017-12-02 17:13:46 -05:00
|
|
|
|
2017-12-03 23:01:03 -05:00
|
|
|
ffi::lua_pushstring(state, cstr!("__metatable"));
|
|
|
|
ffi::lua_pushboolean(state, 0);
|
2017-12-04 00:15:20 -05:00
|
|
|
ffi::lua_rawset(state, -3);
|
2017-12-02 17:13:46 -05:00
|
|
|
|
2017-12-04 00:15:20 -05:00
|
|
|
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
2017-12-03 23:01:03 -05:00
|
|
|
})
|
2017-12-02 17:13:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
ffi::LUA_TTABLE
|
2017-05-21 19:50:59 -04:00
|
|
|
}
|
2017-06-24 22:26:35 -04:00
|
|
|
|
2017-12-02 17:47:00 -05:00
|
|
|
unsafe fn get_panic_metatable(state: *mut ffi::lua_State) -> c_int {
|
2017-12-03 21:19:32 -05:00
|
|
|
static PANIC_METATABLE_REGISTRY_KEY: u8 = 0;
|
|
|
|
|
2017-06-24 22:26:35 -04:00
|
|
|
ffi::lua_pushlightuserdata(
|
|
|
|
state,
|
|
|
|
&PANIC_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
|
|
|
);
|
2018-02-06 20:23:16 -05:00
|
|
|
let t = ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
|
2017-12-02 17:13:46 -05:00
|
|
|
|
|
|
|
if t != ffi::LUA_TTABLE {
|
|
|
|
ffi::lua_pop(state, 1);
|
|
|
|
|
|
|
|
ffi::luaL_checkstack(state, 8, ptr::null());
|
|
|
|
|
2017-12-03 23:01:03 -05:00
|
|
|
gc_guard(state, || {
|
|
|
|
ffi::lua_newtable(state);
|
|
|
|
ffi::lua_pushlightuserdata(
|
|
|
|
state,
|
|
|
|
&PANIC_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
|
|
|
);
|
|
|
|
ffi::lua_pushvalue(state, -2);
|
2017-12-02 17:13:46 -05:00
|
|
|
|
2017-12-03 23:25:03 -05:00
|
|
|
ffi::lua_pushstring(state, cstr!("__gc"));
|
2017-12-03 23:01:03 -05:00
|
|
|
ffi::lua_pushcfunction(state, userdata_destructor::<WrappedPanic>);
|
2017-12-04 00:15:20 -05:00
|
|
|
ffi::lua_rawset(state, -3);
|
2017-12-02 17:13:46 -05:00
|
|
|
|
2017-12-03 23:25:03 -05:00
|
|
|
ffi::lua_pushstring(state, cstr!("__metatable"));
|
2017-12-03 23:01:03 -05:00
|
|
|
ffi::lua_pushboolean(state, 0);
|
2017-12-04 00:15:20 -05:00
|
|
|
ffi::lua_rawset(state, -3);
|
2017-12-02 17:13:46 -05:00
|
|
|
|
2017-12-04 00:15:20 -05:00
|
|
|
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
2017-12-03 23:01:03 -05:00
|
|
|
});
|
2017-12-02 17:13:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
ffi::LUA_TTABLE
|
2017-06-24 22:26:35 -04:00
|
|
|
}
|
2018-02-05 14:39:01 -05:00
|
|
|
|
2018-02-06 20:23:16 -05:00
|
|
|
unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) -> c_int {
|
|
|
|
static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
|
2018-02-05 14:39:01 -05:00
|
|
|
|
2018-02-15 21:39:35 -05:00
|
|
|
#[cfg_attr(feature = "unwind", unwind)]
|
2018-02-06 20:23:16 -05:00
|
|
|
unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
|
2018-02-09 23:35:29 -05:00
|
|
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
2018-02-09 21:21:29 -05:00
|
|
|
push_wrapped_error(state, Error::CallbackDestructed);
|
2018-02-05 14:39:01 -05:00
|
|
|
ffi::lua_error(state)
|
|
|
|
}
|
|
|
|
|
2018-02-06 20:23:16 -05:00
|
|
|
ffi::lua_pushlightuserdata(
|
|
|
|
state,
|
|
|
|
&DESTRUCTED_USERDATA_METATABLE as *const u8 as *mut c_void,
|
|
|
|
);
|
|
|
|
let t = ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
|
2018-02-05 14:39:01 -05:00
|
|
|
|
|
|
|
if t != ffi::LUA_TTABLE {
|
|
|
|
ffi::lua_pop(state, 1);
|
|
|
|
|
|
|
|
ffi::luaL_checkstack(state, 8, ptr::null());
|
|
|
|
|
|
|
|
gc_guard(state, || {
|
|
|
|
ffi::lua_newtable(state);
|
2018-02-06 20:23:16 -05:00
|
|
|
ffi::lua_pushlightuserdata(
|
|
|
|
state,
|
|
|
|
&DESTRUCTED_USERDATA_METATABLE as *const u8 as *mut c_void,
|
|
|
|
);
|
2018-02-05 14:39:01 -05:00
|
|
|
ffi::lua_pushvalue(state, -2);
|
|
|
|
|
|
|
|
for &method in &[
|
|
|
|
cstr!("__add"),
|
|
|
|
cstr!("__sub"),
|
|
|
|
cstr!("__mul"),
|
|
|
|
cstr!("__div"),
|
|
|
|
cstr!("__mod"),
|
|
|
|
cstr!("__pow"),
|
|
|
|
cstr!("__unm"),
|
|
|
|
cstr!("__idiv"),
|
|
|
|
cstr!("__band"),
|
|
|
|
cstr!("__bor"),
|
|
|
|
cstr!("__bxor"),
|
|
|
|
cstr!("__bnot"),
|
|
|
|
cstr!("__shl"),
|
|
|
|
cstr!("__shr"),
|
|
|
|
cstr!("__concat"),
|
|
|
|
cstr!("__len"),
|
|
|
|
cstr!("__eq"),
|
|
|
|
cstr!("__lt"),
|
|
|
|
cstr!("__le"),
|
|
|
|
cstr!("__index"),
|
|
|
|
cstr!("__newindex"),
|
|
|
|
cstr!("__call"),
|
|
|
|
cstr!("__tostring"),
|
|
|
|
cstr!("__pairs"),
|
|
|
|
cstr!("__ipairs"),
|
|
|
|
] {
|
|
|
|
ffi::lua_pushstring(state, method);
|
2018-02-06 20:23:16 -05:00
|
|
|
ffi::lua_pushcfunction(state, destructed_error);
|
2018-02-05 14:39:01 -05:00
|
|
|
ffi::lua_rawset(state, -3);
|
|
|
|
}
|
|
|
|
|
|
|
|
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
ffi::LUA_TTABLE
|
|
|
|
}
|