123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- -- $Id: testes/cstack.lua $
- -- See Copyright Notice in file lua.h
- local tracegc = require"tracegc"
- print"testing stack overflow detection"
- -- Segmentation faults in these tests probably result from a C-stack
- -- overflow. To avoid these errors, you should set a smaller limit for
- -- the use of C stack by Lua, by changing the constant 'LUAI_MAXCCALLS'.
- -- Alternatively, you can ensure a larger stack for the program.
- local function checkerror (msg, f, ...)
- local s, err = pcall(f, ...)
- assert(not s and string.find(err, msg))
- end
- do print("testing stack overflow in message handling")
- local count = 0
- local function loop (x, y, z)
- count = count + 1
- return 1 + loop(x, y, z)
- end
- tracegc.stop() -- __gc should not be called with a full stack
- local res, msg = xpcall(loop, loop)
- tracegc.start()
- assert(msg == "error in error handling")
- print("final count: ", count)
- end
- -- bug since 2.5 (C-stack overflow in recursion inside pattern matching)
- do print("testing recursion inside pattern matching")
- local function f (size)
- local s = string.rep("a", size)
- local p = string.rep(".?", size)
- return string.match(s, p)
- end
- local m = f(80)
- assert(#m == 80)
- checkerror("too complex", f, 2000)
- end
- do print("testing stack-overflow in recursive 'gsub'")
- local count = 0
- local function foo ()
- count = count + 1
- string.gsub("a", ".", foo)
- end
- checkerror("stack overflow", foo)
- print("final count: ", count)
- print("testing stack-overflow in recursive 'gsub' with metatables")
- local count = 0
- local t = setmetatable({}, {__index = foo})
- foo = function ()
- count = count + 1
- string.gsub("a", ".", t)
- end
- checkerror("stack overflow", foo)
- print("final count: ", count)
- end
- do -- bug in 5.4.0
- print("testing limits in coroutines inside deep calls")
- local count = 0
- local lim = 1000
- local function stack (n)
- if n > 0 then return stack(n - 1) + 1
- else coroutine.wrap(function ()
- count = count + 1
- stack(lim)
- end)()
- end
- end
- local st, msg = xpcall(stack, function () return "ok" end, lim)
- assert(not st and msg == "ok")
- print("final count: ", count)
- end
- do -- bug since 5.4.0
- local count = 0
- print("chain of 'coroutine.close'")
- -- create N coroutines forming a list so that each one, when closed,
- -- closes the previous one. (With a large enough N, previous Lua
- -- versions crash in this test.)
- local coro = false
- for i = 1, 1000 do
- local previous = coro
- coro = coroutine.create(function()
- local cc <close> = setmetatable({}, {__close=function()
- count = count + 1
- if previous then
- assert(coroutine.close(previous))
- end
- end})
- coroutine.yield() -- leaves 'cc' pending to be closed
- end)
- assert(coroutine.resume(coro)) -- start it and run until it yields
- end
- local st, msg = coroutine.close(coro)
- assert(not st and string.find(msg, "C stack overflow"))
- print("final count: ", count)
- end
- do
- print("nesting of resuming yielded coroutines")
- local count = 0
- local function body ()
- coroutine.yield()
- local f = coroutine.wrap(body)
- f(); -- start new coroutine (will stop in previous yield)
- count = count + 1
- f() -- call it recursively
- end
- local f = coroutine.wrap(body)
- f()
- assert(not pcall(f))
- print("final count: ", count)
- end
- do -- bug in 5.4.2
- print("nesting coroutines running after recoverable errors")
- local count = 0
- local function foo()
- count = count + 1
- pcall(1) -- create an error
- -- running now inside 'precover' ("protected recover")
- coroutine.wrap(foo)() -- call another coroutine
- end
- checkerror("C stack overflow", foo)
- print("final count: ", count)
- end
- if T then
- print("testing stack recovery")
- local N = 0 -- trace number of calls
- local LIM = -1 -- will store N just before stack overflow
- -- trace stack size; after stack overflow, it should be
- -- the maximum allowed stack size.
- local stack1
- local dummy
- local function err(msg)
- assert(string.find(msg, "stack overflow"))
- local _, stacknow = T.stacklevel()
- assert(stacknow == stack1 + 200)
- end
- -- When LIM==-1, the 'if' is not executed, so this function only
- -- counts and stores the stack limits up to overflow. Then, LIM
- -- becomes N, and then the 'if' code is run when the stack is
- -- full. Then, there is a stack overflow inside 'xpcall', after which
- -- the stack must have been restored back to its maximum normal size.
- local function f()
- dummy, stack1 = T.stacklevel()
- if N == LIM then
- xpcall(f, err)
- local _, stacknow = T.stacklevel()
- assert(stacknow == stack1)
- return
- end
- N = N + 1
- f()
- end
- local topB, sizeB -- top and size Before overflow
- local topA, sizeA -- top and size After overflow
- topB, sizeB = T.stacklevel()
- tracegc.stop() -- __gc should not be called with a full stack
- xpcall(f, err)
- tracegc.start()
- topA, sizeA = T.stacklevel()
- -- sizes should be comparable
- assert(topA == topB and sizeA < sizeB * 2)
- print(string.format("maximum stack size: %d", stack1))
- LIM = N -- will stop recursion at maximum level
- N = 0 -- to count again
- tracegc.stop() -- __gc should not be called with a full stack
- f()
- tracegc.start()
- print"+"
- end
- print'OK'
|