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'