From c9986d934295c3d7b8702f4067cd4661cfd2888a Mon Sep 17 00:00:00 2001
From: Georgiy Lebedev <g.lebedev@tarantool.org>
Date: Wed, 11 Jan 2023 14:40:46 +0300
Subject: [PATCH] lua: fix `print` wrapper to handle errors thrown from `print`

Both of the callbacks in the `print` wrapper are expected to be called, but
`print` may throw errors, e.g.,
`print(setmetatable({}, {__tostring = error})`, so we need to call it in a
protected environment and execute the 'after' callback even if `print`
throws.

Closes #8136

NO_CHANGELOG=<gh-7186 was not released yet>
NO_DOC=bugfix
---
 src/lua/print.lua                             |  5 ++-
 ...handling_errors_thrown_from_print_test.lua | 32 +++++++++++++++++++
 2 files changed, 36 insertions(+), 1 deletion(-)
 create mode 100644 test/app-luatest/gh_8136_fix_print_wrapper_not_handling_errors_thrown_from_print_test.lua

diff --git a/src/lua/print.lua b/src/lua/print.lua
index 75fe436b73..c9213e200a 100644
--- a/src/lua/print.lua
+++ b/src/lua/print.lua
@@ -8,10 +8,13 @@ _G.print = function(...)
     if M.before_cb ~= nil then
         M.before_cb()
     end
-    M.raw_print(...)
+    local ok, err = pcall(M.raw_print, ...)
     if M.after_cb ~= nil then
         M.after_cb()
     end
+    if not ok then
+        error(err)
+    end
 end
 
 return M
diff --git a/test/app-luatest/gh_8136_fix_print_wrapper_not_handling_errors_thrown_from_print_test.lua b/test/app-luatest/gh_8136_fix_print_wrapper_not_handling_errors_thrown_from_print_test.lua
new file mode 100644
index 0000000000..6bfa3b2ad2
--- /dev/null
+++ b/test/app-luatest/gh_8136_fix_print_wrapper_not_handling_errors_thrown_from_print_test.lua
@@ -0,0 +1,32 @@
+local it = require('test.interactive_tarantool')
+
+local t = require('luatest')
+local g = t.group()
+
+-- Checks that `print` throwing an error is handled correctly.
+g.test_basic_print_with_exception = function()
+    local child = it.new({args = {'-l', 'fiber'}})
+
+    child:execute_command([[
+        _ = fiber.create(function()
+            fiber.name('print_flood', {truncate = true})
+            while true do
+                pcall(function()
+                          print(setmetatable({}, {__tostring = error}))
+                      end)
+                print('flood')
+                fiber.sleep(0.01)
+            end
+        end)
+    ]])
+    child:assert_empty_response()
+
+    local exp_line = it.PROMPT .. it.CR .. it.ERASE_IN_LINE ..
+                     it.PROMPT .. it.CR .. it.ERASE_IN_LINE ..
+                     'flood'
+    for _ = 1, 10 do
+        child:assert_line(exp_line)
+    end
+
+    child:close()
+end
-- 
GitLab