1-- $Id: testes/closure.lua $
  2-- See Copyright Notice in file all.lua
  3
  4print "testing closures"
  5
  6local A,B = 0,{g=10}
  7local function f(x)
  8  local a = {}
  9  for i=1,1000 do
 10    local y = 0
 11    do
 12      a[i] = function () B.g = B.g+1; y = y+x; return y+A end
 13    end
 14  end
 15  local dummy = function () return a[A] end
 16  collectgarbage()
 17  A = 1; assert(dummy() == a[1]); A = 0;
 18  assert(a[1]() == x)
 19  assert(a[3]() == x)
 20  collectgarbage()
 21  assert(B.g == 12)
 22  return a
 23end
 24
 25local a = f(10)
 26-- force a GC in this level
 27local x = {[1] = {}}   -- to detect a GC
 28setmetatable(x, {__mode = 'kv'})
 29while x[1] do   -- repeat until GC
 30  local a = A..A..A..A  -- create garbage
 31  A = A+1
 32end
 33assert(a[1]() == 20+A)
 34assert(a[1]() == 30+A)
 35assert(a[2]() == 10+A)
 36collectgarbage()
 37assert(a[2]() == 20+A)
 38assert(a[2]() == 30+A)
 39assert(a[3]() == 20+A)
 40assert(a[8]() == 10+A)
 41assert(getmetatable(x).__mode == 'kv')
 42assert(B.g == 19)
 43
 44
 45-- testing equality
 46a = {}
 47
 48for i = 1, 5 do  a[i] = function (x) return i + a + _ENV end  end
 49assert(a[3] ~= a[4] and a[4] ~= a[5])
 50
 51do
 52  local a = function (x)  return math.sin(_ENV[x])  end
 53  local function f()
 54    return a
 55  end
 56  assert(f() == f())
 57end
 58
 59
 60-- testing closures with 'for' control variable
 61a = {}
 62for i=1,10 do
 63  a[i] = {set = function(x) i=x end, get = function () return i end}
 64  if i == 3 then break end
 65end
 66assert(a[4] == undef)
 67a[1].set(10)
 68assert(a[2].get() == 2)
 69a[2].set('a')
 70assert(a[3].get() == 3)
 71assert(a[2].get() == 'a')
 72
 73a = {}
 74local t = {"a", "b"}
 75for i = 1, #t do
 76  local k = t[i]
 77  a[i] = {set = function(x, y) i=x; k=y end,
 78          get = function () return i, k end}
 79  if i == 2 then break end
 80end
 81a[1].set(10, 20)
 82local r,s = a[2].get()
 83assert(r == 2 and s == 'b')
 84r,s = a[1].get()
 85assert(r == 10 and s == 20)
 86a[2].set('a', 'b')
 87r,s = a[2].get()
 88assert(r == "a" and s == "b")
 89
 90
 91-- testing closures with 'for' control variable x break
 92local f
 93for i=1,3 do
 94  f = function () return i end
 95  break
 96end
 97assert(f() == 1)
 98
 99for k = 1, #t do
100  local v = t[k]
101  f = function () return k, v end
102  break
103end
104assert(({f()})[1] == 1)
105assert(({f()})[2] == "a")
106
107
108-- testing closure x break x return x errors
109
110local b
111function f(x)
112  local first = 1
113  while 1 do
114    if x == 3 and not first then return end
115    local a = 'xuxu'
116    b = function (op, y)
117          if op == 'set' then
118            a = x+y
119          else
120            return a
121          end
122        end
123    if x == 1 then do break end
124    elseif x == 2 then return
125    else if x ~= 3 then error() end
126    end
127    first = nil
128  end
129end
130
131for i=1,3 do
132  f(i)
133  assert(b('get') == 'xuxu')
134  b('set', 10); assert(b('get') == 10+i)
135  b = nil
136end
137
138pcall(f, 4);
139assert(b('get') == 'xuxu')
140b('set', 10); assert(b('get') == 14)
141
142
143local y, w
144-- testing multi-level closure
145function f(x)
146  return function (y)
147    return function (z) return w+x+y+z end
148  end
149end
150
151y = f(10)
152w = 1.345
153assert(y(20)(30) == 60+w)
154
155
156-- testing closures x break
157do
158  local X, Y
159  local a = math.sin(0)
160
161  while a do
162    local b = 10
163    X = function () return b end   -- closure with upvalue
164    if a then break end
165  end
166  
167  do
168    local b = 20
169    Y = function () return b end   -- closure with upvalue
170  end
171
172  -- upvalues must be different
173  assert(X() == 10 and Y() == 20)
174end
175
176  
177-- testing closures x repeat-until
178
179local a = {}
180local i = 1
181repeat
182  local x = i
183  a[i] = function () i = x+1; return x end
184until i > 10 or a[i]() ~= x
185assert(i == 11 and a[1]() == 1 and a[3]() == 3 and i == 4)
186
187
188-- testing closures created in 'then' and 'else' parts of 'if's
189a = {}
190for i = 1, 10 do
191  if i % 3 == 0 then
192    local y = 0
193    a[i] = function (x) local t = y; y = x; return t end
194  elseif i % 3 == 1 then
195    goto L1
196    error'not here'
197  ::L1::
198    local y = 1
199    a[i] = function (x) local t = y; y = x; return t end
200  elseif i % 3 == 2 then
201    local t
202    goto l4
203    ::l4a:: a[i] = t; goto l4b
204    error("should never be here!")
205    ::l4::
206    local y = 2
207    t = function (x) local t = y; y = x; return t end
208    goto l4a
209    error("should never be here!")
210    ::l4b::
211  end
212end
213
214for i = 1, 10 do
215  assert(a[i](i * 10) == i % 3 and a[i]() == i * 10)
216end
217
218print'+'
219
220
221-- test for correctly closing upvalues in tail calls of vararg functions
222local function t ()
223  local function c(a,b) assert(a=="test" and b=="OK") end
224  local function v(f, ...) c("test", f() ~= 1 and "FAILED" or "OK") end
225  local x = 1
226  return v(function() return x end)
227end
228t()
229
230
231-- test for debug manipulation of upvalues
232local debug = require'debug'
233
234local foo1, foo2, foo3
235do
236  local a , b, c = 3, 5, 7
237  foo1 = function () return a+b end;
238  foo2 = function () return b+a end;
239  do
240    local a = 10
241    foo3 = function () return a+b end;
242  end
243end
244
245assert(debug.upvalueid(foo1, 1))
246assert(debug.upvalueid(foo1, 2))
247assert(not debug.upvalueid(foo1, 3))
248assert(debug.upvalueid(foo1, 1) == debug.upvalueid(foo2, 2))
249assert(debug.upvalueid(foo1, 2) == debug.upvalueid(foo2, 1))
250assert(debug.upvalueid(foo3, 1))
251assert(debug.upvalueid(foo1, 1) ~= debug.upvalueid(foo3, 1))
252assert(debug.upvalueid(foo1, 2) == debug.upvalueid(foo3, 2))
253
254assert(debug.upvalueid(string.gmatch("x", "x"), 1) ~= nil)
255
256assert(foo1() == 3 + 5 and foo2() == 5 + 3)
257debug.upvaluejoin(foo1, 2, foo2, 2)
258assert(foo1() == 3 + 3 and foo2() == 5 + 3)
259assert(foo3() == 10 + 5)
260debug.upvaluejoin(foo3, 2, foo2, 1)
261assert(foo3() == 10 + 5)
262debug.upvaluejoin(foo3, 2, foo2, 2)
263assert(foo3() == 10 + 3)
264
265assert(not pcall(debug.upvaluejoin, foo1, 3, foo2, 1))
266assert(not pcall(debug.upvaluejoin, foo1, 1, foo2, 3))
267assert(not pcall(debug.upvaluejoin, foo1, 0, foo2, 1))
268assert(not pcall(debug.upvaluejoin, print, 1, foo2, 1))
269assert(not pcall(debug.upvaluejoin, {}, 1, foo2, 1))
270assert(not pcall(debug.upvaluejoin, foo1, 1, print, 1))
271
272print'OK'