github.com/shawnclovie/gopher-lua@v0.0.0-20200520092726-90b44ec0e2f2/state_test.go (about)

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