aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-12-22 10:54:25 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-12-22 10:54:25 -0300
commit0ceada8da92135717d31a3954b5b89a954f9e71a (patch)
tree99655e44791a11bf805d9f059da7391310618eec
parentf9d29b0c442447ebe429bcaad1e2b4bf13c5dc93 (diff)
downloadlua-0ceada8da92135717d31a3954b5b89a954f9e71a.tar.gz
lua-0ceada8da92135717d31a3954b5b89a954f9e71a.tar.bz2
lua-0ceada8da92135717d31a3954b5b89a954f9e71a.zip
Report last error in closing methods
When there are multiple errors around closing methods, report the last error instead of the original.
-rw-r--r--lcorolib.c7
-rw-r--r--lfunc.c10
-rw-r--r--manual/manual.of5
-rw-r--r--testes/coroutine.lua15
-rw-r--r--testes/locals.lua99
5 files changed, 35 insertions, 101 deletions
diff --git a/lcorolib.c b/lcorolib.c
index c165031d..ed7c58b2 100644
--- a/lcorolib.c
+++ b/lcorolib.c
@@ -75,8 +75,11 @@ static int luaB_auxwrap (lua_State *L) {
75 int r = auxresume(L, co, lua_gettop(L)); 75 int r = auxresume(L, co, lua_gettop(L));
76 if (r < 0) { /* error? */ 76 if (r < 0) { /* error? */
77 int stat = lua_status(co); 77 int stat = lua_status(co);
78 if (stat != LUA_OK && stat != LUA_YIELD) /* error in the coroutine? */ 78 if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */
79 lua_resetthread(co); /* close its tbc variables */ 79 stat = lua_resetthread(co); /* close its tbc variables */
80 lua_assert(stat != LUA_OK);
81 lua_xmove(co, L, 1); /* copy error message */
82 }
80 if (stat != LUA_ERRMEM && /* not a memory error and ... */ 83 if (stat != LUA_ERRMEM && /* not a memory error and ... */
81 lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */ 84 lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
82 luaL_where(L, 1); /* add extra info, if available */ 85 luaL_where(L, 1); /* add extra info, if available */
diff --git a/lfunc.c b/lfunc.c
index 6608592b..bfbf270b 100644
--- a/lfunc.c
+++ b/lfunc.c
@@ -162,14 +162,10 @@ static int callclosemth (lua_State *L, StkId level, int status) {
162 luaD_seterrorobj(L, status, level); /* set error message */ 162 luaD_seterrorobj(L, status, level); /* set error message */
163 if (prepclosingmethod(L, uv, s2v(level))) { /* something to call? */ 163 if (prepclosingmethod(L, uv, s2v(level))) { /* something to call? */
164 int newstatus = luaD_pcall(L, callclose, NULL, oldtop, 0); 164 int newstatus = luaD_pcall(L, callclose, NULL, oldtop, 0);
165 if (newstatus != LUA_OK && status == CLOSEPROTECT) /* first error? */ 165 if (newstatus != LUA_OK) /* new error? */
166 status = newstatus; /* this will be the new error */ 166 status = newstatus; /* this will be the error now */
167 else { 167 else /* leave original error (or nil) on top */
168 if (newstatus != LUA_OK) /* suppressed error? */
169 luaE_warnerror(L, "__close metamethod");
170 /* leave original error (or nil) on top */
171 L->top = restorestack(L, oldtop); 168 L->top = restorestack(L, oldtop);
172 }
173 } 169 }
174 /* else no metamethod; ignore this case and keep original error */ 170 /* else no metamethod; ignore this case and keep original error */
175 } 171 }
diff --git a/manual/manual.of b/manual/manual.of
index 5d0c35cf..c5385258 100644
--- a/manual/manual.of
+++ b/manual/manual.of
@@ -1630,13 +1630,8 @@ they are closed in the reverse order that they were declared.
1630If there is any error while running a closing method, 1630If there is any error while running a closing method,
1631that error is handled like an error in the regular code 1631that error is handled like an error in the regular code
1632where the variable was defined. 1632where the variable was defined.
1633
1634After an error, 1633After an error,
1635the other pending closing methods will still be called. 1634the other pending closing methods will still be called.
1636Errors in these methods
1637interrupt the respective method and generate a warning,
1638but are otherwise ignored;
1639the error reported is only the original one.
1640 1635
1641If a coroutine yields and is never resumed again, 1636If a coroutine yields and is never resumed again,
1642some variables may never go out of scope, 1637some variables may never go out of scope,
diff --git a/testes/coroutine.lua b/testes/coroutine.lua
index aaf565fb..0a970e98 100644
--- a/testes/coroutine.lua
+++ b/testes/coroutine.lua
@@ -172,13 +172,12 @@ do
172 assert(not X and coroutine.status(co) == "dead") 172 assert(not X and coroutine.status(co) == "dead")
173 173
174 -- error closing a coroutine 174 -- error closing a coroutine
175 warn("@on")
176 local x = 0 175 local x = 0
177 co = coroutine.create(function() 176 co = coroutine.create(function()
178 local y <close> = func2close(function (self,err) 177 local y <close> = func2close(function (self,err)
179 if (err ~= 111) then os.exit(false) end -- should not happen 178 assert(err == 111)
180 x = 200 179 x = 200
181 error("200") 180 error(200)
182 end) 181 end)
183 local x <close> = func2close(function (self, err) 182 local x <close> = func2close(function (self, err)
184 assert(err == nil); error(111) 183 assert(err == nil); error(111)
@@ -187,16 +186,8 @@ do
187 end) 186 end)
188 coroutine.resume(co) 187 coroutine.resume(co)
189 assert(x == 0) 188 assert(x == 0)
190 -- with test library, use 'store' mode to check warnings
191 warn(not T and "@off" or "@store")
192 local st, msg = coroutine.close(co) 189 local st, msg = coroutine.close(co)
193 if not T then 190 assert(st == false and coroutine.status(co) == "dead" and msg == 200)
194 warn("@on")
195 else -- test library
196 assert(string.find(_WARN, "200")); _WARN = false
197 warn("@normal")
198 end
199 assert(st == false and coroutine.status(co) == "dead" and msg == 111)
200 assert(x == 200) 191 assert(x == 200)
201 192
202end 193end
diff --git a/testes/locals.lua b/testes/locals.lua
index d32a9a3e..84e0b03a 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -232,7 +232,11 @@ end
232do 232do
233 local X = false 233 local X = false
234 234
235 local x, closescope = func2close(function () stack(10); X = true end, 100) 235 local x, closescope = func2close(function (_, msg)
236 stack(10);
237 assert(msg == nil)
238 X = true
239 end, 100)
236 assert(x == 100); x = 101; -- 'x' is not read-only 240 assert(x == 100); x = 101; -- 'x' is not read-only
237 241
238 -- closing functions do not corrupt returning values 242 -- closing functions do not corrupt returning values
@@ -246,10 +250,11 @@ do
246 250
247 X = false 251 X = false
248 foo = function (x) 252 foo = function (x)
249 local _<close> = func2close(function () 253 local _<close> = func2close(function (_, msg)
250 -- without errors, enclosing function should be still active when 254 -- without errors, enclosing function should be still active when
251 -- __close is called 255 -- __close is called
252 assert(debug.getinfo(2).name == "foo") 256 assert(debug.getinfo(2).name == "foo")
257 assert(msg == nil)
253 end) 258 end)
254 local _<close> = closescope 259 local _<close> = closescope
255 local y = 15 260 local y = 15
@@ -328,64 +333,20 @@ do
328end 333end
329 334
330 335
331-- auxiliary functions for testing warnings in '__close'
332local function prepwarn ()
333 if not T then -- no test library?
334 warn("@off") -- do not show (lots of) warnings
335 else
336 warn("@store") -- to test the warnings
337 end
338end
339
340
341local function endwarn ()
342 if not T then
343 warn("@on") -- back to normal
344 else
345 assert(_WARN == false)
346 warn("@normal")
347 end
348end
349
350
351-- errors inside __close can generate a warning instead of an
352-- error. This new 'assert' force them to appear.
353local function assert(cond, msg)
354 if not cond then
355 local line = debug.getinfo(2).currentline or "?"
356 msg = string.format("assertion failed! line %d (%s)\n", line, msg or "")
357 io.stderr:write(msg)
358 os.exit(1)
359 end
360end
361
362
363local function checkwarn (msg)
364 if T then
365 assert(_WARN and string.find(_WARN, msg))
366 _WARN = false -- reset variable to check next warning
367 end
368end
369
370warn("@on")
371
372do print("testing errors in __close") 336do print("testing errors in __close")
373 337
374 prepwarn()
375
376 -- original error is in __close 338 -- original error is in __close
377 local function foo () 339 local function foo ()
378 340
379 local x <close> = 341 local x <close> =
380 func2close(function (self, msg) 342 func2close(function (self, msg)
381 assert(string.find(msg, "@z")) 343 assert(string.find(msg, "@y"))
382 error("@x") 344 error("@x")
383 end) 345 end)
384 346
385 local x1 <close> = 347 local x1 <close> =
386 func2close(function (self, msg) 348 func2close(function (self, msg)
387 checkwarn("@y") 349 assert(string.find(msg, "@y"))
388 assert(string.find(msg, "@z"))
389 end) 350 end)
390 351
391 local gc <close> = func2close(function () collectgarbage() end) 352 local gc <close> = func2close(function () collectgarbage() end)
@@ -406,8 +367,7 @@ do print("testing errors in __close")
406 end 367 end
407 368
408 local stat, msg = pcall(foo, false) 369 local stat, msg = pcall(foo, false)
409 assert(string.find(msg, "@z")) 370 assert(string.find(msg, "@x"))
410 checkwarn("@x")
411 371
412 372
413 -- original error not in __close 373 -- original error not in __close
@@ -418,14 +378,13 @@ do print("testing errors in __close")
418 -- after error, 'foo' was discarded, so caller now 378 -- after error, 'foo' was discarded, so caller now
419 -- must be 'pcall' 379 -- must be 'pcall'
420 assert(debug.getinfo(2).name == "pcall") 380 assert(debug.getinfo(2).name == "pcall")
421 assert(msg == 4) 381 assert(string.find(msg, "@x1"))
422 end) 382 end)
423 383
424 local x1 <close> = 384 local x1 <close> =
425 func2close(function (self, msg) 385 func2close(function (self, msg)
426 assert(debug.getinfo(2).name == "pcall") 386 assert(debug.getinfo(2).name == "pcall")
427 checkwarn("@y") 387 assert(string.find(msg, "@y"))
428 assert(msg == 4)
429 error("@x1") 388 error("@x1")
430 end) 389 end)
431 390
@@ -434,8 +393,7 @@ do print("testing errors in __close")
434 local y <close> = 393 local y <close> =
435 func2close(function (self, msg) 394 func2close(function (self, msg)
436 assert(debug.getinfo(2).name == "pcall") 395 assert(debug.getinfo(2).name == "pcall")
437 assert(msg == 4) -- error in body 396 assert(string.find(msg, "@z"))
438 checkwarn("@z")
439 error("@y") 397 error("@y")
440 end) 398 end)
441 399
@@ -453,8 +411,7 @@ do print("testing errors in __close")
453 end 411 end
454 412
455 local stat, msg = pcall(foo, true) 413 local stat, msg = pcall(foo, true)
456 assert(msg == 4) 414 assert(string.find(msg, "@x1"))
457 checkwarn("@x1") -- last error
458 415
459 -- error leaving a block 416 -- error leaving a block
460 local function foo (...) 417 local function foo (...)
@@ -466,7 +423,8 @@ do print("testing errors in __close")
466 end) 423 end)
467 424
468 local x123 <close> = 425 local x123 <close> =
469 func2close(function () 426 func2close(function (_, msg)
427 assert(msg == nil)
470 error("@X") 428 error("@X")
471 end) 429 end)
472 end 430 end
@@ -474,9 +432,7 @@ do print("testing errors in __close")
474 end 432 end
475 433
476 local st, msg = xpcall(foo, debug.traceback) 434 local st, msg = xpcall(foo, debug.traceback)
477 assert(string.match(msg, "^[^ ]* @X")) 435 assert(string.match(msg, "^[^ ]* @Y"))
478 assert(string.find(msg, "in metamethod 'close'"))
479 checkwarn("@Y")
480 436
481 -- error in toclose in vararg function 437 -- error in toclose in vararg function
482 local function foo (...) 438 local function foo (...)
@@ -486,7 +442,6 @@ do print("testing errors in __close")
486 local st, msg = xpcall(foo, debug.traceback) 442 local st, msg = xpcall(foo, debug.traceback)
487 assert(string.match(msg, "^[^ ]* @x123")) 443 assert(string.match(msg, "^[^ ]* @x123"))
488 assert(string.find(msg, "in metamethod 'close'")) 444 assert(string.find(msg, "in metamethod 'close'"))
489 endwarn()
490end 445end
491 446
492 447
@@ -511,8 +466,6 @@ end
511 466
512if rawget(_G, "T") then 467if rawget(_G, "T") then
513 468
514 warn("@off")
515
516 -- memory error inside closing function 469 -- memory error inside closing function
517 local function foo () 470 local function foo ()
518 local y <close> = func2close(function () T.alloccount() end) 471 local y <close> = func2close(function () T.alloccount() end)
@@ -527,7 +480,7 @@ if rawget(_G, "T") then
527 -- despite memory error, 'y' will be executed and 480 -- despite memory error, 'y' will be executed and
528 -- memory limit will be lifted 481 -- memory limit will be lifted
529 local _, msg = pcall(foo) 482 local _, msg = pcall(foo)
530 assert(msg == 1000) 483 assert(msg == "not enough memory")
531 484
532 local close = func2close(function (self, msg) 485 local close = func2close(function (self, msg)
533 T.alloccount() 486 T.alloccount()
@@ -570,7 +523,7 @@ if rawget(_G, "T") then
570 end 523 end
571 524
572 local _, msg = pcall(test) 525 local _, msg = pcall(test)
573 assert(msg == "not enough memory") -- reported error is the first one 526 assert(msg == 1000)
574 527
575 do -- testing 'toclose' in C string buffer 528 do -- testing 'toclose' in C string buffer
576 collectgarbage() 529 collectgarbage()
@@ -625,7 +578,6 @@ if rawget(_G, "T") then
625 print'+' 578 print'+'
626 end 579 end
627 580
628 warn("@on")
629end 581end
630 582
631 583
@@ -655,14 +607,14 @@ end
655 607
656 608
657do 609do
658 prepwarn()
659 610
660 -- error in a wrapped coroutine raising errors when closing a variable 611 -- error in a wrapped coroutine raising errors when closing a variable
661 local x = 0 612 local x = 0
662 local co = coroutine.wrap(function () 613 local co = coroutine.wrap(function ()
663 local xx <close> = func2close(function () 614 local xx <close> = func2close(function (_, msg)
664 x = x + 1; 615 x = x + 1;
665 checkwarn("@XXX"); error("@YYY") 616 assert(string.find(msg, "@XXX"))
617 error("@YYY")
666 end) 618 end)
667 local xv <close> = func2close(function () x = x + 1; error("@XXX") end) 619 local xv <close> = func2close(function () x = x + 1; error("@XXX") end)
668 coroutine.yield(100) 620 coroutine.yield(100)
@@ -670,8 +622,7 @@ do
670 end) 622 end)
671 assert(co() == 100); assert(x == 0) 623 assert(co() == 100); assert(x == 0)
672 local st, msg = pcall(co); assert(x == 2) 624 local st, msg = pcall(co); assert(x == 2)
673 assert(not st and msg == 200) -- should get first error raised 625 assert(not st and string.find(msg, "@YYY")) -- should get error raised
674 checkwarn("@YYY")
675 626
676 local x = 0 627 local x = 0
677 local y = 0 628 local y = 0
@@ -691,10 +642,8 @@ do
691 local st, msg = pcall(co) 642 local st, msg = pcall(co)
692 assert(x == 1 and y == 1) 643 assert(x == 1 and y == 1)
693 -- should get first error raised 644 -- should get first error raised
694 assert(not st and string.find(msg, "%w+%.%w+:%d+: XXX")) 645 assert(not st and string.find(msg, "%w+%.%w+:%d+: YYY"))
695 checkwarn("YYY")
696 646
697 endwarn()
698end 647end
699 648
700 649