diff options
Diffstat (limited to 'testes/errors.lua')
| -rw-r--r-- | testes/errors.lua | 554 |
1 files changed, 554 insertions, 0 deletions
diff --git a/testes/errors.lua b/testes/errors.lua new file mode 100644 index 00000000..63a7b740 --- /dev/null +++ b/testes/errors.lua | |||
| @@ -0,0 +1,554 @@ | |||
| 1 | -- $Id: errors.lua,v 1.97 2017/11/28 15:31:56 roberto Exp $ | ||
| 2 | -- See Copyright Notice in file all.lua | ||
| 3 | |||
| 4 | print("testing errors") | ||
| 5 | |||
| 6 | local debug = require"debug" | ||
| 7 | |||
| 8 | -- avoid problems with 'strict' module (which may generate other error messages) | ||
| 9 | local mt = getmetatable(_G) or {} | ||
| 10 | local oldmm = mt.__index | ||
| 11 | mt.__index = nil | ||
| 12 | |||
| 13 | local function checkerr (msg, f, ...) | ||
| 14 | local st, err = pcall(f, ...) | ||
| 15 | assert(not st and string.find(err, msg)) | ||
| 16 | end | ||
| 17 | |||
| 18 | |||
| 19 | local function doit (s) | ||
| 20 | local f, msg = load(s) | ||
| 21 | if f == nil then return msg end | ||
| 22 | local cond, msg = pcall(f) | ||
| 23 | return (not cond) and msg | ||
| 24 | end | ||
| 25 | |||
| 26 | |||
| 27 | local function checkmessage (prog, msg) | ||
| 28 | local m = doit(prog) | ||
| 29 | assert(string.find(m, msg, 1, true)) | ||
| 30 | end | ||
| 31 | |||
| 32 | local function checksyntax (prog, extra, token, line) | ||
| 33 | local msg = doit(prog) | ||
| 34 | if not string.find(token, "^<%a") and not string.find(token, "^char%(") | ||
| 35 | then token = "'"..token.."'" end | ||
| 36 | token = string.gsub(token, "(%p)", "%%%1") | ||
| 37 | local pt = string.format([[^%%[string ".*"%%]:%d: .- near %s$]], | ||
| 38 | line, token) | ||
| 39 | assert(string.find(msg, pt)) | ||
| 40 | assert(string.find(msg, msg, 1, true)) | ||
| 41 | end | ||
| 42 | |||
| 43 | |||
| 44 | -- test error message with no extra info | ||
| 45 | assert(doit("error('hi', 0)") == 'hi') | ||
| 46 | |||
| 47 | -- test error message with no info | ||
| 48 | assert(doit("error()") == nil) | ||
| 49 | |||
| 50 | |||
| 51 | -- test common errors/errors that crashed in the past | ||
| 52 | assert(doit("table.unpack({}, 1, n=2^30)")) | ||
| 53 | assert(doit("a=math.sin()")) | ||
| 54 | assert(not doit("tostring(1)") and doit("tostring()")) | ||
| 55 | assert(doit"tonumber()") | ||
| 56 | assert(doit"repeat until 1; a") | ||
| 57 | assert(doit"return;;") | ||
| 58 | assert(doit"assert(false)") | ||
| 59 | assert(doit"assert(nil)") | ||
| 60 | assert(doit("function a (... , ...) end")) | ||
| 61 | assert(doit("function a (, ...) end")) | ||
| 62 | assert(doit("local t={}; t = t[#t] + 1")) | ||
| 63 | |||
| 64 | checksyntax([[ | ||
| 65 | local a = {4 | ||
| 66 | |||
| 67 | ]], "'}' expected (to close '{' at line 1)", "<eof>", 3) | ||
| 68 | |||
| 69 | |||
| 70 | if not T then | ||
| 71 | (Message or print) | ||
| 72 | ('\n >>> testC not active: skipping memory message test <<<\n') | ||
| 73 | else | ||
| 74 | print "testing memory error message" | ||
| 75 | local a = {} | ||
| 76 | for i = 1, 10000 do a[i] = true end -- preallocate array | ||
| 77 | collectgarbage() | ||
| 78 | T.totalmem(T.totalmem() + 10000) | ||
| 79 | -- force a memory error (by a small margin) | ||
| 80 | local st, msg = pcall(function() | ||
| 81 | for i = 1, 100000 do a[i] = tostring(i) end | ||
| 82 | end) | ||
| 83 | T.totalmem(0) | ||
| 84 | assert(not st and msg == "not enough" .. " memory") | ||
| 85 | end | ||
| 86 | |||
| 87 | |||
| 88 | -- tests for better error messages | ||
| 89 | |||
| 90 | checkmessage("a = {} + 1", "arithmetic") | ||
| 91 | checkmessage("a = {} | 1", "bitwise operation") | ||
| 92 | checkmessage("a = {} < 1", "attempt to compare") | ||
| 93 | checkmessage("a = {} <= 1", "attempt to compare") | ||
| 94 | |||
| 95 | checkmessage("a=1; bbbb=2; a=math.sin(3)+bbbb(3)", "global 'bbbb'") | ||
| 96 | checkmessage("a={}; do local a=1 end a:bbbb(3)", "method 'bbbb'") | ||
| 97 | checkmessage("local a={}; a.bbbb(3)", "field 'bbbb'") | ||
| 98 | assert(not string.find(doit"a={13}; local bbbb=1; a[bbbb](3)", "'bbbb'")) | ||
| 99 | checkmessage("a={13}; local bbbb=1; a[bbbb](3)", "number") | ||
| 100 | checkmessage("a=(1)..{}", "a table value") | ||
| 101 | |||
| 102 | checkmessage("a = #print", "length of a function value") | ||
| 103 | checkmessage("a = #3", "length of a number value") | ||
| 104 | |||
| 105 | aaa = nil | ||
| 106 | checkmessage("aaa.bbb:ddd(9)", "global 'aaa'") | ||
| 107 | checkmessage("local aaa={bbb=1}; aaa.bbb:ddd(9)", "field 'bbb'") | ||
| 108 | checkmessage("local aaa={bbb={}}; aaa.bbb:ddd(9)", "method 'ddd'") | ||
| 109 | checkmessage("local a,b,c; (function () a = b+1.1 end)()", "upvalue 'b'") | ||
| 110 | assert(not doit"local aaa={bbb={ddd=next}}; aaa.bbb:ddd(nil)") | ||
| 111 | |||
| 112 | -- upvalues being indexed do not go to the stack | ||
| 113 | checkmessage("local a,b,cc; (function () a = cc[1] end)()", "upvalue 'cc'") | ||
| 114 | checkmessage("local a,b,cc; (function () a.x = 1 end)()", "upvalue 'a'") | ||
| 115 | |||
| 116 | checkmessage("local _ENV = {x={}}; a = a + 1", "global 'a'") | ||
| 117 | |||
| 118 | checkmessage("b=1; local aaa={}; x=aaa+b", "local 'aaa'") | ||
| 119 | checkmessage("aaa={}; x=3.3/aaa", "global 'aaa'") | ||
| 120 | checkmessage("aaa=2; b=nil;x=aaa*b", "global 'b'") | ||
| 121 | checkmessage("aaa={}; x=-aaa", "global 'aaa'") | ||
| 122 | |||
| 123 | -- short circuit | ||
| 124 | checkmessage("a=1; local a,bbbb=2,3; a = math.sin(1) and bbbb(3)", | ||
| 125 | "local 'bbbb'") | ||
| 126 | checkmessage("a=1; local a,bbbb=2,3; a = bbbb(1) or a(3)", "local 'bbbb'") | ||
| 127 | checkmessage("local a,b,c,f = 1,1,1; f((a and b) or c)", "local 'f'") | ||
| 128 | checkmessage("local a,b,c = 1,1,1; ((a and b) or c)()", "call a number value") | ||
| 129 | assert(not string.find(doit"aaa={}; x=(aaa or aaa)+(aaa and aaa)", "'aaa'")) | ||
| 130 | assert(not string.find(doit"aaa={}; (aaa or aaa)()", "'aaa'")) | ||
| 131 | |||
| 132 | checkmessage("print(print < 10)", "function with number") | ||
| 133 | checkmessage("print(print < print)", "two function values") | ||
| 134 | checkmessage("print('10' < 10)", "string with number") | ||
| 135 | checkmessage("print(10 < '23')", "number with string") | ||
| 136 | |||
| 137 | -- float->integer conversions | ||
| 138 | checkmessage("local a = 2.0^100; x = a << 2", "local a") | ||
| 139 | checkmessage("local a = 1 >> 2.0^100", "has no integer representation") | ||
| 140 | checkmessage("local a = 10.1 << 2.0^100", "has no integer representation") | ||
| 141 | checkmessage("local a = 2.0^100 & 1", "has no integer representation") | ||
| 142 | checkmessage("local a = 2.0^100 & 1e100", "has no integer representation") | ||
| 143 | checkmessage("local a = 2.0 | 1e40", "has no integer representation") | ||
| 144 | checkmessage("local a = 2e100 ~ 1", "has no integer representation") | ||
| 145 | checkmessage("string.sub('a', 2.0^100)", "has no integer representation") | ||
| 146 | checkmessage("string.rep('a', 3.3)", "has no integer representation") | ||
| 147 | checkmessage("return 6e40 & 7", "has no integer representation") | ||
| 148 | checkmessage("return 34 << 7e30", "has no integer representation") | ||
| 149 | checkmessage("return ~-3e40", "has no integer representation") | ||
| 150 | checkmessage("return ~-3.009", "has no integer representation") | ||
| 151 | checkmessage("return 3.009 & 1", "has no integer representation") | ||
| 152 | checkmessage("return 34 >> {}", "table value") | ||
| 153 | checkmessage("a = 24 // 0", "divide by zero") | ||
| 154 | checkmessage("a = 1 % 0", "'n%0'") | ||
| 155 | |||
| 156 | |||
| 157 | -- passing light userdata instead of full userdata | ||
| 158 | _G.D = debug | ||
| 159 | checkmessage([[ | ||
| 160 | -- create light udata | ||
| 161 | local x = D.upvalueid(function () return debug end, 1) | ||
| 162 | D.setuservalue(x, {}) | ||
| 163 | ]], "light userdata") | ||
| 164 | _G.D = nil | ||
| 165 | |||
| 166 | do -- named objects (field '__name') | ||
| 167 | checkmessage("math.sin(io.input())", "(number expected, got FILE*)") | ||
| 168 | _G.XX = setmetatable({}, {__name = "My Type"}) | ||
| 169 | assert(string.find(tostring(XX), "^My Type")) | ||
| 170 | checkmessage("io.input(XX)", "(FILE* expected, got My Type)") | ||
| 171 | checkmessage("return XX + 1", "on a My Type value") | ||
| 172 | checkmessage("return ~io.stdin", "on a FILE* value") | ||
| 173 | checkmessage("return XX < XX", "two My Type values") | ||
| 174 | checkmessage("return {} < XX", "table with My Type") | ||
| 175 | checkmessage("return XX < io.stdin", "My Type with FILE*") | ||
| 176 | _G.XX = nil | ||
| 177 | end | ||
| 178 | |||
| 179 | -- global functions | ||
| 180 | checkmessage("(io.write or print){}", "io.write") | ||
| 181 | checkmessage("(collectgarbage or print){}", "collectgarbage") | ||
| 182 | |||
| 183 | -- errors in functions without debug info | ||
| 184 | do | ||
| 185 | local f = function (a) return a + 1 end | ||
| 186 | f = assert(load(string.dump(f, true))) | ||
| 187 | assert(f(3) == 4) | ||
| 188 | checkerr("^%?:%-1:", f, {}) | ||
| 189 | |||
| 190 | -- code with a move to a local var ('OP_MOV A B' with A<B) | ||
| 191 | f = function () local a; a = {}; return a + 2 end | ||
| 192 | -- no debug info (so that 'a' is unknown) | ||
| 193 | f = assert(load(string.dump(f, true))) | ||
| 194 | -- symbolic execution should not get lost | ||
| 195 | checkerr("^%?:%-1:.*table value", f) | ||
| 196 | end | ||
| 197 | |||
| 198 | |||
| 199 | -- tests for field accesses after RK limit | ||
| 200 | local t = {} | ||
| 201 | for i = 1, 1000 do | ||
| 202 | t[i] = "a = x" .. i | ||
| 203 | end | ||
| 204 | local s = table.concat(t, "; ") | ||
| 205 | t = nil | ||
| 206 | checkmessage(s.."; a = bbb + 1", "global 'bbb'") | ||
| 207 | checkmessage("local _ENV=_ENV;"..s.."; a = bbb + 1", "global 'bbb'") | ||
| 208 | checkmessage(s.."; local t = {}; a = t.bbb + 1", "field 'bbb'") | ||
| 209 | checkmessage(s.."; local t = {}; t:bbb()", "method 'bbb'") | ||
| 210 | |||
| 211 | checkmessage([[aaa=9 | ||
| 212 | repeat until 3==3 | ||
| 213 | local x=math.sin(math.cos(3)) | ||
| 214 | if math.sin(1) == x then return math.sin(1) end -- tail call | ||
| 215 | local a,b = 1, { | ||
| 216 | {x='a'..'b'..'c', y='b', z=x}, | ||
| 217 | {1,2,3,4,5} or 3+3<=3+3, | ||
| 218 | 3+1>3+1, | ||
| 219 | {d = x and aaa[x or y]}} | ||
| 220 | ]], "global 'aaa'") | ||
| 221 | |||
| 222 | checkmessage([[ | ||
| 223 | local x,y = {},1 | ||
| 224 | if math.sin(1) == 0 then return 3 end -- return | ||
| 225 | x.a()]], "field 'a'") | ||
| 226 | |||
| 227 | checkmessage([[ | ||
| 228 | prefix = nil | ||
| 229 | insert = nil | ||
| 230 | while 1 do | ||
| 231 | local a | ||
| 232 | if nil then break end | ||
| 233 | insert(prefix, a) | ||
| 234 | end]], "global 'insert'") | ||
| 235 | |||
| 236 | checkmessage([[ -- tail call | ||
| 237 | return math.sin("a") | ||
| 238 | ]], "'sin'") | ||
| 239 | |||
| 240 | checkmessage([[collectgarbage("nooption")]], "invalid option") | ||
| 241 | |||
| 242 | checkmessage([[x = print .. "a"]], "concatenate") | ||
| 243 | checkmessage([[x = "a" .. false]], "concatenate") | ||
| 244 | checkmessage([[x = {} .. 2]], "concatenate") | ||
| 245 | |||
| 246 | checkmessage("getmetatable(io.stdin).__gc()", "no value") | ||
| 247 | |||
| 248 | checkmessage([[ | ||
| 249 | local Var | ||
| 250 | local function main() | ||
| 251 | NoSuchName (function() Var=0 end) | ||
| 252 | end | ||
| 253 | main() | ||
| 254 | ]], "global 'NoSuchName'") | ||
| 255 | print'+' | ||
| 256 | |||
| 257 | a = {}; setmetatable(a, {__index = string}) | ||
| 258 | checkmessage("a:sub()", "bad self") | ||
| 259 | checkmessage("string.sub('a', {})", "#2") | ||
| 260 | checkmessage("('a'):sub{}", "#1") | ||
| 261 | |||
| 262 | checkmessage("table.sort({1,2,3}, table.sort)", "'table.sort'") | ||
| 263 | checkmessage("string.gsub('s', 's', setmetatable)", "'setmetatable'") | ||
| 264 | |||
| 265 | -- tests for errors in coroutines | ||
| 266 | |||
| 267 | local function f (n) | ||
| 268 | local c = coroutine.create(f) | ||
| 269 | local a,b = coroutine.resume(c) | ||
| 270 | return b | ||
| 271 | end | ||
| 272 | assert(string.find(f(), "C stack overflow")) | ||
| 273 | |||
| 274 | checkmessage("coroutine.yield()", "outside a coroutine") | ||
| 275 | |||
| 276 | f = coroutine.wrap(function () table.sort({1,2,3}, coroutine.yield) end) | ||
| 277 | checkerr("yield across", f) | ||
| 278 | |||
| 279 | |||
| 280 | -- testing size of 'source' info; size of buffer for that info is | ||
| 281 | -- LUA_IDSIZE, declared as 60 in luaconf. Get one position for '\0'. | ||
| 282 | idsize = 60 - 1 | ||
| 283 | local function checksize (source) | ||
| 284 | -- syntax error | ||
| 285 | local _, msg = load("x", source) | ||
| 286 | msg = string.match(msg, "^([^:]*):") -- get source (1st part before ':') | ||
| 287 | assert(msg:len() <= idsize) | ||
| 288 | end | ||
| 289 | |||
| 290 | for i = 60 - 10, 60 + 10 do -- check border cases around 60 | ||
| 291 | checksize("@" .. string.rep("x", i)) -- file names | ||
| 292 | checksize(string.rep("x", i - 10)) -- string sources | ||
| 293 | checksize("=" .. string.rep("x", i)) -- exact sources | ||
| 294 | end | ||
| 295 | |||
| 296 | |||
| 297 | -- testing line error | ||
| 298 | |||
| 299 | local function lineerror (s, l) | ||
| 300 | local err,msg = pcall(load(s)) | ||
| 301 | local line = string.match(msg, ":(%d+):") | ||
| 302 | assert((line and line+0) == l) | ||
| 303 | end | ||
| 304 | |||
| 305 | lineerror("local a\n for i=1,'a' do \n print(i) \n end", 2) | ||
| 306 | lineerror("\n local a \n for k,v in 3 \n do \n print(k) \n end", 3) | ||
| 307 | lineerror("\n\n for k,v in \n 3 \n do \n print(k) \n end", 4) | ||
| 308 | lineerror("function a.x.y ()\na=a+1\nend", 1) | ||
| 309 | |||
| 310 | lineerror("a = \na\n+\n{}", 3) | ||
| 311 | lineerror("a = \n3\n+\n(\n4\n/\nprint)", 6) | ||
| 312 | lineerror("a = \nprint\n+\n(\n4\n/\n7)", 3) | ||
| 313 | |||
| 314 | lineerror("a\n=\n-\n\nprint\n;", 3) | ||
| 315 | |||
| 316 | lineerror([[ | ||
| 317 | a | ||
| 318 | ( | ||
| 319 | 23) | ||
| 320 | ]], 1) | ||
| 321 | |||
| 322 | lineerror([[ | ||
| 323 | local a = {x = 13} | ||
| 324 | a | ||
| 325 | . | ||
| 326 | x | ||
| 327 | ( | ||
| 328 | 23 | ||
| 329 | ) | ||
| 330 | ]], 2) | ||
| 331 | |||
| 332 | lineerror([[ | ||
| 333 | local a = {x = 13} | ||
| 334 | a | ||
| 335 | . | ||
| 336 | x | ||
| 337 | ( | ||
| 338 | 23 + a | ||
| 339 | ) | ||
| 340 | ]], 6) | ||
| 341 | |||
| 342 | local p = [[ | ||
| 343 | function g() f() end | ||
| 344 | function f(x) error('a', X) end | ||
| 345 | g() | ||
| 346 | ]] | ||
| 347 | X=3;lineerror((p), 3) | ||
| 348 | X=0;lineerror((p), nil) | ||
| 349 | X=1;lineerror((p), 2) | ||
| 350 | X=2;lineerror((p), 1) | ||
| 351 | |||
| 352 | |||
| 353 | if not _soft then | ||
| 354 | -- several tests that exaust the Lua stack | ||
| 355 | collectgarbage() | ||
| 356 | print"testing stack overflow" | ||
| 357 | C = 0 | ||
| 358 | local l = debug.getinfo(1, "l").currentline; function y () C=C+1; y() end | ||
| 359 | |||
| 360 | local function checkstackmessage (m) | ||
| 361 | return (string.find(m, "stack overflow")) | ||
| 362 | end | ||
| 363 | -- repeated stack overflows (to check stack recovery) | ||
| 364 | assert(checkstackmessage(doit('y()'))) | ||
| 365 | print('+') | ||
| 366 | assert(checkstackmessage(doit('y()'))) | ||
| 367 | print('+') | ||
| 368 | assert(checkstackmessage(doit('y()'))) | ||
| 369 | print('+') | ||
| 370 | |||
| 371 | |||
| 372 | -- error lines in stack overflow | ||
| 373 | C = 0 | ||
| 374 | local l1 | ||
| 375 | local function g(x) | ||
| 376 | l1 = debug.getinfo(x, "l").currentline; y() | ||
| 377 | end | ||
| 378 | local _, stackmsg = xpcall(g, debug.traceback, 1) | ||
| 379 | print('+') | ||
| 380 | local stack = {} | ||
| 381 | for line in string.gmatch(stackmsg, "[^\n]*") do | ||
| 382 | local curr = string.match(line, ":(%d+):") | ||
| 383 | if curr then table.insert(stack, tonumber(curr)) end | ||
| 384 | end | ||
| 385 | local i=1 | ||
| 386 | while stack[i] ~= l1 do | ||
| 387 | assert(stack[i] == l) | ||
| 388 | i = i+1 | ||
| 389 | end | ||
| 390 | assert(i > 15) | ||
| 391 | |||
| 392 | |||
| 393 | -- error in error handling | ||
| 394 | local res, msg = xpcall(error, error) | ||
| 395 | assert(not res and type(msg) == 'string') | ||
| 396 | print('+') | ||
| 397 | |||
| 398 | local function f (x) | ||
| 399 | if x==0 then error('a\n') | ||
| 400 | else | ||
| 401 | local aux = function () return f(x-1) end | ||
| 402 | local a,b = xpcall(aux, aux) | ||
| 403 | return a,b | ||
| 404 | end | ||
| 405 | end | ||
| 406 | f(3) | ||
| 407 | |||
| 408 | local function loop (x,y,z) return 1 + loop(x, y, z) end | ||
| 409 | |||
| 410 | local res, msg = xpcall(loop, function (m) | ||
| 411 | assert(string.find(m, "stack overflow")) | ||
| 412 | checkerr("error handling", loop) | ||
| 413 | assert(math.sin(0) == 0) | ||
| 414 | return 15 | ||
| 415 | end) | ||
| 416 | assert(msg == 15) | ||
| 417 | |||
| 418 | local f = function () | ||
| 419 | for i = 999900, 1000000, 1 do table.unpack({}, 1, i) end | ||
| 420 | end | ||
| 421 | checkerr("too many results", f) | ||
| 422 | |||
| 423 | end | ||
| 424 | |||
| 425 | |||
| 426 | do | ||
| 427 | -- non string messages | ||
| 428 | local t = {} | ||
| 429 | local res, msg = pcall(function () error(t) end) | ||
| 430 | assert(not res and msg == t) | ||
| 431 | |||
| 432 | res, msg = pcall(function () error(nil) end) | ||
| 433 | assert(not res and msg == nil) | ||
| 434 | |||
| 435 | local function f() error{msg='x'} end | ||
| 436 | res, msg = xpcall(f, function (r) return {msg=r.msg..'y'} end) | ||
| 437 | assert(msg.msg == 'xy') | ||
| 438 | |||
| 439 | -- 'assert' with extra arguments | ||
| 440 | res, msg = pcall(assert, false, "X", t) | ||
| 441 | assert(not res and msg == "X") | ||
| 442 | |||
| 443 | -- 'assert' with no message | ||
| 444 | res, msg = pcall(function () assert(false) end) | ||
| 445 | local line = string.match(msg, "%w+%.lua:(%d+): assertion failed!$") | ||
| 446 | assert(tonumber(line) == debug.getinfo(1, "l").currentline - 2) | ||
| 447 | |||
| 448 | -- 'assert' with non-string messages | ||
| 449 | res, msg = pcall(assert, false, t) | ||
| 450 | assert(not res and msg == t) | ||
| 451 | |||
| 452 | res, msg = pcall(assert, nil, nil) | ||
| 453 | assert(not res and msg == nil) | ||
| 454 | |||
| 455 | -- 'assert' without arguments | ||
| 456 | res, msg = pcall(assert) | ||
| 457 | assert(not res and string.find(msg, "value expected")) | ||
| 458 | end | ||
| 459 | |||
| 460 | -- xpcall with arguments | ||
| 461 | a, b, c = xpcall(string.find, error, "alo", "al") | ||
| 462 | assert(a and b == 1 and c == 2) | ||
| 463 | a, b, c = xpcall(string.find, function (x) return {} end, true, "al") | ||
| 464 | assert(not a and type(b) == "table" and c == nil) | ||
| 465 | |||
| 466 | |||
| 467 | print("testing tokens in error messages") | ||
| 468 | checksyntax("syntax error", "", "error", 1) | ||
| 469 | checksyntax("1.000", "", "1.000", 1) | ||
| 470 | checksyntax("[[a]]", "", "[[a]]", 1) | ||
| 471 | checksyntax("'aa'", "", "'aa'", 1) | ||
| 472 | checksyntax("while << do end", "", "<<", 1) | ||
| 473 | checksyntax("for >> do end", "", ">>", 1) | ||
| 474 | |||
| 475 | -- test invalid non-printable char in a chunk | ||
| 476 | checksyntax("a\1a = 1", "", "<\\1>", 1) | ||
| 477 | |||
| 478 | -- test 255 as first char in a chunk | ||
| 479 | checksyntax("\255a = 1", "", "<\\255>", 1) | ||
| 480 | |||
| 481 | doit('I = load("a=9+"); a=3') | ||
| 482 | assert(a==3 and I == nil) | ||
| 483 | print('+') | ||
| 484 | |||
| 485 | lim = 1000 | ||
| 486 | if _soft then lim = 100 end | ||
| 487 | for i=1,lim do | ||
| 488 | doit('a = ') | ||
| 489 | doit('a = 4+nil') | ||
| 490 | end | ||
| 491 | |||
| 492 | |||
| 493 | -- testing syntax limits | ||
| 494 | |||
| 495 | local function testrep (init, rep, close, repc) | ||
| 496 | local s = init .. string.rep(rep, 100) .. close .. string.rep(repc, 100) | ||
| 497 | assert(load(s)) -- 100 levels is OK | ||
| 498 | s = init .. string.rep(rep, 10000) | ||
| 499 | local res, msg = load(s) -- 10000 levels not ok | ||
| 500 | assert(not res and (string.find(msg, "too many registers") or | ||
| 501 | string.find(msg, "stack overflow"))) | ||
| 502 | end | ||
| 503 | |||
| 504 | testrep("local a; a", ",a", "= 1", ",1") -- multiple assignment | ||
| 505 | testrep("local a; a=", "{", "0", "}") | ||
| 506 | testrep("local a; a=", "(", "2", ")") | ||
| 507 | testrep("local a; ", "a(", "2", ")") | ||
| 508 | testrep("", "do ", "", " end") | ||
| 509 | testrep("", "while a do ", "", " end") | ||
| 510 | testrep("local a; ", "if a then else ", "", " end") | ||
| 511 | testrep("", "function foo () ", "", " end") | ||
| 512 | testrep("local a; a=", "a..", "a", "") | ||
| 513 | testrep("local a; a=", "a^", "a", "") | ||
| 514 | |||
| 515 | checkmessage("a = f(x" .. string.rep(",x", 260) .. ")", "too many registers") | ||
| 516 | |||
| 517 | |||
| 518 | -- testing other limits | ||
| 519 | |||
| 520 | -- upvalues | ||
| 521 | local lim = 127 | ||
| 522 | local s = "local function fooA ()\n local " | ||
| 523 | for j = 1,lim do | ||
| 524 | s = s.."a"..j..", " | ||
| 525 | end | ||
| 526 | s = s.."b,c\n" | ||
| 527 | s = s.."local function fooB ()\n local " | ||
| 528 | for j = 1,lim do | ||
| 529 | s = s.."b"..j..", " | ||
| 530 | end | ||
| 531 | s = s.."b\n" | ||
| 532 | s = s.."function fooC () return b+c" | ||
| 533 | local c = 1+2 | ||
| 534 | for j = 1,lim do | ||
| 535 | s = s.."+a"..j.."+b"..j | ||
| 536 | c = c + 2 | ||
| 537 | end | ||
| 538 | s = s.."\nend end end" | ||
| 539 | local a,b = load(s) | ||
| 540 | assert(c > 255 and string.find(b, "too many upvalues") and | ||
| 541 | string.find(b, "line 5")) | ||
| 542 | |||
| 543 | -- local variables | ||
| 544 | s = "\nfunction foo ()\n local " | ||
| 545 | for j = 1,300 do | ||
| 546 | s = s.."a"..j..", " | ||
| 547 | end | ||
| 548 | s = s.."b\n" | ||
| 549 | local a,b = load(s) | ||
| 550 | assert(string.find(b, "line 2") and string.find(b, "too many local variables")) | ||
| 551 | |||
| 552 | mt.__index = oldmm | ||
| 553 | |||
| 554 | print('OK') | ||
