unsafe fn callback_error_ext<F, R>(state: *mut ffi::lua_State, extra: *mut ExtraData, f: F) -> R
where
    F: FnOnce(c_int) -> Result<R>,
{
    if extra.is_null() {
        return callback_error(state, f);
    }
    let extra = &mut *extra;

    let nargs = ffi::lua_gettop(state);

    enum PreallocatedFailure {
        New(*mut WrappedFailure),
        Cached(i32),
    }

    // We cannot shadow Rust errors with Lua ones, so we need to obtain pre-allocated memory
    // to store a wrapped failure (error or panic) *before* we proceed.
    let prealloc_failure = match extra.wrapped_failures_cache.pop() {
        Some(index) => PreallocatedFailure::Cached(index),
        None => {
            // We need 2 extra stack spaces to store userdata and error/panic metatable.
            let extra_stack = if nargs < 2 { 2 - nargs } else { 1 };
            ffi::luaL_checkstack(
                state,
                extra_stack,
                cstr!("not enough stack space for callback error handling"),
            );
            let ud = WrappedFailure::new_userdata(state);
            ffi::lua_rotate(state, 1, 1);
            PreallocatedFailure::New(ud)
        }
    };

    let mut get_wrapped_failure = || match prealloc_failure {
        PreallocatedFailure::New(ud) => {
            ffi::lua_settop(state, 1);
            ud
        }
        PreallocatedFailure::Cached(index) => {
            ffi::lua_settop(state, 0);
            ffi::lua_pushvalue(extra.ref_thread, index);
            ffi::lua_xmove(extra.ref_thread, state, 1);
            ffi::lua_pushnil(extra.ref_thread);
            ffi::lua_replace(extra.ref_thread, index);
            extra.ref_free.push(index);
            ffi::lua_touserdata(state, -1) as *mut WrappedFailure
        }
    };

    match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
        Ok(Ok(r)) => {
            // Return unused WrappedFailure to the cache
            match prealloc_failure {
                PreallocatedFailure::New(_)
                    if extra.wrapped_failures_cache.len() < WRAPPED_FAILURES_CACHE_SIZE =>
                {
                    ffi::lua_rotate(state, 1, -1);
                    ffi::lua_xmove(state, extra.ref_thread, 1);
                    let index = ref_stack_pop(extra);
                    extra.wrapped_failures_cache.push(index);
                }
                PreallocatedFailure::New(_) => {
                    ffi::lua_remove(state, 1);
                }
                PreallocatedFailure::Cached(index)
                    if extra.wrapped_failures_cache.len() < WRAPPED_FAILURES_CACHE_SIZE =>
                {
                    extra.wrapped_failures_cache.push(index);
                }
                PreallocatedFailure::Cached(index) => {
                    ffi::lua_pushnil(extra.ref_thread);
                    ffi::lua_replace(extra.ref_thread, index);
                    extra.ref_free.push(index);
                }
            }
            r
        }
        Ok(Err(err)) => {
            let wrapped_error = get_wrapped_failure();

            // Build `CallbackError` with traceback
            let traceback = if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
                ffi::luaL_traceback(state, state, ptr::null(), 0);
                let traceback = util::to_string(state, -1);
                ffi::lua_pop(state, 1);
                traceback
            } else {
                "<not enough stack space for traceback>".to_string()
            };
            let cause = Arc::new(err);
            ptr::write(
                wrapped_error,
                WrappedFailure::Error(Error::CallbackError { traceback, cause }),
            );
            get_gc_metatable::<WrappedFailure>(state);
            ffi::lua_setmetatable(state, -2);

            ffi::lua_error(state)
        }
        Err(p) => {
            let wrapped_panic = get_wrapped_failure();
            ptr::write(wrapped_panic, WrappedFailure::Panic(Some(p)));
            get_gc_metatable::<WrappedFailure>(state);
            ffi::lua_setmetatable(state, -2);
            ffi::lua_error(state)
        }
    }
}
// An optimized version of `callback_error` that does not allocate `WrappedFailure` userdata
// and instead reuses unsed and cached values from previous calls (or allocates new).
An optimized version of `callback_error` that does not allocate `WrappedFailure` userdata
and instead reuses unsed and cached values from previous calls (or allocates new).
An optimized version of ` callback_error ` that does not allocate ` WrappedFailure ` userdata and instead reuses unsed and cached values from previous calls ( or allocates new ) .
unsafe fn callback_error_ext<F, R>(state: *mut ffi::lua_State, extra: *mut ExtraData, f: F) -> R
where
    F: FnOnce(c_int) -> Result<R>,
{
    if extra.is_null() {
        return callback_error(state, f);
    }
    let extra = &mut *extra;
    let nargs = ffi::lua_gettop(state);
    enum PreallocatedFailure {
        New(*mut WrappedFailure),
        Cached(i32),
    }
    // We cannot shadow Rust errors with Lua ones, so we need to obtain pre-allocated memory
    // to store a wrapped failure (error or panic) *before* we proceed.
    let prealloc_failure = match extra.wrapped_failures_cache.pop() {
        Some(index) => PreallocatedFailure::Cached(index),
        None => {
            // We need 2 extra stack spaces to store userdata and error/panic metatable.
            let extra_stack = if nargs < 2 { 2 - nargs } else { 1 };
            ffi::luaL_checkstack(
                state,
                extra_stack,
                cstr!("not enough stack space for callback error handling"),
            );
            let ud = WrappedFailure::new_userdata(state);
            ffi::lua_rotate(state, 1, 1);
            PreallocatedFailure::New(ud)
        }
    };
    let mut get_wrapped_failure = || match prealloc_failure {
        PreallocatedFailure::New(ud) => {
            ffi::lua_settop(state, 1);
            ud
        }
        PreallocatedFailure::Cached(index) => {
            ffi::lua_settop(state, 0);
            ffi::lua_pushvalue(extra.ref_thread, index);
            ffi::lua_xmove(extra.ref_thread, state, 1);
            ffi::lua_pushnil(extra.ref_thread);
            ffi::lua_replace(extra.ref_thread, index);
            extra.ref_free.push(index);
            ffi::lua_touserdata(state, -1) as *mut WrappedFailure
        }
    };
    match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
        Ok(Ok(r)) => {
            // Return unused WrappedFailure to the cache
            match prealloc_failure {
                PreallocatedFailure::New(_)
                    if extra.wrapped_failures_cache.len() < WRAPPED_FAILURES_CACHE_SIZE =>
                {
                    ffi::lua_rotate(state, 1, -1);
                    ffi::lua_xmove(state, extra.ref_thread, 1);
                    let index = ref_stack_pop(extra);
                    extra.wrapped_failures_cache.push(index);
                }
                PreallocatedFailure::New(_) => {
                    ffi::lua_remove(state, 1);
                }
                PreallocatedFailure::Cached(index)
                    if extra.wrapped_failures_cache.len() < WRAPPED_FAILURES_CACHE_SIZE =>
                {
                    extra.wrapped_failures_cache.push(index);
                }
                PreallocatedFailure::Cached(index) => {
                    ffi::lua_pushnil(extra.ref_thread);
                    ffi::lua_replace(extra.ref_thread, index);
                    extra.ref_free.push(index);
                }
            }
            r
        }
        Ok(Err(err)) => {
            let wrapped_error = get_wrapped_failure();
            // Build `CallbackError` with traceback
            let traceback = if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
                ffi::luaL_traceback(state, state, ptr::null(), 0);
                let traceback = util::to_string(state, -1);
                ffi::lua_pop(state, 1);
                traceback
            } else {
                "<not enough stack space for traceback>".to_string()
            };
            let cause = Arc::new(err);
            ptr::write(
                wrapped_error,
                WrappedFailure::Error(Error::CallbackError { traceback, cause }),
            );
            get_gc_metatable::<WrappedFailure>(state);
            ffi::lua_setmetatable(state, -2);
            ffi::lua_error(state)
        }
        Err(p) => {
            let wrapped_panic = get_wrapped_failure();
            ptr::write(wrapped_panic, WrappedFailure::Panic(Some(p)));
            get_gc_metatable::<WrappedFailure>(state);
            ffi::lua_setmetatable(state, -2);
            ffi::lua_error(state)
        }
    }
}
unsafe fn callback_error_ext < F , R > ( state : * mut ffi :: lua_State , extra : * mut ExtraData , f : F ) -> R where F : FnOnce ( c_int ) -> Result < R > , { if extra . is_null ( ) { return callback_error ( state , f ) ; } let extra = & mut * extra ; let nargs = ffi :: lua_gettop ( state ) ; enum PreallocatedFailure { New ( * mut WrappedFailure ) , Cached ( i32 ) , } // We cannot shadow Rust errors with Lua ones, so we need to obtain pre-allocated memory // to store a wrapped failure (error or panic) *before* we proceed. let prealloc_failure = match extra . wrapped_failures_cache . pop ( ) { Some ( index ) => PreallocatedFailure :: Cached ( index ) , None => { // We need 2 extra stack spaces to store userdata and error/panic metatable. let extra_stack = if nargs < 2 { 2 - nargs } else { 1 } ; ffi :: luaL_checkstack ( state , extra_stack , cstr ! ( " " ) , ) ; let ud = WrappedFailure :: new_userdata ( state ) ; ffi :: lua_rotate ( state , 1 , 1 ) ; PreallocatedFailure :: New ( ud ) } } ; let mut get_wrapped_failure = | | match prealloc_failure { PreallocatedFailure :: New ( ud ) => { ffi :: lua_settop ( state , 1 ) ; ud } PreallocatedFailure :: Cached ( index ) => { ffi :: lua_settop ( state , 0 ) ; ffi :: lua_pushvalue ( extra . ref_thread , index ) ; ffi :: lua_xmove ( extra . ref_thread , state , 1 ) ; ffi :: lua_pushnil ( extra . ref_thread ) ; ffi :: lua_replace ( extra . ref_thread , index ) ; extra . ref_free . push ( index ) ; ffi :: lua_touserdata ( state , - 1 ) as * mut WrappedFailure } } ; match catch_unwind ( AssertUnwindSafe ( | | f ( nargs ) ) ) { Ok ( Ok ( r ) ) => { // Return unused WrappedFailure to the cache match prealloc_failure { PreallocatedFailure :: New ( _ ) if extra . wrapped_failures_cache . len ( ) < WRAPPED_FAILURES_CACHE_SIZE => { ffi :: lua_rotate ( state , 1 , - 1 ) ; ffi :: lua_xmove ( state , extra . ref_thread , 1 ) ; let index = ref_stack_pop ( extra ) ; extra . wrapped_failures_cache . push ( index ) ; } PreallocatedFailure :: New ( _ ) => { ffi :: lua_remove ( state , 1 ) ; } PreallocatedFailure :: Cached ( index ) if extra . wrapped_failures_cache . len ( ) < WRAPPED_FAILURES_CACHE_SIZE => { extra . wrapped_failures_cache . push ( index ) ; } PreallocatedFailure :: Cached ( index ) => { ffi :: lua_pushnil ( extra . ref_thread ) ; ffi :: lua_replace ( extra . ref_thread , index ) ; extra . ref_free . push ( index ) ; } } r } Ok ( Err ( err ) ) => { let wrapped_error = get_wrapped_failure ( ) ; // Build `CallbackError` with traceback let traceback = if ffi :: lua_checkstack ( state , ffi :: LUA_TRACEBACK_STACK ) != 0 { ffi :: luaL_traceback ( state , state , ptr :: null ( ) , 0 ) ; let traceback = util :: to_string ( state , - 1 ) ; ffi :: lua_pop ( state , 1 ) ; traceback } else { " " . to_string ( ) } ; let cause = Arc :: new ( err ) ; ptr :: write ( wrapped_error , WrappedFailure :: Error ( Error :: CallbackError { traceback , cause } ) , ) ; get_gc_metatable :: < WrappedFailure > ( state ) ; ffi :: lua_setmetatable ( state , - 2 ) ; ffi :: lua_error ( state ) } Err ( p ) => { let wrapped_panic = get_wrapped_failure ( ) ; ptr :: write ( wrapped_panic , WrappedFailure :: Panic ( Some ( p ) ) ) ; get_gc_metatable :: < WrappedFailure > ( state ) ; ffi :: lua_setmetatable ( state , - 2 ) ; ffi :: lua_error ( state ) } } }
An optimized version of `callback_error` that does not allocate `WrappedFailure` userdata
and instead reuses unsed and cached values from previous calls (or allocates new).
An optimized version of ` callback_error ` that does not allocate ` WrappedFailure ` userdata and instead reuses unsed and cached values from previous calls ( or allocates new ) .
state
*mut ffi::lua_State
extra
*mut ExtraData
f
F
state
*mut ffi::lua_State
extra
*mut ExtraData
f
F