tomblind / local-lua-debugger-vscode

Local Lua Debugger for VSCode
MIT License
106 stars 26 forks source link

Local Lua Debugger doesn't catches errors in wrapped coroutines #29

Closed sewbacca closed 3 years ago

sewbacca commented 3 years ago

luacobug.gif

Exception has occurred.
....local-lua-debugger-vscode-0.2.0/debugger/lldebugger.lua:1395: /tmp/test/test.lua:3: Error
tomblind commented 3 years ago

Apologies for not responding to this sooner.

Coroutines work similar to pcall as they catch their own errors, so the debugger doesn't break on them by design. However it should not break inside the debugger code like this, but one level up. This is a bug.

Thinking about it, it would be good to have an option to break on errors in coroutines, since that's probably what most people would expect to happen. I'll look into adding that functionality.

tomblind commented 3 years ago

I've fixed the issue with breaking into the debugger code itself and added the ability to break directly into the coroutine when error() or assert() are used. Implicit errors will still break outside of the coroutine, though. Because coroutines shut down as soon as the error occurs, there doesn't seem to be a way to intercept before the call stack is gone. I'm keeping this open for now, though, to remind myself to explore further.

sewbacca commented 3 years ago

Great to see that this project is still maintained 👍 This idea is neither tested, nor really thought through, but couldn't you override coroutine.create and xpcall() the given function, so the routine cannot be closed down immediatly?

tomblind commented 3 years ago

I've been spread too thin this year, but I have no intention of abandoning this!

Unfortunately, you can't yield through an xpcall, so the coroutines wouldn't work if we tried to inject them like that.

sewbacca commented 3 years ago

Do you need to? Since you are using io afaik to communicate with the debugger, I thought you could do something like this:

local createco = coroutine.create
function coroutine.create(func)
     return createco(function(...)
           local ok = false
           local results = {}
            xpcall(function() results = { func(...) }; ok = true end, function() --[[ Error checking code and io calls here ]] end)
            return table.unpack(results)
     end
end

Maybe I have understood something wrong, but to me it seems like this would be the way to go.

tomblind commented 3 years ago

In that example, when func calls coroutine.yield, the error error: attempt to yield across metamethod/C-call boundary will occur.

I just checked, though, and this seems to only be a limitation in lua5.1. Later versions seem to allow it, so it might be something we could enable on a per-version basis.

tomblind commented 3 years ago

implemented in 1dfd86f

sewbacca commented 3 years ago

In that example, when func calls coroutine.yield, the error error: attempt to yield across metamethod/C-call boundary will occur.

I just checked, though, and this seems to only be a limitation in lua5.1. Later versions seem to allow it, so it might be something we could enable on a per-version basis.

For the time being, this is a work arround (probably only >lua5.1):

local create = coroutine.create
local wrap = coroutine.wrap

function coroutine.create(f)
    return create(function(...)
        return unpack(debugger.call(f))
    end)
end

function coroutine.wrap(f)
    return wrap(function(...)
        return unpack(debugger.call(f))
    end)
end
tomblind commented 3 years ago

This is now supported in version 0.2.3 by enabling breakInCoroutines in launch.json. When enabled, the debugger will break inside coroutines when an error occurs.

The one caveat to this is that in Lua5.1, implicit errors (those not triggered via calls to error or assert) will break where the coroutine resumed instead of at the location they occurred. The real location will be in the error message displayed. This is due to the fact that yield through an xpcall is not possible in 5.1, so catching the error before the stack unwinds is impossible (as far as I know).