github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/object/print.go (about)

     1  package object
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  
     8  	"github.com/hirochachacha/plua/internal/strconv"
     9  	"github.com/hirochachacha/plua/internal/version"
    10  	"github.com/hirochachacha/plua/opcode"
    11  )
    12  
    13  func PrintError(err error) error {
    14  	return FprintError(os.Stderr, err)
    15  }
    16  
    17  func FprintError(w io.Writer, err error) error {
    18  	return fprintError(w, err)
    19  }
    20  
    21  type errWriter struct {
    22  	w   io.Writer
    23  	err error
    24  }
    25  
    26  func (w *errWriter) Write(p []byte) (n int, err error) {
    27  	if w.err == nil {
    28  		n, w.err = w.w.Write(p)
    29  	}
    30  	return n, w.err
    31  }
    32  
    33  func fprintError(w io.Writer, err error) error {
    34  	ew := &errWriter{w: w}
    35  	if rerr, ok := err.(*RuntimeError); ok {
    36  		fmt.Fprintln(ew, rerr)
    37  		fmt.Fprint(ew, "stack traceback:")
    38  		tb := rerr.Traceback
    39  		if len(tb) <= 22 {
    40  			for _, st := range tb {
    41  				printStackTrace(ew, st)
    42  			}
    43  		} else {
    44  			for _, st := range tb[:10] {
    45  				printStackTrace(ew, st)
    46  			}
    47  			fmt.Fprint(ew, "\n\t")
    48  			fmt.Fprint(ew, "...")
    49  			for _, st := range tb[len(tb)-11:] {
    50  				printStackTrace(ew, st)
    51  			}
    52  		}
    53  		fmt.Fprintln(ew)
    54  	} else {
    55  		fmt.Fprintln(ew, err)
    56  	}
    57  	return ew.err
    58  }
    59  
    60  func printStackTrace(w io.Writer, st *StackTrace) {
    61  	fmt.Fprint(w, "\n\t")
    62  
    63  	var write bool
    64  
    65  	if st.Source != "" {
    66  		fmt.Fprint(w, st.Source)
    67  		fmt.Fprint(w, ":")
    68  		write = true
    69  	}
    70  
    71  	if st.Line > 0 {
    72  		fmt.Fprint(w, st.Line)
    73  		fmt.Fprint(w, ":")
    74  		write = true
    75  	}
    76  
    77  	if write {
    78  		fmt.Fprint(w, " in ")
    79  	}
    80  
    81  	fmt.Fprint(w, st.Signature)
    82  
    83  	if st.IsTailCall {
    84  		fmt.Fprint(w, "\n\t")
    85  		fmt.Fprint(w, "(...tail calls...)")
    86  	}
    87  }
    88  
    89  func PrintProto(p *Proto) error {
    90  	return FprintProto(os.Stdout, p)
    91  }
    92  
    93  func FprintProto(w io.Writer, p *Proto) error {
    94  	pr := &printer{w: w}
    95  	pr.printFunc(p)
    96  	return pr.err
    97  }
    98  
    99  type printer struct {
   100  	w   io.Writer
   101  	err error
   102  }
   103  
   104  func (pr *printer) Write(p []byte) (n int, err error) {
   105  	if pr.err == nil {
   106  		n, pr.err = pr.w.Write(p)
   107  	}
   108  	return n, pr.err
   109  }
   110  
   111  func (pr *printer) printf(s string, args ...interface{}) {
   112  	fmt.Fprintf(pr, s, args...)
   113  }
   114  
   115  func (pr *printer) print(args ...interface{}) {
   116  	fmt.Fprint(pr, args...)
   117  }
   118  
   119  func (pr *printer) println(args ...interface{}) {
   120  	fmt.Fprintln(pr, args...)
   121  }
   122  
   123  func (pr *printer) printFunc(p *Proto) {
   124  	pr.printHeader(p)
   125  	pr.printCode(p)
   126  	pr.printConstants(p)
   127  	pr.printLocals(p)
   128  	pr.printUpvalues(p)
   129  	pr.printProtos(p)
   130  }
   131  
   132  func (pr *printer) printHeader(p *Proto) {
   133  	s := "=?"
   134  	if len(p.Source) != 0 {
   135  		s = string(p.Source)
   136  	}
   137  
   138  	if s[0] == '@' || s[0] == '=' {
   139  		s = s[1:]
   140  	} else if s[:len(version.LUA_SIGNATURE)] == version.LUA_SIGNATURE {
   141  		s = "(bstring)"
   142  	} else {
   143  		s = "(string)"
   144  	}
   145  
   146  	var typ string
   147  	if p.LineDefined == 0 {
   148  		typ = "main"
   149  	} else {
   150  		typ = "function"
   151  	}
   152  
   153  	pr.printf(
   154  		"\n%s <%s:%d,%d> (%d instructions at %p)\n",
   155  		typ, s, p.LineDefined, p.LastLineDefined, len(p.Code), p,
   156  	)
   157  
   158  	var vararg string
   159  	if p.IsVararg {
   160  		vararg = "+"
   161  	}
   162  
   163  	pr.printf(
   164  		"%d%s params, %d slots, %d upvalues, ",
   165  		p.NParams, vararg, p.MaxStackSize, len(p.Upvalues),
   166  	)
   167  
   168  	pr.printf(
   169  		"%d locals, %d constants, %d functions\n",
   170  		len(p.LocVars), len(p.Constants), len(p.Protos),
   171  	)
   172  }
   173  
   174  func (pr *printer) printValue(val Value) {
   175  	if val, ok := val.(String); ok {
   176  		pr.print(strconv.Quote(string(val)))
   177  
   178  		return
   179  	}
   180  
   181  	pr.print(val)
   182  }
   183  
   184  func (pr *printer) printCode(p *Proto) {
   185  	for pc, code := range p.Code {
   186  		a := code.A()
   187  		b := code.B()
   188  		c := code.C()
   189  		bx := code.Bx()
   190  		ax := code.Ax()
   191  		sbx := code.SBx()
   192  
   193  		pr.printf("\t%d\t", pc+1)
   194  
   195  		if p.LineInfo != nil {
   196  			pr.printf("[%d]\t", p.LineInfo[pc])
   197  		} else {
   198  			pr.printf("[-]\t")
   199  		}
   200  
   201  		pr.printf("%-9s\t", code.OpName())
   202  
   203  		switch code.OpMode() {
   204  		case opcode.IABC:
   205  			pr.printf("%d", a)
   206  			if code.BMode() != opcode.OpArgN {
   207  				if b&opcode.BitRK != 0 {
   208  					pr.printf(" %d", -1-(b & ^opcode.BitRK))
   209  				} else {
   210  					pr.printf(" %d", b)
   211  				}
   212  			}
   213  			if code.CMode() != opcode.OpArgN {
   214  				if c&opcode.BitRK != 0 {
   215  					pr.printf(" %d", -1-(c & ^opcode.BitRK))
   216  				} else {
   217  					pr.printf(" %d", c)
   218  				}
   219  			}
   220  		case opcode.IABx:
   221  			pr.printf("%d", a)
   222  			switch code.BMode() {
   223  			case opcode.OpArgK:
   224  				pr.printf(" %d", -1-bx)
   225  			case opcode.OpArgU:
   226  				pr.printf(" %d", bx)
   227  			}
   228  		case opcode.IAsBx:
   229  			pr.printf("%d %d", a, sbx)
   230  		case opcode.IAx:
   231  			pr.printf("%d", -1-ax)
   232  		default:
   233  			panic("unreachable")
   234  		}
   235  
   236  		switch code.OpCode() {
   237  		case opcode.LOADK:
   238  			pr.print("\t; ")
   239  			pr.printValue(p.Constants[bx])
   240  		case opcode.GETUPVAL, opcode.SETUPVAL:
   241  			pr.print("\t; ")
   242  			pr.print(upvalName(p, b))
   243  		case opcode.GETTABUP:
   244  			pr.print("\t; ")
   245  			pr.print(upvalName(p, b))
   246  			if c&opcode.BitRK != 0 {
   247  				pr.print(" ")
   248  				pr.printValue(p.Constants[c & ^opcode.BitRK])
   249  			}
   250  		case opcode.SETTABUP:
   251  			pr.print("\t; ")
   252  			pr.print(upvalName(p, a))
   253  			if b&opcode.BitRK != 0 {
   254  				pr.print(" ")
   255  				pr.printValue(p.Constants[b & ^opcode.BitRK])
   256  			}
   257  			if c&opcode.BitRK != 0 {
   258  				pr.print(" ")
   259  				pr.printValue(p.Constants[c & ^opcode.BitRK])
   260  			}
   261  		case opcode.GETTABLE, opcode.SELF:
   262  			if c&opcode.BitRK != 0 {
   263  				pr.print("\t; ")
   264  				pr.printValue(p.Constants[c & ^opcode.BitRK])
   265  			}
   266  		case opcode.SETTABLE, opcode.ADD, opcode.SUB, opcode.MUL,
   267  			opcode.POW, opcode.DIV, opcode.IDIV, opcode.BAND,
   268  			opcode.BOR, opcode.BXOR, opcode.SHL, opcode.SHR,
   269  			opcode.EQ, opcode.LT, opcode.LE:
   270  			if b&opcode.BitRK != 0 || c&opcode.BitRK != 0 {
   271  				pr.print("\t; ")
   272  				if b&opcode.BitRK != 0 {
   273  					pr.printValue(p.Constants[b & ^opcode.BitRK])
   274  				} else {
   275  					pr.print("-")
   276  				}
   277  
   278  				pr.print(" ")
   279  
   280  				if c&opcode.BitRK != 0 {
   281  					pr.printValue(p.Constants[c & ^opcode.BitRK])
   282  				} else {
   283  					pr.print("-")
   284  				}
   285  			}
   286  		case opcode.JMP, opcode.FORLOOP, opcode.FORPREP, opcode.TFORLOOP:
   287  			pr.printf("\t; to %d", sbx+pc+2)
   288  		case opcode.CLOSURE:
   289  			pr.printf("\t; %p", p.Protos[bx])
   290  		case opcode.SETLIST:
   291  			if c == 0 {
   292  				pc++
   293  				pr.printf("\t; %d", p.Code[pc])
   294  			} else {
   295  				pr.printf("\t; %d", c)
   296  			}
   297  		}
   298  
   299  		pr.print("\n")
   300  	}
   301  }
   302  
   303  func (pr *printer) printConstants(p *Proto) {
   304  	pr.printf("constants (%d) for %p: \n", len(p.Constants), p)
   305  	for i, c := range p.Constants {
   306  		pr.printf("\t%d\t", i+1)
   307  		pr.printValue(c)
   308  		pr.println()
   309  	}
   310  }
   311  
   312  func (pr *printer) printLocals(p *Proto) {
   313  	pr.printf("locals (%d) for %p: \n", len(p.LocVars), p)
   314  	for i, locvar := range p.LocVars {
   315  		pr.printf("\t%d\t%s\t%d\t%d\n", i, locvar.Name, locvar.StartPC, locvar.EndPC)
   316  	}
   317  }
   318  
   319  func (pr *printer) printUpvalues(p *Proto) {
   320  	pr.printf("upvalues (%d) for %p: \n", len(p.Upvalues), p)
   321  	for i, upval := range p.Upvalues {
   322  		pr.printf("\t%d\t%s\t%t\t%d\n", i, upval.Name, upval.Instack, upval.Index)
   323  	}
   324  }
   325  
   326  func (pr *printer) printProtos(p *Proto) {
   327  	for _, f := range p.Protos {
   328  		pr.printFunc(f)
   329  	}
   330  }
   331  
   332  func upvalName(p *Proto, r int) (name string) {
   333  	name = string(p.Upvalues[r].Name)
   334  	if len(name) == 0 {
   335  		name = "-"
   336  	}
   337  	return
   338  }