github.com/yuin/gopher-lua@v1.1.2-0.20231212122839-2348fd042596/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 }