github.com/coyove/nj@v0.0.0-20221110084952-c7f8db1065c3/main_test.go (about) 1 package nj 2 3 import ( 4 "bytes" 5 "flag" 6 "fmt" 7 "go/parser" 8 "log" 9 "math" 10 "math/rand" 11 "os" 12 "reflect" 13 "runtime" 14 "runtime/pprof" 15 "strconv" 16 "strings" 17 "sync" 18 "testing" 19 "time" 20 21 "github.com/coyove/nj/bas" 22 "github.com/coyove/nj/internal" 23 _parser "github.com/coyove/nj/parser" 24 "github.com/coyove/nj/typ" 25 ) 26 27 func init() { 28 runtime.GOMAXPROCS(runtime.NumCPU() * 2) 29 log.SetFlags(log.Lshortfile | log.Ltime) 30 bas.AddTopValue("G", bas.Int(1)) 31 } 32 33 type testStruct struct { 34 A int 35 } 36 37 func (ts testStruct) Foo() int { return ts.A } 38 39 func (ts *testStruct) SetFoo(a int) { ts.A = a } 40 41 type testStructEmbed struct { 42 T testStruct 43 } 44 45 type testStructPtr struct { 46 T *testStruct 47 Next *testStructPtr 48 } 49 50 func runFile(t *testing.T, path string) { 51 if !flag.Parsed() { 52 flag.Parse() 53 } 54 55 b, err := LoadFile(path, &LoadOptions{ 56 Globals: bas.NewObject(0). 57 SetProp("syncMap", bas.ValueOf(&sync.Map{})). 58 SetProp("structAddrTest", bas.ValueOf(&testStruct{2})). 59 SetProp("structAddrTest2", bas.ValueOf(testStruct{3})). 60 SetProp("structAddrTestEmbed", bas.ValueOf(&testStructEmbed{testStruct{4}})). 61 SetProp("nativeVarargTest", bas.ValueOf(func(a ...int) int { 62 return len(a) 63 })). 64 SetProp("nativeVarargTest2", bas.ValueOf(func(b string, a ...int) string { 65 return b + strconv.Itoa(len(a)) 66 })). 67 SetProp("nativeVarargTest3", bas.ValueOf(func(s testStructPtr) int { 68 return s.T.A 69 })). 70 SetProp("nativeVarargTest4", bas.ValueOf(func(s *testStructPtr, v int) { 71 s.T.A = v 72 })). 73 SetProp("gomap", bas.ValueOf(func(m map[string]int, k string, v int) map[string]int { 74 m[k] = v 75 return m 76 })). 77 SetProp("intAlias", bas.ValueOf(func(d time.Duration) time.Time { 78 return time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC).Add(d) 79 })). 80 SetProp("goVarg", bas.ValueOf(func(a int, f func(a int, b ...int) int) int { 81 return f(a, a+1, a+2) 82 })). 83 SetProp("boolConvert", bas.ValueOf(func(v bool) { 84 if !v { 85 panic("bad") 86 } 87 })). 88 SetProp("findGlobal", bas.ValueOf(func(env *bas.Env) { 89 v, err := env.MustProgram().Get("G_FLAG") 90 fmt.Println(err) 91 if v.IsFalse() { 92 panic("findGlobal failed") 93 } 94 env.MustProgram().Set("G_FLAG", bas.Str("ok")) 95 fmt.Println("find global") 96 })). 97 SetProp("G", bas.Str("test")).ToMap(), 98 }) 99 if err != nil { 100 t.Fatal(err) 101 } 102 103 if internal.IsDebug() { 104 fmt.Println(b.GoString()) 105 } 106 107 out := b.Run() 108 if out.IsError() { 109 t.Fatal(out) 110 } 111 } 112 113 func TestFileTest(t *testing.T) { runFile(t, "tests/test.nj.lua") } 114 115 func TestFileStruct(t *testing.T) { runFile(t, "tests/struct.nj.lua") } 116 117 func TestFileString(t *testing.T) { runFile(t, "tests/string.nj.lua") } 118 119 func TestFileGoto(t *testing.T) { runFile(t, "tests/goto.nj.lua") } 120 121 func TestFileR2(t *testing.T) { runFile(t, "tests/r2.nj.lua") } 122 123 func TestFileStringIndex(t *testing.T) { runFile(t, "tests/indexstr.nj.lua") } 124 125 func TestFileCurry(t *testing.T) { runFile(t, "tests/curry.nj.lua") } 126 127 func TestFileEvaluator(t *testing.T) { runFile(t, "tests/eval.nj.lua") } 128 129 func TestReturnFunction(t *testing.T) { 130 { 131 cls, _ := LoadString(` 132 print(init) 133 a = init 134 function foo(n) 135 a=a+n 136 return a 137 end 138 return foo 139 `, &LoadOptions{Globals: bas.NewObject(0).SetProp("init", bas.Int(1)).ToMap()}) 140 v := cls.Run() 141 if v := v.Object().Call(nil, bas.Int64(10)); v.Int64() != 11 { 142 t.Fatal(v) 143 } 144 145 if v := v.Object().Call(nil, bas.Int64(100)); v.Int64() != 111 { 146 t.Fatal(v) 147 } 148 } 149 { 150 cls, _ := LoadString(` 151 a = 1 152 function foo(x) 153 if not x then return a end 154 for i=0,#(x) do 155 a=a+x[i] 156 end 157 return a 158 end 159 return foo 160 `, nil) 161 v := cls.Run() 162 if v := v.Object().Call(nil, bas.Array(bas.Int64(1), bas.Int64(2), bas.Int64(3), bas.Int64(4))); v.Int64() != 11 { 163 t.Fatal(v) 164 } 165 166 if v := v.Object().Call(nil, bas.Array(bas.Int64(10), bas.Int64(20))); v.Int64() != 41 { 167 t.Fatal(v) 168 } 169 } 170 } 171 172 func TestTailCallPanic(t *testing.T) { 173 cls, err := LoadString(` 174 x = 0 175 function foo() 176 x=x+1 177 if x == 1e5 then assert(false) end 178 foo() 179 end 180 foo() 181 `, nil) 182 fmt.Println(err, cls.GoString()) 183 if s := cls.GoString(); !strings.Contains(s, "tailcall") { 184 t.Fatal(s) 185 } 186 187 outErr := cls.Run() 188 if !outErr.IsError() { 189 t.FailNow() 190 } 191 if len(outErr.String()) > 1e6 { // error too long, which means tail call is not effective 192 t.Fatal(len(err.Error())) 193 } 194 } 195 196 func TestArithmeticUnfold(t *testing.T) { 197 cls, err := LoadString(` 198 return 1 + 2 * 3 / 4 199 `, nil) 200 if err != nil { 201 t.Error(err) 202 } 203 204 if v := cls.Run(); v.Float64() != 2.5 { 205 t.Error("exec failed") 206 } 207 } 208 209 func TestArithmeticNAN(t *testing.T) { 210 cls, err := LoadString(` 211 a = 0 212 return (1 / a + 1) * a 213 `, nil) 214 if err != nil { 215 t.Error(err) 216 } 217 218 if v := cls.Run(); !math.IsNaN(v.Float64()) { 219 t.Error("wrong answer") 220 } 221 } 222 223 func init() { 224 if os.Getenv("njb") == "1" { 225 f, err := os.Create("cpuprofile") 226 if err != nil { 227 log.Fatal(err) 228 } 229 pprof.StartCPUProfile(f) 230 fmt.Println("cpuprofile") 231 for i := 0; i < 1e5; i++ { 232 // _parser.Parse("(a+1)", "") 233 LoadString("(a+1)", nil) 234 } 235 pprof.StopCPUProfile() 236 } 237 } 238 239 func BenchmarkParsing(b *testing.B) { 240 for i := 0; i < b.N; i++ { 241 _parser.Parse("(a+1)", "") 242 } 243 } 244 245 func BenchmarkCompiling(b *testing.B) { 246 for i := 0; i < b.N; i++ { 247 LoadString("(a+1)", nil) 248 } 249 } 250 251 func BenchmarkGoCompiling(b *testing.B) { 252 for i := 0; i < b.N; i++ { 253 parser.ParseExpr("(a+1)") 254 } 255 } 256 257 func TestBigList(t *testing.T) { 258 n := typ.RegMaxAddress/2 - bas.TopSymbols().Len() 259 260 makeCode := func(n int) string { 261 buf := bytes.Buffer{} 262 for i := 0; i < n; i++ { 263 buf.WriteString(fmt.Sprintf("a%d = %d\n", i, i)) 264 } 265 buf.WriteString("return [") 266 for i := 0; i < n; i++ { 267 buf.WriteString(fmt.Sprintf("a%d,", i)) 268 } 269 buf.Truncate(buf.Len() - 1) 270 return buf.String() + "]" 271 } 272 273 f, _ := LoadString(makeCode(n), nil) 274 // fmt.Println(f.GoString()) 275 v2 := f.Run() 276 if v2.IsError() { 277 t.Fatal(v2) 278 } 279 280 for i := 0; i < n; i++ { 281 if v2.Native().Get(i).Int() != i { 282 t.Fatal(v2) 283 } 284 } 285 286 start := time.Now() 287 _, err := LoadString(makeCode(typ.RegMaxAddress), nil) 288 fmt.Println("load", time.Since(start)) 289 if !strings.Contains(err.Error(), "too many") { 290 t.Fatal(err) 291 } 292 293 { 294 buf := bytes.NewBufferString("function foo(") 295 for i := 0; i < 256; i++ { 296 buf.WriteString(fmt.Sprintf("a%d,", i)) 297 } 298 buf.WriteString("x) end") 299 _, err = LoadString(buf.String(), nil) 300 if !strings.Contains(err.Error(), "too many") { 301 t.Fatal(err) 302 } 303 } 304 } 305 306 func TestPlainReturn(t *testing.T) { 307 if _, err := LoadString("return", nil); err != nil { 308 t.FailNow() 309 } 310 if _, err := LoadString("return ", nil); err != nil { 311 t.FailNow() 312 } 313 if _, err := LoadString("return \n ", nil); err != nil { 314 t.FailNow() 315 } 316 } 317 318 func TestFunctionClosure(t *testing.T) { 319 p, _ := LoadString(` local a = 0 320 function add () 321 a=a+1 322 return a 323 end 324 return add`, nil) 325 add := p.Run() 326 327 p2, _ := LoadString(` 328 local a = 100 329 return [a + add(), a + add(), a + add()] 330 `, &LoadOptions{Globals: bas.NewObject(0).SetProp("add", bas.ValueOf(add)).ToMap()}) 331 v := p2.Run() 332 if v.IsError() { 333 panic(v) 334 } 335 fmt.Println(p2.GoString()) 336 if v1 := v.Native().Values(); v1[0].Int64() != 101 || v1[1].Int64() != 102 || v1[2].Int64() != 103 { 337 t.Fatal(v, v1, p2.GoString()) 338 } 339 340 add = MustRun(LoadString("function foo(a) panic(a) end return function(b) foo(b) + 1 end", nil)) 341 outErr := add.Object().TryCall(nil, bas.Int(10)) 342 if outErr.Error().GetCause() != bas.Int(10) { 343 t.Fatal(outErr) 344 } 345 } 346 347 func TestNumberLexer(t *testing.T) { 348 assert := func(src string, v bas.Value) { 349 _, fn, ln, _ := runtime.Caller(1) 350 r := MustRun(LoadString(src, nil)) 351 if r != v { 352 n, _ := _parser.Parse(src, "") 353 n.Dump(os.Stdout) 354 t.Fatal(fn, ln, r, v) 355 } 356 } 357 assert("1 + 2 ", bas.Int64(3)) 358 assert("1+ 2 ", bas.Int64(3)) 359 assert("-1+ 2 ", bas.Int64(1)) 360 assert("1- 2 ", bas.Int64(-1)) 361 assert("(1+1)- 2 ", bas.Zero) 362 assert("1 - 2 ", bas.Int64(-1)) 363 assert("1 - -2 ", bas.Int64(3)) 364 assert("1- -2 ", bas.Int64(3)) 365 assert("1-2 ", bas.Int64(-1)) 366 assert("1.5 +2", bas.Float64(3.5)) 367 assert("1.5+ 2 ", bas.Float64(3.5)) 368 assert("12.5e-1+ 2 ", bas.Float64(3.25)) 369 assert("1.5e+1+ 2", bas.Float64(17)) 370 assert(".5+ 2 ", bas.Float64(2.5)) 371 assert("-.5+ 2", bas.Float64(1.5)) 372 assert("0x1+ 2", bas.Int64(3)) 373 assert("0xE+1 ", bas.Int64(15)) 374 assert(".5E+1 ", bas.Int64(5)) 375 assert("0x1_2_e+1", bas.Int64(0x12f)) 376 assert("([[1]])[0]", bas.Int(49)) 377 assert("'1'[0]", bas.Int(49)) 378 assert("([ [1] ])[0][0]", bas.Int(1)) 379 assert("([ [1]])[0][0]", bas.Int(1)) 380 assert("[0,1,2][1]", bas.Int(1)) 381 assert("[[%d]]['format'](1)", bas.Str("1")) 382 assert("function()end (1)", bas.Int(1)) 383 assert("function()end [1][0]", bas.Int(1)) 384 assert("function() return 1 end()", bas.Int(1)) 385 assert("function() return-1 end()", bas.Int(-1)) 386 } 387 388 func TestSmallString(t *testing.T) { 389 rand.Seed(time.Now().Unix()) 390 randString := func() string { 391 buf := make([]byte, rand.Intn(10)) 392 for i := range buf { 393 buf[i] = byte(rand.Intn(256)) 394 } 395 return string(buf) 396 } 397 for i := 0; i < 1e6; i++ { 398 v := randString() 399 if bas.Str(v).Str() != v { 400 t.Fatal(bas.Str(v).UnsafeInt64(), v) 401 } 402 } 403 } 404 405 func TestStrLess(t *testing.T) { 406 a := bas.Str("a") 407 b := bas.Str("a\x00") 408 t.Log(a, b) 409 if !a.Less(b) { 410 t.FailNow() 411 } 412 } 413 414 func TestACall(t *testing.T) { 415 foo := MustRun(LoadString(`function foo(m...) 416 print(m) 417 assert(m[1] == 1 and m[2] == 2) 418 a0 = 0 a1 = 1 a2 = 2 a3 = 3 419 end 420 return foo`, nil)) 421 foo.Object().Call(nil, bas.Nil, bas.Int64(1), bas.Int64(2)) 422 423 foo = MustRun(LoadString(`function foo(a, b, m...) 424 assert(a == 1 and #(m) == 0) 425 end 426 return foo`, nil)) 427 foo.Object().Call(nil, bas.Int64(1), bas.Nil) 428 429 foo = MustRun(LoadString(`m = {a=1} 430 function m.pow2() 431 return this.a * this.a 432 end 433 a = new(m, {a=10}) 434 return a`, nil)) 435 v := foo.Object().Get(bas.Str("pow2")).Object().Call(nil) 436 if v.Int64() != 100 { 437 t.Fatal(v) 438 } 439 440 foo = MustRun(LoadString(`m.a = 11 441 return m.pow2()`, &LoadOptions{ 442 Globals: bas.NewObject(0). 443 SetProp("m", bas.NewObject(0).SetPrototype(bas.NewObject(0). 444 SetProp("a", bas.Int64(0)). 445 AddMethod("pow2", func(e *bas.Env) { 446 i := e.Object(-1).Get(bas.Str("a")).Int64() 447 e.A = bas.Int64(i * i) 448 })).ToValue()). 449 ToMap(), 450 })) 451 if foo.Int64() != 121 { 452 t.Fatal(foo) 453 } 454 455 foo = MustRun(LoadString(`function foo(m...) 456 return sum(m.concat(m)...) + sum2(m[:2]...) 457 end 458 return foo`, &LoadOptions{ 459 Globals: bas.NewObject(0). 460 SetProp("sum", bas.ValueOf(func(a ...int) int { 461 s := 0 462 for _, a := range a { 463 s += a 464 } 465 return s 466 })). 467 SetProp("sum2", bas.ValueOf(func(a, b int) int { 468 return a + b 469 })). 470 ToMap(), 471 })) 472 v = foo.Object().Call(nil, bas.Int64(1), bas.Int64(2), bas.Int64(3)) 473 if v.Int64() != 15 { 474 t.Fatal(v) 475 } 476 } 477 478 func TestReflectedValue(t *testing.T) { 479 v := bas.Array(bas.True, bas.False) 480 x := v.ToType(reflect.TypeOf([2]bool{})).Interface().([2]bool) 481 if x[0] != true || x[1] != false { 482 t.Fatal(x) 483 } 484 v = bas.NewObject(2).SetProp("a", bas.Int64(1)).SetProp("b", bas.Int64(2)).ToValue() 485 y := v.ToType(reflect.TypeOf(map[string]byte{})).Interface().(map[string]byte) 486 if y["a"] != 1 || y["b"] != 2 { 487 t.Fatal(x) 488 } 489 490 p, _ := LoadString(`function foo(v, p) 491 p[0] = 99 492 return v, v + 1, nil 493 end 494 bar(foo)`, &LoadOptions{Globals: bas.NewObject(0). 495 SetProp("bar", bas.ValueOf(func(cb func(a int, p []byte) (int, int, error)) { 496 buf := []byte{0} 497 a, b, _ := cb(10, buf) 498 if a != 10 || b != 11 || buf[0] != 99 { 499 t.Fatal(a, b) 500 } 501 })).ToMap(), 502 }) 503 err := p.Run() 504 if err.IsError() { 505 t.Fatal(err) 506 } 507 } 508 509 // func TestRunTimeout(t *testing.T) { 510 // o := bas.NewObject(0) 511 // p, _ := LoadString("for i=0,1e8 do z.a = i end", &bas.Environment{ 512 // Globals: bas.NewObject(0).SetProp("z", o.ToValue()), 513 // }) 514 // 515 // p.Deadline = time.Now().Add(time.Second / 2) 516 // _, err := p.Run() 517 // if err.Error() != "timeout" { 518 // t.Fatal(err) 519 // } 520 // if v := o.Prop("a"); v.Maybe().Int(0) == 0 { 521 // t.Fatal(v) 522 // } 523 // 524 // p.Deadline = time.Now().Add(time.Second / 2) 525 // _, err = p.Run() 526 // if err.Error() != "timeout" { 527 // t.Fatal(err) 528 // } 529 // if v := o.Prop("a"); v.Maybe().Int64(0) == 0 { 530 // t.Fatal(v) 531 // } 532 // }