luvit / lit

Toolkit for developing, sharing, and running luvit/lua programs and libraries.
http://lit.luvit.io/
Apache License 2.0
245 stars 58 forks source link

Preserves tracebacks for coroutine resumes #276

Closed SinisterRectus closed 4 years ago

SinisterRectus commented 4 years ago

Closes #273.

I replaced every assert(coroutine.resume(thread, ...)) with an assertResume(thread, ...) wrapper defined in each file that needs it. Not really a sustainable solution, but I didn't want to make a new dependency at this time.

SinisterRectus commented 4 years ago

I should point out that this assertResume doesn't return any values given by coroutine.resume. This isn't an actual issue, though, because all of the coroutine.yield calls are "empty".

truemedian commented 4 years ago

If this were to end up going into a library instead of being copied around everywhere, I think luvit/utils would be the best place for it. It already holds the utils used around luvit to bind functions, I think this might fit there.

SinisterRectus commented 4 years ago

I was actually going to do that next, for the couple of places that luvit resumes coroutines. There aren't many, but one in particular is timer.sleep. The utils module is definitely amenable to this kind of change, but does it make sense to depend on it in lit and the coro libraries?

SinisterRectus commented 4 years ago

A general solution probably should have a mechanism for collecting the returns, although I'm not sure how useful this is when all of the resumers are callbacks that return into the void.

Example:

local pack = table.pack or function(...)
  return {..., n = select('#', ...)}
end

local unpack = table.unpack or unpack

local function assertResume(thread, ...)
  local success, err = coroutine.resume(thread, ...)
  if not success then
    error(debug.traceback(thread, err), 0)
  end
end

local function assertResumePack(thread, ...)
  local ret = pack(coroutine.resume(thread, ...))
  if not ret[1] then
    error(debug.traceback(thread, ret[2]), 0)
  else
    return unpack(ret, 2, ret.n)
  end
end

local thread1 = coroutine.create(coroutine.yield)
local thread2 = coroutine.create(coroutine.yield)

local x = assertResume(thread1, 1)
assert(x == nil)

local a, b, c = assertResumePack(thread2, 1, nil, 3)
assert(a == 1)
assert(b == nil)
assert(c == 3)
SinisterRectus commented 4 years ago

Forgot to bump the versions. Did a patch bump for each.