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  }