github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/stdlib/debug/debug.go (about) 1 package debug 2 3 import ( 4 "bufio" 5 "fmt" 6 "os" 7 8 "github.com/hirochachacha/plua/internal/compiler_pool" 9 "github.com/hirochachacha/plua/object" 10 "github.com/hirochachacha/plua/object/fnutil" 11 ) 12 13 // debug() 14 func debug(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 15 stdin := bufio.NewScanner(os.Stdin) 16 17 for { 18 _, e := os.Stderr.WriteString("lua_debug> ") 19 if e != nil { 20 return nil, object.NewRuntimeError(e.Error()) 21 } 22 23 if !stdin.Scan() { 24 if e := stdin.Err(); e != nil { 25 return nil, object.NewRuntimeError(e.Error()) 26 } 27 28 return nil, nil 29 } 30 31 line := stdin.Text() 32 if line == "cont" { 33 return nil, nil 34 } 35 36 p, err := compiler_pool.CompileString(line, "=(debug command)", 0) 37 if err != nil { 38 return nil, err 39 } 40 41 rets, err := th.Call(th.NewClosure(p)) 42 if err != nil { 43 return nil, err 44 } 45 46 if len(rets) != 0 { 47 s := object.Repr(rets[0]) 48 49 _, e := fmt.Fprintf(os.Stderr, "\n%s\n", s) 50 if e != nil { 51 return nil, object.NewRuntimeError(e.Error()) 52 } 53 } 54 } 55 } 56 57 // gethook([thread]) -> (fn, mask, count) 58 func gethook(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 59 ap := fnutil.NewArgParser(th, args) 60 61 th1 := ap.GetThread() 62 63 hook, mask, count := th1.GetHook() 64 65 return []object.Value{hook, object.String(mask), object.Integer(count)}, nil 66 } 67 68 // getinfo([thread,] f [, what]) -> debug_info 69 func getinfo(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 70 ap := fnutil.NewArgParser(th, args) 71 72 th1 := ap.GetThread() 73 74 f, err := ap.ToTypes(0, object.TNUMINT, object.TFUNCTION) 75 if err != nil { 76 return nil, err 77 } 78 79 what, err := ap.OptGoString(1, "flnStu") 80 if err != nil { 81 return nil, err 82 } 83 84 opts := map[rune]bool{ 85 'S': false, 86 'l': false, 87 'u': false, 88 't': false, 89 'n': false, 90 'L': false, 91 'f': false, 92 } 93 94 // check what and remove dups 95 { 96 var w string 97 for _, r := range what { 98 found, ok := opts[r] 99 if !ok { 100 return nil, ap.OptionError(1, string(r)) 101 } 102 if !found { 103 w += string(r) 104 105 opts[r] = true 106 } 107 } 108 what = w 109 } 110 111 var d *object.DebugInfo 112 113 switch f := f.(type) { 114 case object.Integer: 115 level, ok := object.ToGoInt(f) 116 if !ok { 117 return nil, nil 118 } 119 120 d = th1.GetInfo(level, what) 121 case object.GoFunction: 122 d = th1.GetInfoByFunc(f, what) 123 case object.Closure: 124 d = th1.GetInfoByFunc(f, what) 125 default: 126 panic("unreachable") 127 } 128 129 if d == nil { 130 return nil, nil 131 } 132 133 t := th.NewTableSize(0, 13) 134 135 if opts['S'] { 136 t.Set(object.String("source"), object.String(d.Source)) 137 t.Set(object.String("short_src"), object.String(d.ShortSource)) 138 t.Set(object.String("linedefined"), object.Integer(d.LineDefined)) 139 t.Set(object.String("lastlinedefined"), object.Integer(d.LastLineDefined)) 140 t.Set(object.String("what"), object.String(d.What)) 141 } 142 143 if opts['l'] { 144 t.Set(object.String("currentline"), object.Integer(d.CurrentLine)) 145 } 146 147 if opts['u'] { 148 t.Set(object.String("nups"), object.Integer(d.NUpvalues)) 149 t.Set(object.String("nparams"), object.Integer(d.NParams)) 150 if d.IsVararg { 151 t.Set(object.String("isvararg"), object.True) 152 } else { 153 t.Set(object.String("isvararg"), object.False) 154 } 155 } 156 157 if opts['n'] { 158 if d.Name != "" { 159 t.Set(object.String("name"), object.String(d.Name)) 160 } 161 t.Set(object.String("namewhat"), object.String(d.NameWhat)) 162 } 163 164 if opts['t'] { 165 if d.IsTailCall { 166 t.Set(object.String("istailcall"), object.True) 167 } else { 168 t.Set(object.String("istailcall"), object.False) 169 } 170 } 171 172 if opts['L'] { 173 t.Set(object.String("activelines"), d.Lines) 174 } 175 176 if opts['f'] { 177 t.Set(object.String("func"), d.Func) 178 } 179 180 return []object.Value{t}, nil 181 } 182 183 // getlocal([thread,] f, local) 184 func getlocal(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 185 ap := fnutil.NewArgParser(th, args) 186 187 th1 := ap.GetThread() 188 189 f, err := ap.ToTypes(0, object.TNUMINT, object.TFUNCTION) 190 if err != nil { 191 return nil, err 192 } 193 194 local, err := ap.ToGoInt(1) 195 if err != nil { 196 return nil, err 197 } 198 199 switch f := f.(type) { 200 case object.Integer: 201 level, ok := object.ToGoInt(f) 202 if !ok { 203 return nil, nil 204 } 205 206 d := th1.GetInfo(level, "") 207 208 if d == nil { 209 return nil, ap.ArgError(0, "level out of range") 210 } 211 212 name, val := th1.GetLocal(level, local) 213 if name == "" && val == nil { 214 return nil, nil 215 } 216 217 return []object.Value{object.String(name), val}, nil 218 case object.GoFunction: 219 name := th1.GetLocalName(f, local) 220 if name == "" { 221 return nil, nil 222 } 223 224 return []object.Value{object.String(name)}, nil 225 case object.Closure: 226 name := th1.GetLocalName(f, local) 227 if name == "" { 228 return nil, nil 229 } 230 231 return []object.Value{object.String(name)}, nil 232 default: 233 panic("unreachable") 234 } 235 } 236 237 func getmetatable(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 238 ap := fnutil.NewArgParser(th, args) 239 240 val, err := ap.ToValue(0) 241 if err != nil { 242 return nil, err 243 } 244 245 return []object.Value{th.GetMetatable(val)}, nil 246 } 247 248 func getregistry(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 249 return nil, object.NewRuntimeError("not implemented") 250 } 251 252 // getupvalue(f, up) 253 func getupvalue(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 254 ap := fnutil.NewArgParser(th, args) 255 256 f, err := ap.ToFunction(0) 257 if err != nil { 258 return nil, err 259 } 260 261 up, err := ap.ToGoInt(1) 262 if err != nil { 263 return nil, err 264 } 265 266 switch f := f.(type) { 267 case object.GoFunction: 268 return nil, nil 269 case object.Closure: 270 if up <= 0 || f.NUpvalues() < up { 271 return nil, nil 272 } 273 274 name := f.GetUpvalueName(int(up - 1)) 275 if name == "" { 276 name = "(*no name)" 277 } 278 279 val := f.GetUpvalue(int(up - 1)) 280 281 return []object.Value{object.String(name), val}, nil 282 default: 283 panic("unreachable") 284 } 285 } 286 287 // getuservalue(u) 288 func getuservalue(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 289 if len(args) == 0 { 290 return nil, nil 291 } 292 ud, ok := args[0].(*object.Userdata) 293 if !ok { 294 return nil, nil 295 } 296 297 val, ok := ud.Value.(object.Value) 298 if !ok { 299 return nil, nil 300 } 301 302 return []object.Value{val}, nil 303 } 304 305 // sethook([thread,] hook, mask [, count]) or sethook() 306 func sethook(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 307 if len(args) == 0 { 308 th.SetHook(nil, "", 0) 309 310 return nil, nil 311 } 312 313 ap := fnutil.NewArgParser(th, args) 314 315 th1 := ap.GetThread() 316 317 hook, err := ap.ToFunctionOrNil(0) 318 if err != nil { 319 return nil, err 320 } 321 322 mask, err := ap.OptGoString(1, "") 323 if err != nil { 324 return nil, err 325 } 326 327 count, err := ap.OptGoInt(2, 0) 328 if err != nil { 329 return nil, err 330 } 331 332 th1.SetHook(hook, mask, count) 333 334 return nil, nil 335 } 336 337 // setlocal([thread,] level, local, value) 338 func setlocal(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 339 ap := fnutil.NewArgParser(th, args) 340 341 th1 := ap.GetThread() 342 343 level, err := ap.ToGoInt(0) 344 if err != nil { 345 return nil, err 346 } 347 348 local, err := ap.ToGoInt(1) 349 if err != nil { 350 return nil, err 351 } 352 353 val, err := ap.ToValue(2) 354 if err != nil { 355 return nil, err 356 } 357 358 d := th1.GetInfo(level, "") 359 if d == nil { 360 return nil, ap.ArgError(1, "level out of range") 361 } 362 363 name := th1.SetLocal(level, local, val) 364 if name == "" { 365 return nil, nil 366 } 367 368 return []object.Value{object.String(name)}, nil 369 } 370 371 // setmetatable(value, table) 372 func setmetatable(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 373 ap := fnutil.NewArgParser(th, args) 374 375 val, err := ap.ToValue(0) 376 if err != nil { 377 return nil, err 378 } 379 380 mt, err := ap.ToTypes(1, object.TNIL, object.TTABLE) 381 if err != nil { 382 return nil, err 383 } 384 385 switch mt := mt.(type) { 386 case nil: 387 th.SetMetatable(val, nil) 388 case object.Table: 389 th.SetMetatable(val, mt) 390 default: 391 panic("unreachable") 392 } 393 394 return nil, nil 395 } 396 397 // setupvalue(f, up, value) 398 func setupvalue(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 399 ap := fnutil.NewArgParser(th, args) 400 401 f, err := ap.ToFunction(0) 402 if err != nil { 403 return nil, err 404 } 405 406 up, err := ap.ToGoInt(1) 407 if err != nil { 408 return nil, err 409 } 410 411 val, err := ap.ToValue(2) 412 if err != nil { 413 return nil, err 414 } 415 416 switch f := f.(type) { 417 case object.GoFunction: 418 return nil, nil 419 case object.Closure: 420 if up <= 0 || f.NUpvalues() < up { 421 return nil, nil 422 } 423 424 name := f.GetUpvalueName(int(up - 1)) 425 if name == "" { 426 name = "(*no name)" 427 } 428 429 f.SetUpvalue(int(up-1), val) 430 431 return []object.Value{object.String(name)}, nil 432 default: 433 panic("unreachable") 434 } 435 } 436 437 func setuservalue(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 438 ap := fnutil.NewArgParser(th, args) 439 440 ud, err := ap.ToFullUserdata(0) 441 if err != nil { 442 return nil, err 443 } 444 445 val, err := ap.ToValue(1) 446 if err != nil { 447 return nil, err 448 } 449 450 ud.Value = val 451 452 return nil, nil 453 } 454 455 // traceback([thread,] [message [, level]]) 456 func traceback(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 457 ap := fnutil.NewArgParser(th, args) 458 459 th1 := ap.GetThread() 460 461 hasmsg := true 462 463 msg, err := ap.ToGoString(0) 464 if err != nil { 465 if val, _ := ap.Get(0); val != nil { 466 return []object.Value{val}, nil 467 } 468 hasmsg = false 469 } 470 471 l := 0 472 if th == th1 { 473 l = 1 474 } 475 476 level, err := ap.OptGoInt(1, l) 477 if err != nil { 478 return nil, err 479 } 480 481 tb := getTraceback(th1, msg, level, hasmsg) 482 483 return []object.Value{object.String(tb)}, nil 484 } 485 486 // upvalueid(f, n) 487 func upvalueid(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 488 ap := fnutil.NewArgParser(th, args) 489 490 f, err := ap.ToFunction(0) 491 if err != nil { 492 return nil, err 493 } 494 495 up, err := ap.ToGoInt(1) 496 if err != nil { 497 return nil, err 498 } 499 500 switch f := f.(type) { 501 case object.GoFunction: 502 return nil, nil 503 case object.Closure: 504 if up < 1 || up > f.NUpvalues() { 505 return nil, ap.ArgError(1, "invalid upvalue index") 506 } 507 508 id := f.GetUpvalueId(int(up - 1)) 509 510 return []object.Value{id}, nil 511 default: 512 panic("unreachable") 513 } 514 } 515 516 // upvaluejoin(f1, n1, f2, n2) 517 func upvaluejoin(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 518 ap := fnutil.NewArgParser(th, args) 519 520 f1, err := ap.ToClosure(0) 521 if err != nil { 522 return nil, err 523 } 524 525 n1, err := ap.ToGoInt(1) 526 if err != nil { 527 return nil, err 528 } 529 530 f2, err := ap.ToClosure(2) 531 if err != nil { 532 return nil, err 533 } 534 535 n2, err := ap.ToGoInt(3) 536 if err != nil { 537 return nil, err 538 } 539 540 if n1 < 1 || n1 > f1.NUpvalues() { 541 return nil, ap.ArgError(1, "invalid upvalue index") 542 } 543 544 if n2 < 1 || n2 > f2.NUpvalues() { 545 return nil, ap.ArgError(3, "invalid upvalue index") 546 } 547 548 f1.UpvalueJoin(n1-1, f2, n2-1) 549 550 return nil, nil 551 } 552 553 func Open(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 554 m := th.NewTableSize(0, 16) 555 556 m.Set(object.String("debug"), object.GoFunction(debug)) 557 m.Set(object.String("gethook"), object.GoFunction(gethook)) 558 m.Set(object.String("getinfo"), object.GoFunction(getinfo)) 559 m.Set(object.String("getlocal"), object.GoFunction(getlocal)) 560 m.Set(object.String("getmetatable"), object.GoFunction(getmetatable)) 561 m.Set(object.String("getregistry"), object.GoFunction(getregistry)) 562 m.Set(object.String("getupvalue"), object.GoFunction(getupvalue)) 563 m.Set(object.String("getuservalue"), object.GoFunction(getuservalue)) 564 m.Set(object.String("sethook"), object.GoFunction(sethook)) 565 m.Set(object.String("setlocal"), object.GoFunction(setlocal)) 566 m.Set(object.String("setmetatable"), object.GoFunction(setmetatable)) 567 m.Set(object.String("setupvalue"), object.GoFunction(setupvalue)) 568 m.Set(object.String("setuservalue"), object.GoFunction(setuservalue)) 569 m.Set(object.String("traceback"), object.GoFunction(traceback)) 570 m.Set(object.String("upvalueid"), object.GoFunction(upvalueid)) 571 m.Set(object.String("upvaluejoin"), object.GoFunction(upvaluejoin)) 572 573 return []object.Value{m}, nil 574 }