github.com/assemblaj/gopher-lua@v0.0.0-20221116224352-d57295a0d9e8/state_test.go (about)

     1  package lua
     2  
     3  import (
     4  	"context"
     5  	"strings"
     6  	"testing"
     7  	"time"
     8  )
     9  
    10  func TestLStateIsClosed(t *testing.T) {
    11  	L := NewState()
    12  	L.Close()
    13  	errorIfNotEqual(t, true, L.IsClosed())
    14  }
    15  
    16  func 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  
    42  func 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  
    69  func 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  
    79  func 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  
   132  func 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  
   165  func 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  
   176  func 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  
   187  func 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  
   198  func 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  
   209  func 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  
   220  func 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  
   231  func 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  
   242  func 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  
   254  func 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  
   273  func 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  
   279  func 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  
   307  func 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  
   379  func 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  
   402  func 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  
   425  func 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  
   455  func 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  
   475  func 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  
   503  func 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.
   532  func 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  
   563  func 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  
   579  func 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
   596  func 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  
   612  func 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  
   628  func 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  
   644  func 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  
   658  func 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  
   672  func 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  
   686  type registryTestHandler int
   687  
   688  func (registryTestHandler) registryOverflow() {
   689  	panic("registry overflow")
   690  }
   691  
   692  // test pushing and popping from the registry
   693  func 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  
   711  func 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  
   729  func 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  }