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 | ||
