script_test.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. package lua
  2. import (
  3. "fmt"
  4. "os"
  5. "runtime"
  6. "strings"
  7. "sync/atomic"
  8. "testing"
  9. "time"
  10. "github.com/yuin/gopher-lua/parse"
  11. )
  12. const maxMemory = 40
  13. var gluaTests []string = []string{
  14. "base.lua",
  15. "coroutine.lua",
  16. "db.lua",
  17. "issues.lua",
  18. "os.lua",
  19. "table.lua",
  20. "vm.lua",
  21. "math.lua",
  22. "strings.lua",
  23. "goto.lua",
  24. }
  25. var luaTests []string = []string{
  26. "attrib.lua",
  27. "calls.lua",
  28. "closure.lua",
  29. "constructs.lua",
  30. "events.lua",
  31. "literals.lua",
  32. "locals.lua",
  33. "math.lua",
  34. "sort.lua",
  35. "strings.lua",
  36. "vararg.lua",
  37. "pm.lua",
  38. "files.lua",
  39. }
  40. func testScriptCompile(t *testing.T, script string) {
  41. file, err := os.Open(script)
  42. if err != nil {
  43. t.Fatal(err)
  44. return
  45. }
  46. chunk, err2 := parse.Parse(file, script)
  47. if err2 != nil {
  48. t.Fatal(err2)
  49. return
  50. }
  51. parse.Dump(chunk)
  52. proto, err3 := Compile(chunk, script)
  53. if err3 != nil {
  54. t.Fatal(err3)
  55. return
  56. }
  57. nop := func(s string) {}
  58. nop(proto.String())
  59. }
  60. func testScriptDir(t *testing.T, tests []string, directory string) {
  61. if err := os.Chdir(directory); err != nil {
  62. t.Error(err)
  63. }
  64. defer os.Chdir("..")
  65. for _, script := range tests {
  66. fmt.Printf("testing %s/%s\n", directory, script)
  67. testScriptCompile(t, script)
  68. L := NewState(Options{
  69. RegistrySize: 1024 * 20,
  70. CallStackSize: 1024,
  71. IncludeGoStackTrace: true,
  72. })
  73. L.SetMx(maxMemory)
  74. if err := L.DoFile(script); err != nil {
  75. t.Error(err)
  76. }
  77. L.Close()
  78. }
  79. }
  80. var numActiveUserDatas int32 = 0
  81. type finalizerStub struct{ x byte }
  82. func allocFinalizerUserData(L *LState) int {
  83. ud := L.NewUserData()
  84. atomic.AddInt32(&numActiveUserDatas, 1)
  85. a := finalizerStub{}
  86. ud.Value = &a
  87. runtime.SetFinalizer(&a, func(aa *finalizerStub) {
  88. atomic.AddInt32(&numActiveUserDatas, -1)
  89. })
  90. L.Push(ud)
  91. return 1
  92. }
  93. func sleep(L *LState) int {
  94. time.Sleep(time.Duration(L.CheckInt(1)) * time.Millisecond)
  95. return 0
  96. }
  97. func countFinalizers(L *LState) int {
  98. L.Push(LNumber(numActiveUserDatas))
  99. return 1
  100. }
  101. // TestLocalVarFree verifies that tables and user user datas which are no longer referenced by the lua script are
  102. // correctly gc-ed. There was a bug in gopher lua where local vars were not being gc-ed in all circumstances.
  103. func TestLocalVarFree(t *testing.T) {
  104. s := `
  105. function Test(a, b, c)
  106. local a = { v = allocFinalizer() }
  107. local b = { v = allocFinalizer() }
  108. return a
  109. end
  110. Test(1,2,3)
  111. for i = 1, 100 do
  112. collectgarbage()
  113. if countFinalizers() == 0 then
  114. return
  115. end
  116. sleep(100)
  117. end
  118. error("user datas not finalized after 100 gcs")
  119. `
  120. L := NewState()
  121. L.SetGlobal("allocFinalizer", L.NewFunction(allocFinalizerUserData))
  122. L.SetGlobal("sleep", L.NewFunction(sleep))
  123. L.SetGlobal("countFinalizers", L.NewFunction(countFinalizers))
  124. defer L.Close()
  125. if err := L.DoString(s); err != nil {
  126. t.Error(err)
  127. }
  128. }
  129. func TestGlua(t *testing.T) {
  130. testScriptDir(t, gluaTests, "_glua-tests")
  131. }
  132. func TestLua(t *testing.T) {
  133. testScriptDir(t, luaTests, "_lua5.1-tests")
  134. }
  135. func TestMergingLoadNilBug(t *testing.T) {
  136. // there was a bug where a multiple load nils were being incorrectly merged, and the following code exposed it
  137. s := `
  138. function test()
  139. local a = 0
  140. local b = 1
  141. local c = 2
  142. local d = 3
  143. local e = 4 -- reg 4
  144. local f = 5
  145. local g = 6
  146. local h = 7
  147. if e == 4 then
  148. e = nil -- should clear reg 4, but clears regs 4-8 by mistake
  149. end
  150. if f == nil then
  151. error("bad f")
  152. end
  153. if g == nil then
  154. error("bad g")
  155. end
  156. if h == nil then
  157. error("bad h")
  158. end
  159. end
  160. test()
  161. `
  162. L := NewState()
  163. defer L.Close()
  164. if err := L.DoString(s); err != nil {
  165. t.Error(err)
  166. }
  167. }
  168. func TestMergingLoadNil(t *testing.T) {
  169. // multiple nil assignments to consecutive registers should be merged
  170. s := `
  171. function test()
  172. local a = 0
  173. local b = 1
  174. local c = 2
  175. -- this should generate just one LOADNIL byte code instruction
  176. a = nil
  177. b = nil
  178. c = nil
  179. print(a,b,c)
  180. end
  181. test()`
  182. chunk, err := parse.Parse(strings.NewReader(s), "test")
  183. if err != nil {
  184. t.Fatal(err)
  185. }
  186. compiled, err := Compile(chunk, "test")
  187. if err != nil {
  188. t.Fatal(err)
  189. }
  190. if len(compiled.FunctionPrototypes) != 1 {
  191. t.Fatal("expected 1 function prototype")
  192. }
  193. // there should be exactly 1 LOADNIL instruction in the byte code generated for the above
  194. // anymore, and the LOADNIL merging is not working correctly
  195. count := 0
  196. for _, instr := range compiled.FunctionPrototypes[0].Code {
  197. if opGetOpCode(instr) == OP_LOADNIL {
  198. count++
  199. }
  200. }
  201. if count != 1 {
  202. t.Fatalf("expected 1 LOADNIL instruction, found %d", count)
  203. }
  204. }