1package lua
2
3import (
4	"context"
5	"strings"
6	"testing"
7	"time"
8)
9
10func TestLStateIsClosed(t *testing.T) {
11	L := NewState()
12	L.Close()
13	errorIfNotEqual(t, true, L.IsClosed())
14}
15
16func TestCallStackOverflowWhenFixed(t *testing.T) {
17	L := NewState(Options{
18		CallStackSize: 3,
19	})
20	defer L.Close()
21
22	// expect fixed stack implementation by default (for backwards compatibility)
23	stack := L.stack
24	if _, ok := stack.(*fixedCallFrameStack); !ok {
25		t.Errorf("expected fixed callframe stack by default")
26	}
27
28	errorIfScriptNotFail(t, L, `
29    local function recurse(count)
30      if count > 0 then
31        recurse(count - 1)
32      end
33    end
34    local function c()
35      print(_printregs())
36      recurse(9)
37    end
38    c()
39    `, "stack overflow")
40}
41
42func TestCallStackOverflowWhenAutoGrow(t *testing.T) {
43	L := NewState(Options{
44		CallStackSize:       3,
45		MinimizeStackMemory: true,
46	})
47	defer L.Close()
48
49	// expect auto growing stack implementation when MinimizeStackMemory is set
50	stack := L.stack
51	if _, ok := stack.(*autoGrowingCallFrameStack); !ok {
52		t.Errorf("expected fixed callframe stack by default")
53	}
54
55	errorIfScriptNotFail(t, L, `
56    local function recurse(count)
57      if count > 0 then
58        recurse(count - 1)
59      end
60    end
61    local function c()
62      print(_printregs())
63      recurse(9)
64    end
65    c()
66    `, "stack overflow")
67}
68
69func TestSkipOpenLibs(t *testing.T) {
70	L := NewState(Options{SkipOpenLibs: true})
71	defer L.Close()
72	errorIfScriptNotFail(t, L, `print("")`,
73		"attempt to call a non-function object")
74	L2 := NewState()
75	defer L2.Close()
76	errorIfScriptFail(t, L2, `print("")`)
77}
78
79func TestGetAndReplace(t *testing.T) {
80	L := NewState()
81	defer L.Close()
82	L.Push(LString("a"))
83	L.Replace(1, LString("b"))
84	L.Replace(0, LString("c"))
85	errorIfNotEqual(t, LNil, L.Get(0))
86	errorIfNotEqual(t, LNil, L.Get(-10))
87	errorIfNotEqual(t, L.Env, L.Get(EnvironIndex))
88	errorIfNotEqual(t, LString("b"), L.Get(1))
89	L.Push(LString("c"))
90	L.Push(LString("d"))
91	L.Replace(-2, LString("e"))
92	errorIfNotEqual(t, LString("e"), L.Get(-2))
93	registry := L.NewTable()
94	L.Replace(RegistryIndex, registry)
95	L.G.Registry = registry
96	errorIfGFuncNotFail(t, L, func(L *LState) int {
97		L.Replace(RegistryIndex, LNil)
98		return 0
99	}, "registry must be a table")
100	errorIfGFuncFail(t, L, func(L *LState) int {
101		env := L.NewTable()
102		L.Replace(EnvironIndex, env)
103		errorIfNotEqual(t, env, L.Get(EnvironIndex))
104		return 0
105	})
106	errorIfGFuncNotFail(t, L, func(L *LState) int {
107		L.Replace(EnvironIndex, LNil)
108		return 0
109	}, "environment must be a table")
110	errorIfGFuncFail(t, L, func(L *LState) int {
111		gbl := L.NewTable()
112		L.Replace(GlobalsIndex, gbl)
113		errorIfNotEqual(t, gbl, L.G.Global)
114		return 0
115	})
116	errorIfGFuncNotFail(t, L, func(L *LState) int {
117		L.Replace(GlobalsIndex, LNil)
118		return 0
119	}, "_G must be a table")
120
121	L2 := NewState()
122	defer L2.Close()
123	clo := L2.NewClosure(func(L2 *LState) int {
124		L2.Replace(UpvalueIndex(1), LNumber(3))
125		errorIfNotEqual(t, LNumber(3), L2.Get(UpvalueIndex(1)))
126		return 0
127	}, LNumber(1), LNumber(2))
128	L2.SetGlobal("clo", clo)
129	errorIfScriptFail(t, L2, `clo()`)
130}
131
132func TestRemove(t *testing.T) {
133	L := NewState()
134	defer L.Close()
135	L.Push(LString("a"))
136	L.Push(LString("b"))
137	L.Push(LString("c"))
138
139	L.Remove(4)
140	errorIfNotEqual(t, LString("a"), L.Get(1))
141	errorIfNotEqual(t, LString("b"), L.Get(2))
142	errorIfNotEqual(t, LString("c"), L.Get(3))
143	errorIfNotEqual(t, 3, L.GetTop())
144
145	L.Remove(3)
146	errorIfNotEqual(t, LString("a"), L.Get(1))
147	errorIfNotEqual(t, LString("b"), L.Get(2))
148	errorIfNotEqual(t, LNil, L.Get(3))
149	errorIfNotEqual(t, 2, L.GetTop())
150	L.Push(LString("c"))
151
152	L.Remove(-10)
153	errorIfNotEqual(t, LString("a"), L.Get(1))
154	errorIfNotEqual(t, LString("b"), L.Get(2))
155	errorIfNotEqual(t, LString("c"), L.Get(3))
156	errorIfNotEqual(t, 3, L.GetTop())
157
158	L.Remove(2)
159	errorIfNotEqual(t, LString("a"), L.Get(1))
160	errorIfNotEqual(t, LString("c"), L.Get(2))
161	errorIfNotEqual(t, LNil, L.Get(3))
162	errorIfNotEqual(t, 2, L.GetTop())
163}
164
165func TestToInt(t *testing.T) {
166	L := NewState()
167	defer L.Close()
168	L.Push(LNumber(10))
169	L.Push(LString("99.9"))
170	L.Push(L.NewTable())
171	errorIfNotEqual(t, 10, L.ToInt(1))
172	errorIfNotEqual(t, 99, L.ToInt(2))
173	errorIfNotEqual(t, 0, L.ToInt(3))
174}
175
176func TestToInt64(t *testing.T) {
177	L := NewState()
178	defer L.Close()
179	L.Push(LNumber(10))
180	L.Push(LString("99.9"))
181	L.Push(L.NewTable())
182	errorIfNotEqual(t, int64(10), L.ToInt64(1))
183	errorIfNotEqual(t, int64(99), L.ToInt64(2))
184	errorIfNotEqual(t, int64(0), L.ToInt64(3))
185}
186
187func TestToNumber(t *testing.T) {
188	L := NewState()
189	defer L.Close()
190	L.Push(LNumber(10))
191	L.Push(LString("99.9"))
192	L.Push(L.NewTable())
193	errorIfNotEqual(t, LNumber(10), L.ToNumber(1))
194	errorIfNotEqual(t, LNumber(99.9), L.ToNumber(2))
195	errorIfNotEqual(t, LNumber(0), L.ToNumber(3))
196}
197
198func TestToString(t *testing.T) {
199	L := NewState()
200	defer L.Close()
201	L.Push(LNumber(10))
202	L.Push(LString("99.9"))
203	L.Push(L.NewTable())
204	errorIfNotEqual(t, "10", L.ToString(1))
205	errorIfNotEqual(t, "99.9", L.ToString(2))
206	errorIfNotEqual(t, "", L.ToString(3))
207}
208
209func TestToTable(t *testing.T) {
210	L := NewState()
211	defer L.Close()
212	L.Push(LNumber(10))
213	L.Push(LString("99.9"))
214	L.Push(L.NewTable())
215	errorIfFalse(t, L.ToTable(1) == nil, "index 1 must be nil")
216	errorIfFalse(t, L.ToTable(2) == nil, "index 2 must be nil")
217	errorIfNotEqual(t, L.Get(3), L.ToTable(3))
218}
219
220func TestToFunction(t *testing.T) {
221	L := NewState()
222	defer L.Close()
223	L.Push(LNumber(10))
224	L.Push(LString("99.9"))
225	L.Push(L.NewFunction(func(L *LState) int { return 0 }))
226	errorIfFalse(t, L.ToFunction(1) == nil, "index 1 must be nil")
227	errorIfFalse(t, L.ToFunction(2) == nil, "index 2 must be nil")
228	errorIfNotEqual(t, L.Get(3), L.ToFunction(3))
229}
230
231func TestToUserData(t *testing.T) {
232	L := NewState()
233	defer L.Close()
234	L.Push(LNumber(10))
235	L.Push(LString("99.9"))
236	L.Push(L.NewUserData())
237	errorIfFalse(t, L.ToUserData(1) == nil, "index 1 must be nil")
238	errorIfFalse(t, L.ToUserData(2) == nil, "index 2 must be nil")
239	errorIfNotEqual(t, L.Get(3), L.ToUserData(3))
240}
241
242func TestToChannel(t *testing.T) {
243	L := NewState()
244	defer L.Close()
245	L.Push(LNumber(10))
246	L.Push(LString("99.9"))
247	var ch chan LValue
248	L.Push(LChannel(ch))
249	errorIfFalse(t, L.ToChannel(1) == nil, "index 1 must be nil")
250	errorIfFalse(t, L.ToChannel(2) == nil, "index 2 must be nil")
251	errorIfNotEqual(t, ch, L.ToChannel(3))
252}
253
254func TestObjLen(t *testing.T) {
255	L := NewState()
256	defer L.Close()
257	errorIfNotEqual(t, 3, L.ObjLen(LString("abc")))
258	tbl := L.NewTable()
259	tbl.Append(LTrue)
260	tbl.Append(LTrue)
261	errorIfNotEqual(t, 2, L.ObjLen(tbl))
262	mt := L.NewTable()
263	L.SetField(mt, "__len", L.NewFunction(func(L *LState) int {
264		tbl := L.CheckTable(1)
265		L.Push(LNumber(tbl.Len() + 1))
266		return 1
267	}))
268	L.SetMetatable(tbl, mt)
269	errorIfNotEqual(t, 3, L.ObjLen(tbl))
270	errorIfNotEqual(t, 0, L.ObjLen(LNumber(10)))
271}
272
273func TestConcat(t *testing.T) {
274	L := NewState()
275	defer L.Close()
276	errorIfNotEqual(t, "a1c", L.Concat(LString("a"), LNumber(1), LString("c")))
277}
278
279func TestPCall(t *testing.T) {
280	L := NewState()
281	defer L.Close()
282	L.Register("f1", func(L *LState) int {
283		panic("panic!")
284		return 0
285	})
286	errorIfScriptNotFail(t, L, `f1()`, "panic!")
287	L.Push(L.GetGlobal("f1"))
288	err := L.PCall(0, 0, L.NewFunction(func(L *LState) int {
289		L.Push(LString("by handler"))
290		return 1
291	}))
292	errorIfFalse(t, strings.Contains(err.Error(), "by handler"), "")
293
294	err = L.PCall(0, 0, L.NewFunction(func(L *LState) int {
295		L.RaiseError("error!")
296		return 1
297	}))
298	errorIfFalse(t, strings.Contains(err.Error(), "error!"), "")
299
300	err = L.PCall(0, 0, L.NewFunction(func(L *LState) int {
301		panic("panicc!")
302		return 1
303	}))
304	errorIfFalse(t, strings.Contains(err.Error(), "panicc!"), "")
305}
306
307func TestCoroutineApi1(t *testing.T) {
308	L := NewState()
309	defer L.Close()
310	co, _ := L.NewThread()
311	errorIfScriptFail(t, L, `
312      function coro(v)
313        assert(v == 10)
314        local ret1, ret2 = coroutine.yield(1,2,3)
315        assert(ret1 == 11)
316        assert(ret2 == 12)
317        coroutine.yield(4)
318        return 5
319      end
320    `)
321	fn := L.GetGlobal("coro").(*LFunction)
322	st, err, values := L.Resume(co, fn, LNumber(10))
323	errorIfNotEqual(t, ResumeYield, st)
324	errorIfNotNil(t, err)
325	errorIfNotEqual(t, 3, len(values))
326	errorIfNotEqual(t, LNumber(1), values[0].(LNumber))
327	errorIfNotEqual(t, LNumber(2), values[1].(LNumber))
328	errorIfNotEqual(t, LNumber(3), values[2].(LNumber))
329
330	st, err, values = L.Resume(co, fn, LNumber(11), LNumber(12))
331	errorIfNotEqual(t, ResumeYield, st)
332	errorIfNotNil(t, err)
333	errorIfNotEqual(t, 1, len(values))
334	errorIfNotEqual(t, LNumber(4), values[0].(LNumber))
335
336	st, err, values = L.Resume(co, fn)
337	errorIfNotEqual(t, ResumeOK, st)
338	errorIfNotNil(t, err)
339	errorIfNotEqual(t, 1, len(values))
340	errorIfNotEqual(t, LNumber(5), values[0].(LNumber))
341
342	L.Register("myyield", func(L *LState) int {
343		return L.Yield(L.ToNumber(1))
344	})
345	errorIfScriptFail(t, L, `
346      function coro_error()
347        coroutine.yield(1,2,3)
348        myyield(4)
349        assert(false, "--failed--")
350      end
351    `)
352	fn = L.GetGlobal("coro_error").(*LFunction)
353	co, _ = L.NewThread()
354	st, err, values = L.Resume(co, fn)
355	errorIfNotEqual(t, ResumeYield, st)
356	errorIfNotNil(t, err)
357	errorIfNotEqual(t, 3, len(values))
358	errorIfNotEqual(t, LNumber(1), values[0].(LNumber))
359	errorIfNotEqual(t, LNumber(2), values[1].(LNumber))
360	errorIfNotEqual(t, LNumber(3), values[2].(LNumber))
361
362	st, err, values = L.Resume(co, fn)
363	errorIfNotEqual(t, ResumeYield, st)
364	errorIfNotNil(t, err)
365	errorIfNotEqual(t, 1, len(values))
366	errorIfNotEqual(t, LNumber(4), values[0].(LNumber))
367
368	st, err, values = L.Resume(co, fn)
369	errorIfNotEqual(t, ResumeError, st)
370	errorIfNil(t, err)
371	errorIfFalse(t, strings.Contains(err.Error(), "--failed--"), "error message must be '--failed--'")
372	st, err, values = L.Resume(co, fn)
373	errorIfNotEqual(t, ResumeError, st)
374	errorIfNil(t, err)
375	errorIfFalse(t, strings.Contains(err.Error(), "can not resume a dead thread"), "can not resume a dead thread")
376
377}
378
379func TestContextTimeout(t *testing.T) {
380	L := NewState()
381	defer L.Close()
382	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
383	defer cancel()
384	L.SetContext(ctx)
385	errorIfNotEqual(t, ctx, L.Context())
386	err := L.DoString(`
387	  local clock = os.clock
388      function sleep(n)  -- seconds
389        local t0 = clock()
390        while clock() - t0 <= n do end
391      end
392	  sleep(3)
393	`)
394	errorIfNil(t, err)
395	errorIfFalse(t, strings.Contains(err.Error(), "context deadline exceeded"), "execution must be canceled")
396
397	oldctx := L.RemoveContext()
398	errorIfNotEqual(t, ctx, oldctx)
399	errorIfNotNil(t, L.ctx)
400}
401
402func TestContextCancel(t *testing.T) {
403	L := NewState()
404	defer L.Close()
405	ctx, cancel := context.WithCancel(context.Background())
406	errch := make(chan error, 1)
407	L.SetContext(ctx)
408	go func() {
409		errch <- L.DoString(`
410	    local clock = os.clock
411        function sleep(n)  -- seconds
412          local t0 = clock()
413          while clock() - t0 <= n do end
414        end
415	    sleep(3)
416	  `)
417	}()
418	time.Sleep(1 * time.Second)
419	cancel()
420	err := <-errch
421	errorIfNil(t, err)
422	errorIfFalse(t, strings.Contains(err.Error(), "context canceled"), "execution must be canceled")
423}
424
425func TestContextWithCroutine(t *testing.T) {
426	L := NewState()
427	defer L.Close()
428	ctx, cancel := context.WithCancel(context.Background())
429	L.SetContext(ctx)
430	defer cancel()
431	L.DoString(`
432	    function coro()
433		  local i = 0
434		  while true do
435		    coroutine.yield(i)
436			i = i+1
437		  end
438		  return i
439	    end
440	`)
441	co, cocancel := L.NewThread()
442	defer cocancel()
443	fn := L.GetGlobal("coro").(*LFunction)
444	_, err, values := L.Resume(co, fn)
445	errorIfNotNil(t, err)
446	errorIfNotEqual(t, LNumber(0), values[0])
447	// cancel the parent context
448	cancel()
449	_, err, values = L.Resume(co, fn)
450	errorIfNil(t, err)
451	errorIfFalse(t, strings.Contains(err.Error(), "context canceled"), "coroutine execution must be canceled when the parent context is canceled")
452
453}
454
455func TestPCallAfterFail(t *testing.T) {
456	L := NewState()
457	defer L.Close()
458	errFn := L.NewFunction(func(L *LState) int {
459		L.RaiseError("error!")
460		return 0
461	})
462	changeError := L.NewFunction(func(L *LState) int {
463		L.Push(errFn)
464		err := L.PCall(0, 0, nil)
465		if err != nil {
466			L.RaiseError("A New Error")
467		}
468		return 0
469	})
470	L.Push(changeError)
471	err := L.PCall(0, 0, nil)
472	errorIfFalse(t, strings.Contains(err.Error(), "A New Error"), "error not propogated correctly")
473}
474
475func TestRegistryFixedOverflow(t *testing.T) {
476	state := NewState()
477	defer state.Close()
478	reg := state.reg
479	expectedPanic := false
480	// should be non auto grow by default
481	errorIfFalse(t, reg.maxSize == 0, "state should default to non-auto growing implementation")
482	// fill the stack and check we get a panic
483	test := LString("test")
484	for i := 0; i < len(reg.array); i++ {
485		reg.Push(test)
486	}
487	defer func() {
488		rcv := recover()
489		if rcv != nil {
490			if expectedPanic {
491				errorIfFalse(t, rcv.(error).Error() != "registry overflow", "expected registry overflow exception, got "+rcv.(error).Error())
492			} else {
493				t.Errorf("did not expect registry overflow")
494			}
495		} else if expectedPanic {
496			t.Errorf("expected registry overflow exception, but didn't get panic")
497		}
498	}()
499	expectedPanic = true
500	reg.Push(test)
501}
502
503func TestRegistryAutoGrow(t *testing.T) {
504	state := NewState(Options{RegistryMaxSize: 300, RegistrySize: 200, RegistryGrowStep: 25})
505	defer state.Close()
506	expectedPanic := false
507	defer func() {
508		rcv := recover()
509		if rcv != nil {
510			if expectedPanic {
511				errorIfFalse(t, rcv.(error).Error() != "registry overflow", "expected registry overflow exception, got "+rcv.(error).Error())
512			} else {
513				t.Errorf("did not expect registry overflow")
514			}
515		} else if expectedPanic {
516			t.Errorf("expected registry overflow exception, but didn't get panic")
517		}
518	}()
519	reg := state.reg
520	test := LString("test")
521	for i := 0; i < 300; i++ {
522		reg.Push(test)
523	}
524	expectedPanic = true
525	reg.Push(test)
526}
527
528// This test exposed a panic caused by accessing an unassigned var in the lua registry.
529// The panic was caused by initCallFrame. It was calling resize() on the registry after it had written some values
530// directly to the reg's array, but crucially, before it had updated "top". This meant when the resize occurred, the
531// values beyond top where not copied, and were lost, leading to a later uninitialised value being found in the registry.
532func TestUninitializedVarAccess(t *testing.T) {
533	L := NewState(Options{
534		RegistrySize:    128,
535		RegistryMaxSize: 256,
536	})
537	defer L.Close()
538	// This test needs to trigger a resize when the local vars are allocated, so we need it to
539	// be 128 for the padding amount in the test function to work. If it's larger, we will need
540	// more padding to force the error.
541	errorIfNotEqual(t, cap(L.reg.array), 128)
542	ctx, cancel := context.WithCancel(context.Background())
543	L.SetContext(ctx)
544	defer cancel()
545	errorIfScriptFail(t, L, `
546		local function test(arg1, arg2, arg3)
547			-- padding to cause a registry resize when the local vars for this func are reserved
548			local a0,b0,c0,d0,e0,f0,g0,h0,i0,j0,k0,l0,m0,n0,o0,p0,q0,r0,s0,t0,u0,v0,w0,x0,y0,z0
549			local a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1,n1,o1,p1,q1,r1,s1,t1,u1,v1,w1,x1,y1,z1
550			local a2,b2,c2,d2,e2,f2,g2,h2,i2,j2,k2,l2,m2,n2,o2,p2,q2,r2,s2,t2,u2,v2,w2,x2,y2,z2
551			local a3,b3,c3,d3,e3,f3,g3,h3,i3,j3,k3,l3,m3,n3,o3,p3,q3,r3,s3,t3,u3,v3,w3,x3,y3,z3
552			local a4,b4,c4,d4,e4,f4,g4,h4,i4,j4,k4,l4,m4,n4,o4,p4,q4,r4,s4,t4,u4,v4,w4,x4,y4,z4
553			if arg3 == nil then
554				return 1
555			end
556			return 0
557		end
558
559		test(1,2)
560	`)
561}
562
563func BenchmarkCallFrameStackPushPopAutoGrow(t *testing.B) {
564	stack := newAutoGrowingCallFrameStack(256)
565
566	t.ResetTimer()
567
568	const Iterations = 256
569	for j := 0; j < t.N; j++ {
570		for i := 0; i < Iterations; i++ {
571			stack.Push(callFrame{})
572		}
573		for i := 0; i < Iterations; i++ {
574			stack.Pop()
575		}
576	}
577}
578
579func BenchmarkCallFrameStackPushPopFixed(t *testing.B) {
580	stack := newFixedCallFrameStack(256)
581
582	t.ResetTimer()
583
584	const Iterations = 256
585	for j := 0; j < t.N; j++ {
586		for i := 0; i < Iterations; i++ {
587			stack.Push(callFrame{})
588		}
589		for i := 0; i < Iterations; i++ {
590			stack.Pop()
591		}
592	}
593}
594
595// this test will intentionally not incur stack growth in order to bench the performance when no allocations happen
596func BenchmarkCallFrameStackPushPopShallowAutoGrow(t *testing.B) {
597	stack := newAutoGrowingCallFrameStack(256)
598
599	t.ResetTimer()
600
601	const Iterations = 8
602	for j := 0; j < t.N; j++ {
603		for i := 0; i < Iterations; i++ {
604			stack.Push(callFrame{})
605		}
606		for i := 0; i < Iterations; i++ {
607			stack.Pop()
608		}
609	}
610}
611
612func BenchmarkCallFrameStackPushPopShallowFixed(t *testing.B) {
613	stack := newFixedCallFrameStack(256)
614
615	t.ResetTimer()
616
617	const Iterations = 8
618	for j := 0; j < t.N; j++ {
619		for i := 0; i < Iterations; i++ {
620			stack.Push(callFrame{})
621		}
622		for i := 0; i < Iterations; i++ {
623			stack.Pop()
624		}
625	}
626}
627
628func BenchmarkCallFrameStackPushPopFixedNoInterface(t *testing.B) {
629	stack := newFixedCallFrameStack(256).(*fixedCallFrameStack)
630
631	t.ResetTimer()
632
633	const Iterations = 256
634	for j := 0; j < t.N; j++ {
635		for i := 0; i < Iterations; i++ {
636			stack.Push(callFrame{})
637		}
638		for i := 0; i < Iterations; i++ {
639			stack.Pop()
640		}
641	}
642}
643
644func BenchmarkCallFrameStackUnwindAutoGrow(t *testing.B) {
645	stack := newAutoGrowingCallFrameStack(256)
646
647	t.ResetTimer()
648
649	const Iterations = 256
650	for j := 0; j < t.N; j++ {
651		for i := 0; i < Iterations; i++ {
652			stack.Push(callFrame{})
653		}
654		stack.SetSp(0)
655	}
656}
657
658func BenchmarkCallFrameStackUnwindFixed(t *testing.B) {
659	stack := newFixedCallFrameStack(256)
660
661	t.ResetTimer()
662
663	const Iterations = 256
664	for j := 0; j < t.N; j++ {
665		for i := 0; i < Iterations; i++ {
666			stack.Push(callFrame{})
667		}
668		stack.SetSp(0)
669	}
670}
671
672func BenchmarkCallFrameStackUnwindFixedNoInterface(t *testing.B) {
673	stack := newFixedCallFrameStack(256).(*fixedCallFrameStack)
674
675	t.ResetTimer()
676
677	const Iterations = 256
678	for j := 0; j < t.N; j++ {
679		for i := 0; i < Iterations; i++ {
680			stack.Push(callFrame{})
681		}
682		stack.SetSp(0)
683	}
684}
685
686type registryTestHandler int
687
688func (registryTestHandler) registryOverflow() {
689	panic("registry overflow")
690}
691
692// test pushing and popping from the registry
693func BenchmarkRegistryPushPopAutoGrow(t *testing.B) {
694	al := newAllocator(32)
695	sz := 256 * 20
696	reg := newRegistry(registryTestHandler(0), sz/2, 64, sz, al)
697	value := LString("test")
698
699	t.ResetTimer()
700
701	for j := 0; j < t.N; j++ {
702		for i := 0; i < sz; i++ {
703			reg.Push(value)
704		}
705		for i := 0; i < sz; i++ {
706			reg.Pop()
707		}
708	}
709}
710
711func BenchmarkRegistryPushPopFixed(t *testing.B) {
712	al := newAllocator(32)
713	sz := 256 * 20
714	reg := newRegistry(registryTestHandler(0), sz, 0, sz, al)
715	value := LString("test")
716
717	t.ResetTimer()
718
719	for j := 0; j < t.N; j++ {
720		for i := 0; i < sz; i++ {
721			reg.Push(value)
722		}
723		for i := 0; i < sz; i++ {
724			reg.Pop()
725		}
726	}
727}
728
729func BenchmarkRegistrySetTop(t *testing.B) {
730	al := newAllocator(32)
731	sz := 256 * 20
732	reg := newRegistry(registryTestHandler(0), sz, 32, sz*2, al)
733
734	t.ResetTimer()
735
736	for j := 0; j < t.N; j++ {
737		reg.SetTop(sz)
738		reg.SetTop(0)
739	}
740}
741