luau/tests/conformance/basic.luau
2024-12-08 22:09:04 -05:00

2877 lines
46 KiB
Text

-- This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
print("testing language/library basics")
function concat(head, ...)
if select("#", ...) == 0 then
return tostring(head)
else
return tostring(head) .. "," .. concat(...)
end
end
-- constants
assert(tostring(1) == "1")
assert(tostring(-1) == "-1")
assert(tostring(1.125) == "1.125")
assert(tostring(true) == "true")
assert(tostring(nil) == "nil")
-- empty return
assert(select("#", (function() end)()) == 0)
assert(select(
"#",
(function()
return
end)()
) == 0)
-- locals
assert((function()
local a = 1
return a
end)() == 1)
assert((function()
local a, b, c = 1, 2, 3
return c
end)() == 3)
assert((function()
local a, b, c = 1, 2
return c
end)() == nil)
assert((function()
local a = 1, 2
return a
end)() == 1)
-- function calls
local function foo(a, b)
return b
end
assert(foo(1) == nil)
assert(foo(1, 2) == 2)
assert(foo(1, 2, 3) == 2)
-- pcall
assert(concat(pcall(function() end)) == "true")
assert(concat(pcall(function()
return nil
end)) == "true,nil")
assert(concat(pcall(function()
return 1, 2, 3
end)) == "true,1,2,3")
assert(concat(pcall(function()
error("oops")
end)) == "false,basic.luau:63: oops")
-- assignments
assert((function()
local a = 1
a = 2
return a
end)() == 2)
assert((function()
a = 1
a = 2
return a
end)() == 2)
assert((function()
local a = 1
a, b = 1
return a
end)() == 1)
assert((function()
local a = 1
a, b = 1
return b
end)() == nil)
assert((function()
local a = 1
b = 2
a, b = b, a
return a
end)() == 2)
assert((function()
local a = 1
b = 2
a, b = b, a
return b
end)() == 1)
assert((function()
_G.foo = 1
return _G["foo"]
end)() == 1)
assert((function()
_G["bar"] = 1
return _G.bar
end)() == 1)
assert((function()
local a = 1;
(function()
a = 2
end)()
return a
end)() == 2)
-- assignments with local conflicts
assert((function()
local a, b = 1, {}
a, b[a] = 43, -1
return a + b[1]
end)() == 42)
assert((function()
local a = {}
local b = a
a[1], a = 43, -1
return a + b[1]
end)() == 42)
assert((function()
local a, b = 1, {}
a, b[a] = (function()
return 43, -1
end)()
return a + b[1]
end)() == 42)
assert((function()
local a = {}
local b = a
a[1], a = (function()
return 43, -1
end)()
return a + b[1]
end)() == 42)
-- upvalues
assert((function()
local a = 1
function foo()
return a
end
return foo()
end)() == 1)
-- check upvalue propagation - foo must have numupvalues=1
assert((function()
local a = 1
function foo()
return function()
return a
end
end
return foo()()
end)() == 1)
-- check that function args are properly closed over
assert((function()
function foo(a)
return function()
return a
end
end
return foo(1)()
end)() == 1)
-- this checks local aliasing - b & a should share the same local slot, but the capture must return 1 instead of 2
assert((function()
function foo()
local f
do
local a = 1
f = function()
return a
end
end
local b = 2
return f
end
return foo()()
end)() == 1)
-- this checks local mutability - we capture a ref to 1 but must return 2
assert((function()
function foo()
local a = 1
local function f()
return a
end
a = 2
return f
end
return foo()()
end)() == 2)
-- this checks upval mutability - we change the value from a context where it's upval
assert((function()
function foo()
local a = 1;
(function()
a = 2
end)()
return a
end
return foo()
end)() == 2)
-- check self capture: does self go into any upvalues?
assert((function()
local t = { f = 5 }
function t:get()
return (function()
return self.f
end)()
end
return t:get()
end)() == 5)
-- check self capture & close: is self copied to upval?
assert((function()
function foo()
local t = { f = 5 }
function t:get()
return function()
return self.f
end
end
return t:get()
end
return foo()()
end)() == 5)
-- if
assert((function()
local a = 1
if a then
a = 2
end
return a
end)() == 2)
assert((function()
local a
if a then
a = 2
end
return a
end)() == nil)
assert((function()
local a = 0
if a then
a = 1
else
a = 2
end
return a
end)() == 1)
assert((function()
local a
if a then
a = 1
else
a = 2
end
return a
end)() == 2)
-- binary ops
assert((function()
local a = 1
a = a + 2
return a
end)() == 3)
assert((function()
local a = 1
a = a - 2
return a
end)() == -1)
assert((function()
local a = 1
a = a * 2
return a
end)() == 2)
assert((function()
local a = 1
a = a / 2
return a
end)() == 0.5)
-- binary ops with fp specials, neg zero, large constants
-- argument is passed into anonymous function to prevent constant folding
assert((function(a)
return tostring(a + 0)
end)(-0) == "0")
assert((function(a)
return tostring(a - 0)
end)(-0) == "-0")
assert((function(a)
return tostring(0 - a)
end)(0) == "0")
assert((function(a)
return tostring(a - a)
end)(1 / 0) == "nan")
assert((function(a)
return tostring(a * 0)
end)(0 / 0) == "nan")
assert((function(a)
return tostring(a / (2 ^ 1000))
end)(2 ^ 1000) == "1")
assert((function(a)
return tostring(a / (2 ^ -1000))
end)(2 ^ -1000) == "1")
-- floor division should always round towards -Infinity
assert((function()
local a = 1
a = a // 2
return a
end)() == 0)
assert((function()
local a = 3
a = a // 2
return a
end)() == 1)
assert((function()
local a = 3.5
a = a // 2
return a
end)() == 1)
assert((function()
local a = -1
a = a // 2
return a
end)() == -1)
assert((function()
local a = -3
a = a // 2
return a
end)() == -2)
assert((function()
local a = -3.5
a = a // 2
return a
end)() == -2)
assert((function()
local a = 5
a = a % 2
return a
end)() == 1)
assert((function()
local a = 3
a = a ^ 2
return a
end)() == 9)
assert((function()
local a = 3
a = a ^ 3
return a
end)() == 27)
assert((function()
local a = 9
a = a ^ 0.5
return a
end)() == 3)
assert((function()
local a = -2
a = a ^ 2
return a
end)() == 4)
assert((function()
local a = -2
a = a ^ 0.5
return tostring(a)
end)() == "nan")
assert((function()
local a = "1"
a = a .. "2"
return a
end)() == "12")
assert((function()
local a = "1"
a = a .. "2" .. "3"
return a
end)() == "123")
assert(concat(pcall(function()
return "1" .. nil .. "2"
end)):match("^false,.*attempt to concatenate nil with string"))
assert((function()
local a = 1
a = a == 2
return a
end)() == false)
assert((function()
local a = 1
a = a ~= 2
return a
end)() == true)
assert((function()
local a = 1
a = a < 2
return a
end)() == true)
assert((function()
local a = 1
a = a <= 2
return a
end)() == true)
assert((function()
local a = 1
a = a > 2
return a
end)() == false)
assert((function()
local a = 1
a = a >= 2
return a
end)() == false)
assert((function()
local a = 1
a = a and 2
return a
end)() == 2)
assert((function()
local a = nil
a = a and 2
return a
end)() == nil)
assert((function()
local a = 1
a = a or 2
return a
end)() == 1)
assert((function()
local a = nil
a = a or 2
return a
end)() == 2)
assert((function()
local a
a = 1
local b = 2
b = a and b
return b
end)() == 2)
assert((function()
local a
a = nil
local b = 2
b = a and b
return b
end)() == nil)
assert((function()
local a
a = 1
local b = 2
b = a or b
return b
end)() == 1)
assert((function()
local a
a = nil
local b = 2
b = a or b
return b
end)() == 2)
assert((function(a)
return 12 % a
end)(5) == 2)
-- binary arithmetics coerces strings to numbers (sadly)
assert(1 + "2" == 3)
assert(2 * "0xa" == 20)
-- unary ops
assert((function()
local a = true
a = not a
return a
end)() == false)
assert((function()
local a = false
a = not a
return a
end)() == true)
assert((function()
local a = nil
a = not a
return a
end)() == true)
assert((function()
return #_G
end)() == 0)
assert((function()
return #{ 1, 2 }
end)() == 2)
assert((function()
return #"g"
end)() == 1)
assert((function()
local a = 1
a = -a
return a
end)() == -1)
-- __len metamethod
assert((function()
local ud = newproxy(true)
getmetatable(ud).__len = function()
return 42
end
return #ud
end)() == 42)
assert((function()
local t = {}
setmetatable(t, {
__len = function()
return 42
end,
})
return #t
end)() == 42)
-- while/repeat
assert((function()
local a = 10
local b = 1
while a > 1 do
b = b * 2
a = a - 1
end
return b
end)() == 512)
assert((function()
local a = 10
local b = 1
repeat
b = b * 2
a = a - 1
until a == 1
return b
end)() == 512)
assert((function()
local a = 10
local b = 1
while true do
b = b * 2
a = a - 1
if a == 1 then
break
end
end
return b
end)() == 512)
assert((function()
local a = 10
local b = 1
while true do
b = b * 2
a = a - 1
if a == 1 then
break
else
end
end
return b
end)() == 512)
assert((function()
local a = 10
local b = 1
repeat
b = b * 2
a = a - 1
if a == 1 then
break
end
until false
return b
end)() == 512)
assert((function()
local a = 10
local b = 1
repeat
b = b * 2
a = a - 1
if a == 1 then
break
else
end
until false
return b
end)() == 512)
-- this makes sure a - 4 doesn't clobber a (which would happen if the lifetime of locals inside the repeat..until block is contained within
-- the block and ends before the condition is evaluated
assert((function()
repeat
local a = 5
until a - 4 < 0 or a - 4 >= 0
end)() == nil)
-- numeric for
-- basic tests with positive/negative step sizes
assert((function()
local a = 1
for b = 1, 9 do
a = a * 2
end
return a
end)() == 512)
assert((function()
local a = 1
for b = 1, 9, 2 do
a = a * 2
end
return a
end)() == 32)
assert((function()
local a = 1
for b = 1, 9, -2 do
a = a * 2
end
return a
end)() == 1)
assert((function()
local a = 1
for b = 9, 1, -2 do
a = a * 2
end
return a
end)() == 32)
-- make sure break works
assert((function()
local a = 1
for b = 1, 9 do
a = a * 2
if a == 128 then
break
end
end
return a
end)() == 128)
assert((function()
local a = 1
for b = 1, 9 do
a = a * 2
if a == 128 then
break
else
end
end
return a
end)() == 128)
-- make sure internal index is protected against modification
assert((function()
local a = 1
for b = 9, 1, -2 do
a = a * 2
b = nil
end
return a
end)() == 32)
-- make sure that when step is 0, we treat it as backward iteration (and as such, iterate zero times or indefinitely)
-- this is consistent with Lua 5.1; future Lua versions emit an error when step is 0; LuaJIT instead treats 0 as forward iteration
-- we repeat tests twice, with and without constant folding
local zero = tonumber("0")
assert((function()
local c = 0
for i = 1, 10, 0 do
c += 1
if c > 10 then
break
end
end
return c
end)() == 0)
assert((function()
local c = 0
for i = 10, 1, 0 do
c += 1
if c > 10 then
break
end
end
return c
end)() == 11)
assert((function()
local c = 0
for i = 1, 10, zero do
c += 1
if c > 10 then
break
end
end
return c
end)() == 0)
assert((function()
local c = 0
for i = 10, 1, zero do
c += 1
if c > 10 then
break
end
end
return c
end)() == 11)
-- make sure that when limit is nan, we iterate zero times (this is consistent with Lua 5.1; future Lua versions break this)
-- we repeat tests twice, with and without constant folding
local nan = tonumber("nan")
assert((function()
local c = 0
for i = 1, 0 / 0 do
c += 1
end
return c
end)() == 0)
assert((function()
local c = 0
for i = 1, 0 / 0, -1 do
c += 1
end
return c
end)() == 0)
assert((function()
local c = 0
for i = 1, nan do
c += 1
end
return c
end)() == 0)
assert((function()
local c = 0
for i = 1, nan, -1 do
c += 1
end
return c
end)() == 0)
-- make sure that when step is nan, we treat it as backward iteration and as such iterate once iff start<=limit
assert((function()
local c = 0
for i = 1, 10, 0 / 0 do
c += 1
end
return c
end)() == 0)
assert((function()
local c = 0
for i = 10, 1, 0 / 0 do
c += 1
end
return c
end)() == 1)
assert((function()
local c = 0
for i = 1, 10, nan do
c += 1
end
return c
end)() == 0)
assert((function()
local c = 0
for i = 10, 1, nan do
c += 1
end
return c
end)() == 1)
-- make sure that when index becomes nan mid-iteration, we correctly exit the loop (this is broken in Lua 5.1; future Lua versions fix this)
assert((function()
local c = 0
for i = -math.huge, 0, math.huge do
c += 1
end
return c
end)() == 1)
assert((function()
local c = 0
for i = math.huge, math.huge, -math.huge do
c += 1
end
return c
end)() == 1)
-- generic for
-- ipairs
assert((function()
local a = ""
for k in ipairs({ 5, 6, 7 }) do
a = a .. k
end
return a
end)() == "123")
assert((function()
local a = ""
for k, v in ipairs({ 5, 6, 7 }) do
a = a .. k
end
return a
end)() == "123")
assert((function()
local a = ""
for k, v in ipairs({ 5, 6, 7 }) do
a = a .. v
end
return a
end)() == "567")
-- ipairs with gaps
assert((function()
local a = ""
for k in ipairs({ 5, 6, 7, nil, 8 }) do
a = a .. k
end
return a
end)() == "123")
assert((function()
local a = ""
for k, v in ipairs({ 5, 6, 7, nil, 8 }) do
a = a .. k
end
return a
end)() == "123")
assert((function()
local a = ""
for k, v in ipairs({ 5, 6, 7, nil, 8 }) do
a = a .. v
end
return a
end)() == "567")
-- manual ipairs/inext
local inext = ipairs({ 5, 6, 7 })
assert(concat(inext({ 5, 6, 7 }, 2)) == "3,7")
-- pairs on array
assert((function()
local a = ""
for k in pairs({ 5, 6, 7 }) do
a = a .. k
end
return a
end)() == "123")
assert((function()
local a = ""
for k, v in pairs({ 5, 6, 7 }) do
a = a .. k
end
return a
end)() == "123")
assert((function()
local a = ""
for k, v in pairs({ 5, 6, 7 }) do
a = a .. v
end
return a
end)() == "567")
-- pairs on array with gaps
assert((function()
local a = ""
for k in pairs({ 5, 6, 7, nil, 8 }) do
a = a .. k
end
return a
end)() == "1235")
assert((function()
local a = ""
for k, v in pairs({ 5, 6, 7, nil, 8 }) do
a = a .. k
end
return a
end)() == "1235")
assert((function()
local a = ""
for k, v in pairs({ 5, 6, 7, nil, 8 }) do
a = a .. v
end
return a
end)() == "5678")
-- pairs on table
assert((function()
local a = {}
for k in pairs({ a = 1, b = 2, c = 3 }) do
a[k] = 1
end
return a.a + a.b + a.c
end)() == 3)
assert((function()
local a = {}
for k, v in pairs({ a = 1, b = 2, c = 3 }) do
a[k] = 1
end
return a.a + a.b + a.c
end)() == 3)
assert((function()
local a = {}
for k, v in pairs({ a = 1, b = 2, c = 3 }) do
a[k] = v
end
return a.a + a.b + a.c
end)() == 6)
-- pairs on mixed array/table + gaps in the array portion
-- note that a,b,c results in a,c,b during traversal since index is based on hash & size
assert((function()
local a = {}
for k, v in pairs({ 1, 2, 3, a = 5, b = 6, c = 7 }) do
a[#a + 1] = v
end
return table.concat(a, ",")
end)() == "1,2,3,5,7,6")
assert((function()
local a = {}
for k, v in pairs({ 1, 2, 3, nil, 4, a = 5, b = 6, c = 7 }) do
a[#a + 1] = v
end
return table.concat(a, ",")
end)() == "1,2,3,4,5,7,6")
-- pairs manually
assert((function()
local a = ""
for k in next, { 5, 6, 7 } do
a = a .. k
end
return a
end)() == "123")
assert((function()
local a = ""
for k, v in next, { 5, 6, 7 } do
a = a .. k
end
return a
end)() == "123")
assert((function()
local a = ""
for k, v in next, { 5, 6, 7 } do
a = a .. v
end
return a
end)() == "567")
assert((function()
local a = {}
for k in next, { a = 1, b = 2, c = 3 } do
a[k] = 1
end
return a.a + a.b + a.c
end)() == 3)
assert((function()
local a = {}
for k, v in next, { a = 1, b = 2, c = 3 } do
a[k] = 1
end
return a.a + a.b + a.c
end)() == 3)
assert((function()
local a = {}
for k, v in next, { a = 1, b = 2, c = 3 } do
a[k] = v
end
return a.a + a.b + a.c
end)() == 6)
-- too many vars
assert((function()
local a = ""
for k, v, p in pairs({ a = 1, b = 2, c = 3 }) do
a = a .. tostring(p)
end
return a
end)() == "nilnilnil")
-- make sure break works
assert((function()
local a = 1
for _ in pairs({ 1, 2, 3 }) do
a = a * 2
if a == 4 then
break
end
end
return a
end)() == 4)
assert((function()
local a = 1
for _ in pairs({ 1, 2, 3 }) do
a = a * 2
if a == 4 then
break
else
end
end
return a
end)() == 4)
-- make sure internal index is protected against modification
assert((function()
local a = 1
for b in pairs({ 1, 2, 3 }) do
a = a * 2
b = nil
end
return a
end)() == 8)
-- make sure custom iterators work! example is from PIL 7.1
function list_iter(t)
local i = 0
local n = table.getn(t)
return function()
i = i + 1
if i <= n then
return t[i]
end
end
end
assert((function()
local a = ""
for e in list_iter({ 4, 2, 1 }) do
a = a .. e
end
return a
end)() == "421")
-- make sure multret works in context of pairs() - this is a very painful to handle combination due to complex internal details
assert((function()
local function f()
return { 5, 6, 7 }, 8, 9, 0
end
local a = ""
for k, v in ipairs(f()) do
a = a .. v
end
return a
end)() == "567")
-- table literals
-- basic tests
assert((function()
local t = {}
return #t
end)() == 0)
assert((function()
local t = { 1, 2 }
return #t
end)() == 2)
assert((function()
local t = { 1, 2 }
return t[1] + t[2]
end)() == 3)
assert((function()
local t = { data = 4 }
return t.data
end)() == 4)
assert((function()
local t = { [1 + 2] = 4 }
return t[3]
end)() == 4)
assert((function()
local t = { data = 4, [1 + 2] = 5 }
return t.data + t[3]
end)() == 9)
assert((function()
local t = { [1] = 1, [2] = 2 }
return t[1] + t[2]
end)() == 3)
-- since table ctor is chunked in groups of 16, we should be careful with edge cases around that
assert((function()
return table.concat({}, ",")
end)() == "")
assert((function()
return table.concat({ 1 }, ",")
end)() == "1")
assert((function()
return table.concat({ 1, 2 }, ",")
end)() == "1,2")
assert((function()
return table.concat({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, ",")
end)() == "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15")
assert((function()
return table.concat({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, ",")
end)() == "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16")
assert((function()
return table.concat({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }, ",")
end)() == "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17")
-- some scripts rely on exact table traversal order; while it's evil to do so, let's check that it works
assert((function()
local kSelectedBiomes = {
["Mountains"] = true,
["Canyons"] = true,
["Dunes"] = true,
["Arctic"] = true,
["Lavaflow"] = true,
["Hills"] = true,
["Plains"] = true,
["Marsh"] = true,
["Water"] = true,
}
local result = ""
for k in pairs(kSelectedBiomes) do
result = result .. k
end
return result
end)() == "ArcticDunesCanyonsWaterMountainsHillsLavaflowPlainsMarsh")
-- table literals may contain duplicate fields; the language doesn't specify assignment order but we currently assign left to right
assert((function()
local t = { data = 4, data = nil, data = 42 }
return t.data
end)() == 42)
assert((function()
local t = { data = 4, data = nil, data = 42, data = nil }
return t.data
end)() == nil)
-- multiple returns
-- local=
assert((function()
function foo()
return 2, 3, 4
end
local a, b, c = foo()
return "" .. a .. b .. c
end)() == "234")
assert((function()
function foo()
return 2, 3, 4
end
local a, b, c = 1, foo()
return "" .. a .. b .. c
end)() == "123")
assert((function()
function foo()
return 2
end
local a, b, c = 1, foo()
return "" .. a .. b .. tostring(c)
end)() == "12nil")
-- assignments
assert((function()
function foo()
return 2, 3
end
a, b, c, d = 1, foo()
return "" .. a .. b .. c .. tostring(d)
end)() == "123nil")
assert((function()
function foo()
return 2, 3
end
local a, b, c, d
a, b, c, d = 1, foo()
return "" .. a .. b .. c .. tostring(d)
end)() == "123nil")
-- varargs
-- local=
assert((function()
function foo(...)
local a, b, c = ...
return a + b + c
end
return foo(1, 2, 3)
end)() == 6)
assert((function()
function foo(x, ...)
local a, b, c = ...
return a + b + c
end
return foo(1, 2, 3, 4)
end)() == 9)
-- assignments
assert((function()
function foo(...)
a, b, c = ...
return a + b + c
end
return foo(1, 2, 3)
end)() == 6)
assert((function()
function foo(x, ...)
a, b, c = ...
return a + b + c
end
return foo(1, 2, 3, 4)
end)() == 9)
-- extra nils
assert((function()
function foo(...)
local a, b, c = ...
return tostring(a) .. tostring(b) .. tostring(c)
end
return foo(1, 2)
end)() == "12nil")
-- varargs + multiple returns
-- return
assert((function()
function foo(...)
return ...
end
return concat(foo(1, 2, 3))
end)() == "1,2,3")
assert((function()
function foo(...)
return ...
end
return foo()
end)() == nil)
assert((function()
function foo(a, ...)
return a + 10, ...
end
return concat(foo(1, 2, 3))
end)() == "11,2,3")
-- call
assert((function()
function foo(...)
return ...
end
function bar(...)
return foo(...)
end
return concat(bar(1, 2, 3))
end)() == "1,2,3")
assert((function()
function foo(...)
return ...
end
function bar(...)
return foo(...)
end
return bar()
end)() == nil)
assert((function()
function foo(a, ...)
return a + 10, ...
end
function bar(a, ...)
return foo(a * 2, ...)
end
return concat(bar(1, 2, 3))
end)() == "12,2,3")
-- manual pack
assert((function()
function pack(first, ...)
if not first then
return {}
end
local t = pack(...)
table.insert(t, 1, first)
return t
end
function foo(...)
return pack(...)
end
return #foo(0, 1, 2)
end)() == 3)
-- multret + table literals
-- basic tests
assert((function()
function foo(...)
return { ... }
end
return #(foo())
end)() == 0)
assert((function()
function foo(...)
return { ... }
end
return #(foo(1, 2, 3))
end)() == 3)
assert((function()
function foo()
return 1, 2, 3
end
return #{ foo() }
end)() == 3)
-- since table ctor is chunked in groups of 16, we should be careful with edge cases around that
assert((function()
function foo()
return 1, 2, 3
end
return table.concat({ foo() }, ",")
end)() == "1,2,3")
assert((function()
function foo()
return 1, 2, 3
end
return table.concat({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, foo() }, ",")
end)() == "1,2,3,4,5,6,7,8,9,10,11,12,13,14,1,2,3")
assert((function()
function foo()
return 1, 2, 3
end
return table.concat({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, foo() }, ",")
end)() == "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1,2,3")
assert((function()
function foo()
return 1, 2, 3
end
return table.concat({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, foo() }, ",")
end)() == "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,1,2,3")
assert((function()
function foo()
return 1, 2, 3
end
return table.concat({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, foo() }, ",")
end)() == "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,1,2,3")
-- table access
assert((function()
local t = { 6, 9, 7 }
return t[2]
end)() == 9)
assert((function()
local t = { 6, 9, 7 }
return t[0]
end)() == nil)
assert((function()
local t = { 6, 9, 7 }
return t[4]
end)() == nil)
assert((function()
local t = { 6, 9, 7 }
return t[4.5]
end)() == nil)
assert((function()
local t = { 6, 9, 7, [4.5] = 11 }
return t[4.5]
end)() == 11)
assert((function()
local t = { 6, 9, 7, a = 11 }
return t["a"]
end)() == 11)
assert((function()
local t = { 6, 9, 7 }
setmetatable(t, {
__index = function(t, i)
return i * 10
end,
})
return concat(t[2], t[5])
end)() == "9,50")
assert((function()
local t = { 6, 9, 7 }
t[2] = 10
return t[2]
end)() == 10)
assert((function()
local t = { 6, 9, 7 }
t[0] = 5
return t[0]
end)() == 5)
assert((function()
local t = { 6, 9, 7 }
t[4] = 10
return t[4]
end)() == 10)
assert((function()
local t = { 6, 9, 7 }
t[4.5] = 10
return t[4.5]
end)() == 10)
assert((function()
local t = { 6, 9, 7 }
t["a"] = 11
return t["a"]
end)() == 11)
assert((function()
local t = { 6, 9, 7 }
setmetatable(t, {
__newindex = function(t, i, v)
rawset(t, i * 10, v)
end,
})
t[1] = 17
t[5] = 1
return concat(t[1], t[5], t[50])
end)() == "17,nil,1")
-- userdata access
assert((function()
local ud = newproxy(true)
getmetatable(ud).__index = function(ud, i)
return i * 10
end
return ud[2]
end)() == 20)
assert((function()
local ud = newproxy(true)
getmetatable(ud).__index = function()
return function(self, i)
return i * 10
end
end
return ud:meow(2)
end)() == 20)
-- and/or
-- rhs is a constant
assert((function()
local a = 1
a = a and 2
return a
end)() == 2)
assert((function()
local a = nil
a = a and 2
return a
end)() == nil)
assert((function()
local a = 1
a = a or 2
return a
end)() == 1)
assert((function()
local a = nil
a = a or 2
return a
end)() == 2)
-- rhs is a local
assert((function()
local a = 1
local b = 2
a = a and b
return a
end)() == 2)
assert((function()
local a = nil
local b = 2
a = a and b
return a
end)() == nil)
assert((function()
local a = 1
local b = 2
a = a or b
return a
end)() == 1)
assert((function()
local a = nil
local b = 2
a = a or b
return a
end)() == 2)
-- rhs is a global (prevents optimizations)
assert((function()
local a = 1
b = 2
a = a and b
return a
end)() == 2)
assert((function()
local a = nil
b = 2
a = a and b
return a
end)() == nil)
assert((function()
local a = 1
b = 2
a = a or b
return a
end)() == 1)
assert((function()
local a = nil
b = 2
a = a or b
return a
end)() == 2)
-- table access: method calls + fake oop via mt
assert((function()
local Class = {}
Class.__index = Class
function Class.new()
local self = {}
setmetatable(self, Class)
self.field = 5
return self
end
function Class:GetField()
return self.field
end
local object = Class.new()
return object:GetField()
end)() == 5)
-- table access: evil indexer
assert((function()
local a = { 5 }
local b = { 6 }
local mt = {
__index = function()
return b[1]
end,
}
setmetatable(a, mt)
b = a.hi
return b
end)() == 6)
-- table access: fast-path tests for array lookup
-- in-bounds array lookup shouldn't call into Lua, but if the element isn't there we'll still call the metatable
assert((function()
local a = { 9, [1.5] = 7 }
return a[1], a[2], a[1.5]
end)() == 9, nil, 7)
assert((function()
local a = { 9, [1.5] = 7 }
setmetatable(a, {
__index = function()
return 5
end,
})
return concat(a[1], a[2], a[1.5])
end)() == "9,5,7")
assert((function()
local a = { 9, nil, 11 }
setmetatable(a, {
__index = function()
return 5
end,
})
return concat(a[1], a[2], a[3], a[4])
end)() == "9,5,11,5")
-- namecall for userdata: technically not officially supported but hard to test in a different way!
-- warning: this test may break at any time as we may decide that we'll only use userdata-namecall on tagged user data objects
assert((function()
local obj = newproxy(true)
getmetatable(obj).__namecall = function(self, arg)
return 42 + arg
end
return obj:Foo(10)
end)() == 52)
assert((function()
local obj = newproxy(true)
local t = {}
setmetatable(t, {
__call = function(self1, self2, arg)
return 42 + arg
end,
})
getmetatable(obj).__namecall = t
return obj:Foo(10)
end)() == 52)
-- namecall for oop to test fast paths
assert((function()
local Class = {}
Class.__index = Class
function Class:new(klass, v) -- note, this isn't necessarily common but it exercises additional namecall paths
local self = { value = v }
setmetatable(self, Class)
return self
end
function Class:get()
return self.value
end
function Class:set(v)
self.value = v
end
local n = Class:new(32)
n:set(42)
return n:get()
end)() == 42)
-- comparison
-- basic types
assert((function()
a = nil
return concat(a == nil, a ~= nil)
end)() == "true,false")
assert((function()
a = nil
return concat(a == 1, a ~= 1)
end)() == "false,true")
assert((function()
a = 1
return concat(a == 1, a ~= 1)
end)() == "true,false")
assert((function()
a = 1
return concat(a == 2, a ~= 2)
end)() == "false,true")
assert((function()
a = true
return concat(a == true, a ~= true)
end)() == "true,false")
assert((function()
a = true
return concat(a == false, a ~= false)
end)() == "false,true")
assert((function()
a = "a"
return concat(a == "a", a ~= "a")
end)() == "true,false")
assert((function()
a = "a"
return concat(a == "b", a ~= "b")
end)() == "false,true")
-- tables, reference equality (no mt)
assert((function()
a = {}
return concat(a == a, a ~= a)
end)() == "true,false")
assert((function()
a = {}
b = {}
return concat(a == b, a ~= b)
end)() == "false,true")
-- tables, reference equality (mt without __eq)
assert((function()
a = {}
setmetatable(a, {})
return concat(a == a, a ~= a)
end)() == "true,false")
assert((function()
a = {}
b = {}
mt = {}
setmetatable(a, mt)
setmetatable(b, mt)
return concat(a == b, a ~= b)
end)() == "false,true")
-- tables, __eq with same mt/different mt but same function/different function
assert((function()
a = {}
b = {}
mt = {
__eq = function(l, r)
return #l == #r
end,
}
setmetatable(a, mt)
setmetatable(b, mt)
return concat(a == b, a ~= b)
end)() == "true,false")
assert((function()
a = {}
b = {}
function eq(l, r)
return #l == #r
end
setmetatable(a, { __eq = eq })
setmetatable(b, { __eq = eq })
return concat(a == b, a ~= b)
end)() == "true,false")
assert((function()
a = {}
b = {}
setmetatable(a, {
__eq = function(l, r)
return #l == #r
end,
})
setmetatable(b, {
__eq = function(l, r)
return #l == #r
end,
})
return concat(a == b, a ~= b)
end)() == "false,true")
-- userdata, reference equality (no mt or mt.__eq)
assert((function()
a = newproxy()
return concat(a == newproxy(), a ~= newproxy())
end)() == "false,true")
assert((function()
a = newproxy(true)
return concat(a == newproxy(true), a ~= newproxy(true))
end)() == "false,true")
-- rawequal
assert(rawequal(true, 5) == false)
assert(rawequal(nil, nil) == true)
assert(rawequal(true, false) == false)
assert(rawequal(true, true) == true)
assert(rawequal(0, -0) == true)
assert(rawequal(1, 2) == false)
assert(rawequal("a", "a") == true)
assert(rawequal("a", "b") == false)
assert((function()
a = {}
b = {}
mt = {
__eq = function(l, r)
return #l == #r
end,
}
setmetatable(a, mt)
setmetatable(b, mt)
return concat(a == b, rawequal(a, b))
end)() == "true,false")
-- rawequal fallback
assert(concat(pcall(rawequal, "a", "a")) == "true,true")
assert(concat(pcall(rawequal, "a", "b")) == "true,false")
assert(concat(pcall(rawequal, "a", nil)) == "true,false")
assert(pcall(rawequal, "a") == false)
-- metatable ops
local function vec3t(x, y, z)
return setmetatable({ x = x, y = y, z = z }, {
__add = function(l, r)
return vec3t(l.x + r.x, l.y + r.y, l.z + r.z)
end,
__sub = function(l, r)
return vec3t(l.x - r.x, l.y - r.y, l.z - r.z)
end,
__mul = function(l, r)
return type(r) == "number" and vec3t(l.x * r, l.y * r, l.z * r) or vec3t(l.x * r.x, l.y * r.y, l.z * r.z)
end,
__div = function(l, r)
return type(r) == "number" and vec3t(l.x / r, l.y / r, l.z / r) or vec3t(l.x / r.x, l.y / r.y, l.z / r.z)
end,
__idiv = function(l, r)
return type(r) == "number" and vec3t(l.x // r, l.y // r, l.z // r)
or vec3t(l.x // r.x, l.y // r.y, l.z // r.z)
end,
__unm = function(v)
return vec3t(-v.x, -v.y, -v.z)
end,
__tostring = function(v)
return string.format("%g, %g, %g", v.x, v.y, v.z)
end,
})
end
-- reg vs reg
assert((function()
return tostring(vec3t(1, 2, 3) + vec3t(4, 5, 6))
end)() == "5, 7, 9")
assert((function()
return tostring(vec3t(1, 2, 3) - vec3t(4, 5, 6))
end)() == "-3, -3, -3")
assert((function()
return tostring(vec3t(1, 2, 3) * vec3t(4, 5, 6))
end)() == "4, 10, 18")
assert((function()
return tostring(vec3t(1, 2, 3) / vec3t(2, 4, 8))
end)() == "0.5, 0.5, 0.375")
assert((function()
return tostring(vec3t(1, 2, 3) // vec3t(2, 4, 2))
end)() == "0, 0, 1")
assert((function()
return tostring(vec3t(1, 2, 3) // vec3t(-2, -4, -2))
end)() == "-1, -1, -2")
-- reg vs constant
assert((function()
return tostring(vec3t(1, 2, 3) * 2)
end)() == "2, 4, 6")
assert((function()
return tostring(vec3t(1, 2, 3) / 2)
end)() == "0.5, 1, 1.5")
assert((function()
return tostring(vec3t(1, 2, 3) // 2)
end)() == "0, 1, 1")
-- unary
assert((function()
return tostring(-vec3t(1, 2, 3))
end)() == "-1, -2, -3")
-- string comparison
assert((function()
function cmp(a, b)
return a < b, a <= b, a > b, a >= b
end
return concat(cmp("a", "b"))
end)() == "true,true,false,false")
assert((function()
function cmp(a, b)
return a < b, a <= b, a > b, a >= b
end
return concat(cmp("a", "a"))
end)() == "false,true,false,true")
assert((function()
function cmp(a, b)
return a < b, a <= b, a > b, a >= b
end
return concat(cmp("a", ""))
end)() == "false,false,true,true")
assert((function()
function cmp(a, b)
return a < b, a <= b, a > b, a >= b
end
return concat(cmp("", "\\0"))
end)() == "true,true,false,false")
assert((function()
function cmp(a, b)
return a < b, a <= b, a > b, a >= b
end
return concat(cmp("abc", "abd"))
end)() == "true,true,false,false")
assert((function()
function cmp(a, b)
return a < b, a <= b, a > b, a >= b
end
return concat(cmp("ab\\0c", "ab\\0d"))
end)() == "true,true,false,false")
assert((function()
function cmp(a, b)
return a < b, a <= b, a > b, a >= b
end
return concat(cmp("ab\\0c", "ab\\0"))
end)() == "false,false,true,true")
assert((function()
function cmp(a, b)
return a < b, a <= b, a > b, a >= b
end
return concat(cmp("\\0a", "\\0b"))
end)() == "true,true,false,false")
assert((function()
function cmp(a, b)
return a < b, a <= b, a > b, a >= b
end
return concat(cmp("a", "a\\0"))
end)() == "true,true,false,false")
assert((function()
function cmp(a, b)
return a < b, a <= b, a > b, a >= b
end
return concat(cmp("a", "\200"))
end)() == "true,true,false,false")
-- array access
assert((function()
local a = { 4, 5, 6 }
return a[3]
end)() == 6)
assert((function()
local a = { 4, 5, nil, 6 }
return a[3]
end)() == nil)
assert((function()
local a = { 4, 5, nil, 6 }
setmetatable(a, {
__index = function()
return 42
end,
})
return a[4]
end)() == 6)
assert((function()
local a = { 4, 5, nil, 6 }
setmetatable(a, {
__index = function()
return 42
end,
})
return a[3]
end)() == 42)
assert((function()
local a = { 4, 5, 6 }
a[3] = 8
return a[3]
end)() == 8)
assert((function()
local a = { 4, 5, nil, 6 }
a[3] = 8
return a[3]
end)() == 8)
assert((function()
local a = { 4, 5, nil, 6 }
setmetatable(a, {
__newindex = function(t, i)
rawset(t, i, 42)
end,
})
a[4] = 0
return a[4]
end)() == 0)
assert((function()
local a = { 4, 5, nil, 6 }
setmetatable(a, {
__newindex = function(t, i)
rawset(t, i, 42)
end,
})
a[3] = 0
return a[3]
end)() == 42)
-- array index for literal
assert((function()
local a = { 4, 5, nil, 6 }
return concat(a[1], a[3], a[4], a[100])
end)() == "4,nil,6,nil")
assert((function()
local a = { 4, 5, nil, 6 }
a[1] = 42
a[3] = 0
a[100] = 75
return concat(a[1], a[3], a[75], a[100])
end)() == "42,0,nil,75")
-- load error
assert((function()
return concat(loadstring("hello world"))
end)() == 'nil,[string "hello world"]:1: Incomplete statement: expected assignment or a function call')
-- many arguments & locals
function f(
p1,
p2,
p3,
p4,
p5,
p6,
p7,
p8,
p9,
p10,
p11,
p12,
p13,
p14,
p15,
p16,
p17,
p18,
p19,
p20,
p21,
p22,
p23,
p24,
p25,
p26,
p27,
p28,
p29,
p30,
p31,
p32,
p33,
p34,
p35,
p36,
p37,
p38,
p39,
p40,
p41,
p42,
p43,
p44,
p45,
p46,
p48,
p49,
p50,
...
)
local a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14
end
assert(f() == nil)
-- upvalues & loops (validates timely closing)
assert((function()
local res = {}
for i = 1, 5 do
res[#res + 1] = function()
return i
end
end
local sum = 0
for i, f in pairs(res) do
sum = sum + f()
end
return sum
end)() == 15)
assert((function()
local res = {}
for i in ipairs({ 1, 2, 3, 4, 5 }) do
res[#res + 1] = function()
return i
end
end
local sum = 0
for i, f in pairs(res) do
sum = sum + f()
end
return sum
end)() == 15)
assert((function()
local res = {}
local i = 0
while i <= 5 do
local j = i
res[#res + 1] = function()
return j
end
i = i + 1
end
local sum = 0
for i, f in pairs(res) do
sum = sum + f()
end
return sum
end)() == 15)
assert((function()
local res = {}
local i = 0
repeat
local j = i
res[#res + 1] = function()
return j
end
i = i + 1
until i > 5
local sum = 0
for i, f in pairs(res) do
sum = sum + f()
end
return sum
end)() == 15)
-- upvalues & loops & break!
assert((function()
local res = {}
for i = 1, 10 do
res[#res + 1] = function()
return i
end
if i == 5 then
break
end
end
local sum = 0
for i, f in pairs(res) do
sum = sum + f()
end
return sum
end)() == 15)
assert((function()
local res = {}
for i in ipairs({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }) do
res[#res + 1] = function()
return i
end
if i == 5 then
break
end
end
local sum = 0
for i, f in pairs(res) do
sum = sum + f()
end
return sum
end)() == 15)
assert((function()
local res = {}
local i = 0
while i < 10 do
local j = i
res[#res + 1] = function()
return j
end
if i == 5 then
break
end
i = i + 1
end
local sum = 0
for i, f in pairs(res) do
sum = sum + f()
end
return sum
end)() == 15)
assert((function()
local res = {}
local i = 0
repeat
local j = i
res[#res + 1] = function()
return j
end
if i == 5 then
break
end
i = i + 1
until i >= 10
local sum = 0
for i, f in pairs(res) do
sum = sum + f()
end
return sum
end)() == 15)
-- ipairs will not iterate through hash part
assert((function()
local arr = { [1] = 1, [42] = 42, x = 10 }
local sum = 0
for i, v in ipairs(arr) do
sum = sum + v
end
return sum
end)() == 1)
-- the reason why this test is interesting is it ensures we do correct mutability analysis for locals
local function chainTest(n)
local first = nil
local last = nil
-- Build chain of n equality constraints
for i = 0, n do
local name = "v" .. i
if i == 0 then
first = name
end
if i == n then
last = name
end
end
return concat(first, last)
end
assert(chainTest(100) == "v0,v100")
-- this validates import fallbacks
assert(idontexist == nil)
assert(math.idontexist == nil)
assert(pcall(function()
return idontexist.a
end) == false)
assert(pcall(function()
return math.pow.a
end) == false)
assert(pcall(function()
return math.a.b
end) == false)
-- make sure that NaN is preserved by the bytecode compiler
local realnan = tostring(math.abs(0) / math.abs(0))
assert(tostring(0 / 0 * 0) == realnan)
assert(tostring((-1) ^ (1 / 2)) == realnan)
-- make sure that negative zero is preserved by bytecode compiler
assert(tostring(0) == "0")
assert(tostring(-0) == "-0")
-- test newline handling in long strings
assert((function()
local s1 = [[
]]
local s2 = [[
]]
local s3 = [[
foo
bar]]
local s4 = [[
foo
bar
]]
return concat(s1, s2, s3, s4)
end)() == ",\n,foo\nbar,foo\nbar\n")
-- fastcall
-- positive tests for all simple examples; note that in this case the call is a multret call (nresults=LUA_MULTRET)
assert((function()
return math.abs(-5)
end)() == 5)
assert((function()
local abs = math.abs
return abs(-5)
end)() == 5)
assert((function()
local abs = math.abs
function foo()
return abs(-5)
end
return foo()
end)() == 5)
-- vararg testing - in this case nparams = LUA_MULTRET and it gets adjusted before execution
assert((function()
function foo(...)
return math.abs(...)
end
return foo(-5)
end)() == 5)
assert((function()
function foo(...)
local abs = math.abs
return abs(...)
end
return foo(-5)
end)() == 5)
assert((function()
local abs = math.abs
function foo(...)
return abs(...)
end
return foo(-5)
end)() == 5)
-- NOTE: getfenv breaks fastcalls for the remainder of the source! hence why this is delayed until the end
function testgetfenv()
getfenv()
-- declare constant so that at O2 this test doesn't interfere with constant folding which we can't deoptimize
local negfive
negfive = -5
-- getfenv breaks fastcalls (we assume we can't rely on knowing the semantics), but behavior shouldn't change
assert((function()
return math.abs(negfive)
end)() == 5)
assert((function()
local abs = math.abs
return abs(negfive)
end)() == 5)
assert((function()
local abs = math.abs
function foo()
return abs(negfive)
end
return foo()
end)() == 5)
-- ... unless you actually reassign the function :D
getfenv().math = {
abs = function(n)
return n * n
end,
}
assert((function()
return math.abs(negfive)
end)() == 25)
assert((function()
local abs = math.abs
return abs(negfive)
end)() == 25)
assert((function()
local abs = math.abs
function foo()
return abs(negfive)
end
return foo()
end)() == 25)
end
-- you need to have enough arguments and arguments of the right type; if you don't, we'll fallback to the regular code. This checks coercions
-- first to make sure all fallback paths work
assert((function()
return math.abs("-5")
end)() == 5)
assert((function()
local abs = math.abs
return abs("-5")
end)() == 5)
assert((function()
local abs = math.abs
function foo()
return abs("-5")
end
return foo()
end)() == 5)
-- if you don't have enough arguments or types are wrong, we fall back to the regular execution; this checks that the error generated is actually correct
assert(concat(pcall(function()
return math.abs()
end)):match("missing argument #1 to 'abs'"))
assert(concat(pcall(function()
return math.abs(nil)
end)):match("invalid argument #1 to 'abs'"))
assert(concat(pcall(function()
return math.abs({})
end)):match("invalid argument #1 to 'abs'"))
-- very large unpack
assert(
select(
"#",
table.unpack({
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
})
) == 263
)
-- basic continue in for/while/repeat loops
assert((function()
local a = 1
for i = 1, 8 do
a = a + 1
if a < 5 then
continue
end
a = a * 2
end
return a
end)() == 190)
assert((function()
local a = 1
while a < 100 do
a = a + 1
if a < 5 then
continue
end
a = a * 2
end
return a
end)() == 190)
assert((function()
local a = 1
repeat
a = a + 1
if a < 5 then
continue
end
a = a * 2
until a > 100
return a
end)() == 190)
-- upvalues, loops, continue
assert((function()
local res = {}
for i = 1, 10 do
res[#res + 1] = function()
return i
end
if i == 5 then
continue
end
i = i * 2
end
local sum = 0
for i, f in pairs(res) do
sum = sum + f()
end
return sum
end)() == 105)
assert((function()
local res = {}
for i in ipairs({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }) do
res[#res + 1] = function()
return i
end
if i == 5 then
continue
end
i = i * 2
end
local sum = 0
for i, f in pairs(res) do
sum = sum + f()
end
return sum
end)() == 105)
assert((function()
local res = {}
local i = 1
while i <= 10 do
local j = i
res[#res + 1] = function()
return j
end
if i == 5 then
i = i + 1
continue
end
i = i + 1
j = j * 2
end
local sum = 0
for i, f in pairs(res) do
sum = sum + f()
end
return sum
end)() == 105)
assert((function()
local res = {}
local i = 1
repeat
local j = i
res[#res + 1] = function()
return j
end
if i == 5 then
i = i + 1
continue
end
i = i + 1
j = j * 2
until i > 10
local sum = 0
for i, f in pairs(res) do
sum = sum + f()
end
return sum
end)() == 105)
-- shrinking array part
assert((function()
local t = table.create(100, 42)
for i = 1, 90 do
t[i] = nil
end
t[101] = 42
local sum = 0
for _, v in ipairs(t) do
sum += v
end
for _, v in pairs(t) do
sum += v
end
return sum
end)() == 462)
-- upvalues: recursive capture
assert((function()
local function fact(n)
return n < 1 and 1 or n * fact(n - 1)
end
return fact(5)
end)() == 120)
-- basic compound assignment
assert((function()
local a = 1
b = 2
local c = { value = 3 }
local d = { 4 }
local e = 3
local f = 2
a += 5
b -= a
c.value *= 3
d[1] /= b
e %= 2
f ^= 4
return concat(a, b, c.value, d[1], e, f)
end)() == "6,-4,9,-1,1,16")
-- compound concat
assert((function()
local a = "a"
a ..= "b"
a ..= "c" .. "d"
a ..= "e" .. "f" .. a
return a
end)() == "abcdefabcd")
-- compound assignment with side effects validates lhs is evaluated once
assert((function()
local res = { 1, 2, 3 }
local count = 0
res[(function()
count += 1
return count
end)()] += 5
res[(function()
count += 1
return count
end)()] += 6
res[(function()
count += 1
return count
end)()] += 7
return table.concat(res, ",")
end)() == "6,8,10")
-- checking for a CFG issue that was missed in IR
assert((function(b)
local res = 0
if b then
for i = 1, 100 do
res += i
end
else
res += 100000
end
return res
end)(true) == 5050)
-- typeof and type require an argument
assert(pcall(typeof) == false)
assert(pcall(type) == false)
function nothing() end
assert(pcall(function()
return typeof(nothing())
end) == false)
assert(pcall(function()
return type(nothing())
end) == false)
-- typeof == type in absence of custom userdata
assert(concat(typeof(5), typeof(nil), typeof({}), typeof(newproxy())) == "number,nil,table,userdata")
-- type/typeof/newproxy interaction with metatables: __type doesn't work intentionally to avoid spoofing
assert((function()
local ud = newproxy(true)
getmetatable(ud).__type = "number"
return concat(type(ud), typeof(ud))
end)() == "userdata,userdata")
testgetfenv() -- DONT MOVE THIS LINE
return "OK"