github.com/enotodden/gopher-lua@v1.1.2/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  	L.Push(L.GetGlobal("f1"))
   295  	err = L.PCall(0, 0, L.NewFunction(func(L *LState) int {
   296  		L.RaiseError("error!")
   297  		return 1
   298  	}))
   299  	errorIfFalse(t, strings.Contains(err.Error(), "error!"), "")
   300  
   301  	L.Push(L.GetGlobal("f1"))
   302  	err = L.PCall(0, 0, L.NewFunction(func(L *LState) int {
   303  		panic("panicc!")
   304  		return 1
   305  	}))
   306  	errorIfFalse(t, strings.Contains(err.Error(), "panicc!"), "")
   307  
   308  	// Issue #452, expected to be revert back to previous call stack after any error.
   309  	currentFrame, currentTop, currentSp := L.currentFrame, L.GetTop(), L.stack.Sp()
   310  	L.Push(L.GetGlobal("f1"))
   311  	err = L.PCall(0, 0, nil)
   312  	errorIfFalse(t, err != nil, "")
   313  	errorIfFalse(t, L.currentFrame == currentFrame, "")
   314  	errorIfFalse(t, L.GetTop() == currentTop, "")
   315  	errorIfFalse(t, L.stack.Sp() == currentSp, "")
   316  
   317  	currentFrame, currentTop, currentSp = L.currentFrame, L.GetTop(), L.stack.Sp()
   318  	L.Push(L.GetGlobal("f1"))
   319  	err = L.PCall(0, 0, L.NewFunction(func(L *LState) int {
   320  		L.RaiseError("error!")
   321  		return 1
   322  	}))
   323  	errorIfFalse(t, err != nil, "")
   324  	errorIfFalse(t, L.currentFrame == currentFrame, "")
   325  	errorIfFalse(t, L.GetTop() == currentTop, "")
   326  	errorIfFalse(t, L.stack.Sp() == currentSp, "")
   327  }
   328  
   329  func TestCoroutineApi1(t *testing.T) {
   330  	L := NewState()
   331  	defer L.Close()
   332  	co, _ := L.NewThread()
   333  	errorIfScriptFail(t, L, `
   334        function coro(v)
   335          assert(v == 10)
   336          local ret1, ret2 = coroutine.yield(1,2,3)
   337          assert(ret1 == 11)
   338          assert(ret2 == 12)
   339          coroutine.yield(4)
   340          return 5
   341        end
   342      `)
   343  	fn := L.GetGlobal("coro").(*LFunction)
   344  	st, err, values := L.Resume(co, fn, LNumber(10))
   345  	errorIfNotEqual(t, ResumeYield, st)
   346  	errorIfNotNil(t, err)
   347  	errorIfNotEqual(t, 3, len(values))
   348  	errorIfNotEqual(t, LNumber(1), values[0].(LNumber))
   349  	errorIfNotEqual(t, LNumber(2), values[1].(LNumber))
   350  	errorIfNotEqual(t, LNumber(3), values[2].(LNumber))
   351  
   352  	st, err, values = L.Resume(co, fn, LNumber(11), LNumber(12))
   353  	errorIfNotEqual(t, ResumeYield, st)
   354  	errorIfNotNil(t, err)
   355  	errorIfNotEqual(t, 1, len(values))
   356  	errorIfNotEqual(t, LNumber(4), values[0].(LNumber))
   357  
   358  	st, err, values = L.Resume(co, fn)
   359  	errorIfNotEqual(t, ResumeOK, st)
   360  	errorIfNotNil(t, err)
   361  	errorIfNotEqual(t, 1, len(values))
   362  	errorIfNotEqual(t, LNumber(5), values[0].(LNumber))
   363  
   364  	L.Register("myyield", func(L *LState) int {
   365  		return L.Yield(L.ToNumber(1))
   366  	})
   367  	errorIfScriptFail(t, L, `
   368        function coro_error()
   369          coroutine.yield(1,2,3)
   370          myyield(4)
   371          assert(false, "--failed--")
   372        end
   373      `)
   374  	fn = L.GetGlobal("coro_error").(*LFunction)
   375  	co, _ = L.NewThread()
   376  	st, err, values = L.Resume(co, fn)
   377  	errorIfNotEqual(t, ResumeYield, st)
   378  	errorIfNotNil(t, err)
   379  	errorIfNotEqual(t, 3, len(values))
   380  	errorIfNotEqual(t, LNumber(1), values[0].(LNumber))
   381  	errorIfNotEqual(t, LNumber(2), values[1].(LNumber))
   382  	errorIfNotEqual(t, LNumber(3), values[2].(LNumber))
   383  
   384  	st, err, values = L.Resume(co, fn)
   385  	errorIfNotEqual(t, ResumeYield, st)
   386  	errorIfNotNil(t, err)
   387  	errorIfNotEqual(t, 1, len(values))
   388  	errorIfNotEqual(t, LNumber(4), values[0].(LNumber))
   389  
   390  	st, err, values = L.Resume(co, fn)
   391  	errorIfNotEqual(t, ResumeError, st)
   392  	errorIfNil(t, err)
   393  	errorIfFalse(t, strings.Contains(err.Error(), "--failed--"), "error message must be '--failed--'")
   394  	st, err, values = L.Resume(co, fn)
   395  	errorIfNotEqual(t, ResumeError, st)
   396  	errorIfNil(t, err)
   397  	errorIfFalse(t, strings.Contains(err.Error(), "can not resume a dead thread"), "can not resume a dead thread")
   398  
   399  }
   400  
   401  func TestContextTimeout(t *testing.T) {
   402  	L := NewState()
   403  	defer L.Close()
   404  	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
   405  	defer cancel()
   406  	L.SetContext(ctx)
   407  	errorIfNotEqual(t, ctx, L.Context())
   408  	err := L.DoString(`
   409  	  local clock = os.clock
   410        function sleep(n)  -- seconds
   411          local t0 = clock()
   412          while clock() - t0 <= n do end
   413        end
   414  	  sleep(3)
   415  	`)
   416  	errorIfNil(t, err)
   417  	errorIfFalse(t, strings.Contains(err.Error(), "context deadline exceeded"), "execution must be canceled")
   418  
   419  	oldctx := L.RemoveContext()
   420  	errorIfNotEqual(t, ctx, oldctx)
   421  	errorIfNotNil(t, L.ctx)
   422  }
   423  
   424  func TestContextCancel(t *testing.T) {
   425  	L := NewState()
   426  	defer L.Close()
   427  	ctx, cancel := context.WithCancel(context.Background())
   428  	errch := make(chan error, 1)
   429  	L.SetContext(ctx)
   430  	go func() {
   431  		errch <- L.DoString(`
   432  	    local clock = os.clock
   433          function sleep(n)  -- seconds
   434            local t0 = clock()
   435            while clock() - t0 <= n do end
   436          end
   437  	    sleep(3)
   438  	  `)
   439  	}()
   440  	time.Sleep(1 * time.Second)
   441  	cancel()
   442  	err := <-errch
   443  	errorIfNil(t, err)
   444  	errorIfFalse(t, strings.Contains(err.Error(), "context canceled"), "execution must be canceled")
   445  }
   446  
   447  func TestContextWithCroutine(t *testing.T) {
   448  	L := NewState()
   449  	defer L.Close()
   450  	ctx, cancel := context.WithCancel(context.Background())
   451  	L.SetContext(ctx)
   452  	defer cancel()
   453  	L.DoString(`
   454  	    function coro()
   455  		  local i = 0
   456  		  while true do
   457  		    coroutine.yield(i)
   458  			i = i+1
   459  		  end
   460  		  return i
   461  	    end
   462  	`)
   463  	co, cocancel := L.NewThread()
   464  	defer cocancel()
   465  	fn := L.GetGlobal("coro").(*LFunction)
   466  	_, err, values := L.Resume(co, fn)
   467  	errorIfNotNil(t, err)
   468  	errorIfNotEqual(t, LNumber(0), values[0])
   469  	// cancel the parent context
   470  	cancel()
   471  	_, err, values = L.Resume(co, fn)
   472  	errorIfNil(t, err)
   473  	errorIfFalse(t, strings.Contains(err.Error(), "context canceled"), "coroutine execution must be canceled when the parent context is canceled")
   474  
   475  }
   476  
   477  func TestPCallAfterFail(t *testing.T) {
   478  	L := NewState()
   479  	defer L.Close()
   480  	errFn := L.NewFunction(func(L *LState) int {
   481  		L.RaiseError("error!")
   482  		return 0
   483  	})
   484  	changeError := L.NewFunction(func(L *LState) int {
   485  		L.Push(errFn)
   486  		err := L.PCall(0, 0, nil)
   487  		if err != nil {
   488  			L.RaiseError("A New Error")
   489  		}
   490  		return 0
   491  	})
   492  	L.Push(changeError)
   493  	err := L.PCall(0, 0, nil)
   494  	errorIfFalse(t, strings.Contains(err.Error(), "A New Error"), "error not propogated correctly")
   495  }
   496  
   497  func TestRegistryFixedOverflow(t *testing.T) {
   498  	state := NewState()
   499  	defer state.Close()
   500  	reg := state.reg
   501  	expectedPanic := false
   502  	// should be non auto grow by default
   503  	errorIfFalse(t, reg.maxSize == 0, "state should default to non-auto growing implementation")
   504  	// fill the stack and check we get a panic
   505  	test := LString("test")
   506  	for i := 0; i < len(reg.array); i++ {
   507  		reg.Push(test)
   508  	}
   509  	defer func() {
   510  		rcv := recover()
   511  		if rcv != nil {
   512  			if expectedPanic {
   513  				errorIfFalse(t, rcv.(error).Error() != "registry overflow", "expected registry overflow exception, got "+rcv.(error).Error())
   514  			} else {
   515  				t.Errorf("did not expect registry overflow")
   516  			}
   517  		} else if expectedPanic {
   518  			t.Errorf("expected registry overflow exception, but didn't get panic")
   519  		}
   520  	}()
   521  	expectedPanic = true
   522  	reg.Push(test)
   523  }
   524  
   525  func TestRegistryAutoGrow(t *testing.T) {
   526  	state := NewState(Options{RegistryMaxSize: 300, RegistrySize: 200, RegistryGrowStep: 25})
   527  	defer state.Close()
   528  	expectedPanic := false
   529  	defer func() {
   530  		rcv := recover()
   531  		if rcv != nil {
   532  			if expectedPanic {
   533  				errorIfFalse(t, rcv.(error).Error() != "registry overflow", "expected registry overflow exception, got "+rcv.(error).Error())
   534  			} else {
   535  				t.Errorf("did not expect registry overflow")
   536  			}
   537  		} else if expectedPanic {
   538  			t.Errorf("expected registry overflow exception, but didn't get panic")
   539  		}
   540  	}()
   541  	reg := state.reg
   542  	test := LString("test")
   543  	for i := 0; i < 300; i++ {
   544  		reg.Push(test)
   545  	}
   546  	expectedPanic = true
   547  	reg.Push(test)
   548  }
   549  
   550  // This test exposed a panic caused by accessing an unassigned var in the lua registry.
   551  // The panic was caused by initCallFrame. It was calling resize() on the registry after it had written some values
   552  // directly to the reg's array, but crucially, before it had updated "top". This meant when the resize occurred, the
   553  // values beyond top where not copied, and were lost, leading to a later uninitialised value being found in the registry.
   554  func TestUninitializedVarAccess(t *testing.T) {
   555  	L := NewState(Options{
   556  		RegistrySize:    128,
   557  		RegistryMaxSize: 256,
   558  	})
   559  	defer L.Close()
   560  	// This test needs to trigger a resize when the local vars are allocated, so we need it to
   561  	// be 128 for the padding amount in the test function to work. If it's larger, we will need
   562  	// more padding to force the error.
   563  	errorIfNotEqual(t, cap(L.reg.array), 128)
   564  	ctx, cancel := context.WithCancel(context.Background())
   565  	L.SetContext(ctx)
   566  	defer cancel()
   567  	errorIfScriptFail(t, L, `
   568  		local function test(arg1, arg2, arg3)
   569  			-- padding to cause a registry resize when the local vars for this func are reserved
   570  			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
   571  			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
   572  			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
   573  			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
   574  			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
   575  			if arg3 == nil then
   576  				return 1
   577  			end
   578  			return 0
   579  		end
   580  
   581  		test(1,2)
   582  	`)
   583  }
   584  
   585  func BenchmarkCallFrameStackPushPopAutoGrow(t *testing.B) {
   586  	stack := newAutoGrowingCallFrameStack(256)
   587  
   588  	t.ResetTimer()
   589  
   590  	const Iterations = 256
   591  	for j := 0; j < t.N; j++ {
   592  		for i := 0; i < Iterations; i++ {
   593  			stack.Push(callFrame{})
   594  		}
   595  		for i := 0; i < Iterations; i++ {
   596  			stack.Pop()
   597  		}
   598  	}
   599  }
   600  
   601  func BenchmarkCallFrameStackPushPopFixed(t *testing.B) {
   602  	stack := newFixedCallFrameStack(256)
   603  
   604  	t.ResetTimer()
   605  
   606  	const Iterations = 256
   607  	for j := 0; j < t.N; j++ {
   608  		for i := 0; i < Iterations; i++ {
   609  			stack.Push(callFrame{})
   610  		}
   611  		for i := 0; i < Iterations; i++ {
   612  			stack.Pop()
   613  		}
   614  	}
   615  }
   616  
   617  // this test will intentionally not incur stack growth in order to bench the performance when no allocations happen
   618  func BenchmarkCallFrameStackPushPopShallowAutoGrow(t *testing.B) {
   619  	stack := newAutoGrowingCallFrameStack(256)
   620  
   621  	t.ResetTimer()
   622  
   623  	const Iterations = 8
   624  	for j := 0; j < t.N; j++ {
   625  		for i := 0; i < Iterations; i++ {
   626  			stack.Push(callFrame{})
   627  		}
   628  		for i := 0; i < Iterations; i++ {
   629  			stack.Pop()
   630  		}
   631  	}
   632  }
   633  
   634  func BenchmarkCallFrameStackPushPopShallowFixed(t *testing.B) {
   635  	stack := newFixedCallFrameStack(256)
   636  
   637  	t.ResetTimer()
   638  
   639  	const Iterations = 8
   640  	for j := 0; j < t.N; j++ {
   641  		for i := 0; i < Iterations; i++ {
   642  			stack.Push(callFrame{})
   643  		}
   644  		for i := 0; i < Iterations; i++ {
   645  			stack.Pop()
   646  		}
   647  	}
   648  }
   649  
   650  func BenchmarkCallFrameStackPushPopFixedNoInterface(t *testing.B) {
   651  	stack := newFixedCallFrameStack(256).(*fixedCallFrameStack)
   652  
   653  	t.ResetTimer()
   654  
   655  	const Iterations = 256
   656  	for j := 0; j < t.N; j++ {
   657  		for i := 0; i < Iterations; i++ {
   658  			stack.Push(callFrame{})
   659  		}
   660  		for i := 0; i < Iterations; i++ {
   661  			stack.Pop()
   662  		}
   663  	}
   664  }
   665  
   666  func BenchmarkCallFrameStackUnwindAutoGrow(t *testing.B) {
   667  	stack := newAutoGrowingCallFrameStack(256)
   668  
   669  	t.ResetTimer()
   670  
   671  	const Iterations = 256
   672  	for j := 0; j < t.N; j++ {
   673  		for i := 0; i < Iterations; i++ {
   674  			stack.Push(callFrame{})
   675  		}
   676  		stack.SetSp(0)
   677  	}
   678  }
   679  
   680  func BenchmarkCallFrameStackUnwindFixed(t *testing.B) {
   681  	stack := newFixedCallFrameStack(256)
   682  
   683  	t.ResetTimer()
   684  
   685  	const Iterations = 256
   686  	for j := 0; j < t.N; j++ {
   687  		for i := 0; i < Iterations; i++ {
   688  			stack.Push(callFrame{})
   689  		}
   690  		stack.SetSp(0)
   691  	}
   692  }
   693  
   694  func BenchmarkCallFrameStackUnwindFixedNoInterface(t *testing.B) {
   695  	stack := newFixedCallFrameStack(256).(*fixedCallFrameStack)
   696  
   697  	t.ResetTimer()
   698  
   699  	const Iterations = 256
   700  	for j := 0; j < t.N; j++ {
   701  		for i := 0; i < Iterations; i++ {
   702  			stack.Push(callFrame{})
   703  		}
   704  		stack.SetSp(0)
   705  	}
   706  }
   707  
   708  type registryTestHandler int
   709  
   710  func (registryTestHandler) registryOverflow() {
   711  	panic("registry overflow")
   712  }
   713  
   714  // test pushing and popping from the registry
   715  func BenchmarkRegistryPushPopAutoGrow(t *testing.B) {
   716  	al := newAllocator(32)
   717  	sz := 256 * 20
   718  	reg := newRegistry(registryTestHandler(0), sz/2, 64, sz, al)
   719  	value := LString("test")
   720  
   721  	t.ResetTimer()
   722  
   723  	for j := 0; j < t.N; j++ {
   724  		for i := 0; i < sz; i++ {
   725  			reg.Push(value)
   726  		}
   727  		for i := 0; i < sz; i++ {
   728  			reg.Pop()
   729  		}
   730  	}
   731  }
   732  
   733  func BenchmarkRegistryPushPopFixed(t *testing.B) {
   734  	al := newAllocator(32)
   735  	sz := 256 * 20
   736  	reg := newRegistry(registryTestHandler(0), sz, 0, sz, al)
   737  	value := LString("test")
   738  
   739  	t.ResetTimer()
   740  
   741  	for j := 0; j < t.N; j++ {
   742  		for i := 0; i < sz; i++ {
   743  			reg.Push(value)
   744  		}
   745  		for i := 0; i < sz; i++ {
   746  			reg.Pop()
   747  		}
   748  	}
   749  }
   750  
   751  func BenchmarkRegistrySetTop(t *testing.B) {
   752  	al := newAllocator(32)
   753  	sz := 256 * 20
   754  	reg := newRegistry(registryTestHandler(0), sz, 32, sz*2, al)
   755  
   756  	t.ResetTimer()
   757  
   758  	for j := 0; j < t.N; j++ {
   759  		reg.SetTop(sz)
   760  		reg.SetTop(0)
   761  	}
   762  }