github.com/marcomrtt/gopher-lua@v0.1.1/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 }