diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2020-12-22 10:54:25 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2020-12-22 10:54:25 -0300 |
commit | 0ceada8da92135717d31a3954b5b89a954f9e71a (patch) | |
tree | 99655e44791a11bf805d9f059da7391310618eec | |
parent | f9d29b0c442447ebe429bcaad1e2b4bf13c5dc93 (diff) | |
download | lua-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.c | 7 | ||||
-rw-r--r-- | lfunc.c | 10 | ||||
-rw-r--r-- | manual/manual.of | 5 | ||||
-rw-r--r-- | testes/coroutine.lua | 15 | ||||
-rw-r--r-- | testes/locals.lua | 99 |
5 files changed, 35 insertions, 101 deletions
@@ -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 */ |
@@ -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. | |||
1630 | If there is any error while running a closing method, | 1630 | If there is any error while running a closing method, |
1631 | that error is handled like an error in the regular code | 1631 | that error is handled like an error in the regular code |
1632 | where the variable was defined. | 1632 | where the variable was defined. |
1633 | |||
1634 | After an error, | 1633 | After an error, |
1635 | the other pending closing methods will still be called. | 1634 | the other pending closing methods will still be called. |
1636 | Errors in these methods | ||
1637 | interrupt the respective method and generate a warning, | ||
1638 | but are otherwise ignored; | ||
1639 | the error reported is only the original one. | ||
1640 | 1635 | ||
1641 | If a coroutine yields and is never resumed again, | 1636 | If a coroutine yields and is never resumed again, |
1642 | some variables may never go out of scope, | 1637 | some 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 | ||
202 | end | 193 | end |
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 | |||
232 | do | 232 | do |
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 | |||
328 | end | 333 | end |
329 | 334 | ||
330 | 335 | ||
331 | -- auxiliary functions for testing warnings in '__close' | ||
332 | local 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 | ||
338 | end | ||
339 | |||
340 | |||
341 | local function endwarn () | ||
342 | if not T then | ||
343 | warn("@on") -- back to normal | ||
344 | else | ||
345 | assert(_WARN == false) | ||
346 | warn("@normal") | ||
347 | end | ||
348 | end | ||
349 | |||
350 | |||
351 | -- errors inside __close can generate a warning instead of an | ||
352 | -- error. This new 'assert' force them to appear. | ||
353 | local 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 | ||
360 | end | ||
361 | |||
362 | |||
363 | local 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 | ||
368 | end | ||
369 | |||
370 | warn("@on") | ||
371 | |||
372 | do print("testing errors in __close") | 336 | do 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() | ||
490 | end | 445 | end |
491 | 446 | ||
492 | 447 | ||
@@ -511,8 +466,6 @@ end | |||
511 | 466 | ||
512 | if rawget(_G, "T") then | 467 | if 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") | ||
629 | end | 581 | end |
630 | 582 | ||
631 | 583 | ||
@@ -655,14 +607,14 @@ end | |||
655 | 607 | ||
656 | 608 | ||
657 | do | 609 | do |
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() | ||
698 | end | 647 | end |
699 | 648 | ||
700 | 649 | ||