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

     1  package dump
     2  
     3  import (
     4  	"encoding/binary"
     5  	"errors"
     6  	"io"
     7  
     8  	"github.com/hirochachacha/plua/internal/limits"
     9  	"github.com/hirochachacha/plua/internal/version"
    10  	"github.com/hirochachacha/plua/object"
    11  	"github.com/hirochachacha/plua/opcode"
    12  )
    13  
    14  var (
    15  	errIntegerOverflow    = &Error{errors.New("integer overflow")}
    16  	errFloatOverflow      = &Error{errors.New("float overflow")}
    17  	errFloatUnderflow     = &Error{errors.New("float underflow")}
    18  	errByteOverflow       = &Error{errors.New("byte overflow")}
    19  	errInvalidIntSize     = &Error{errors.New("IntSize should be power of 2")}
    20  	errInvalidSizeTSize   = &Error{errors.New("SizeTSize should be power of 2")}
    21  	errInvalidIntegerSize = &Error{errors.New("IntegerSize should be power of 2")}
    22  	errInvalidNumberSize  = &Error{errors.New("NumberSize should be 4 or 8")}
    23  	errInvalidByteOrder   = &Error{errors.New("ByteOrder should not be nil")}
    24  )
    25  
    26  type Mode uint
    27  
    28  const (
    29  	StripDebugInfo Mode = 1 << iota
    30  )
    31  
    32  func DumpTo(w io.Writer, p *object.Proto, mode Mode) (err error) {
    33  	return defaultConfig.DumpTo(w, p, mode)
    34  }
    35  
    36  var defaultConfig = &Config{
    37  	IntSize:     8,
    38  	SizeTSize:   8,
    39  	IntegerSize: 8,
    40  	NumberSize:  8,
    41  	ByteOrder:   binary.LittleEndian,
    42  }
    43  
    44  type Config struct {
    45  	IntSize     int              // size of int
    46  	SizeTSize   int              // size of size_t
    47  	IntegerSize int              // size of lua integer
    48  	NumberSize  int              // size of lua number
    49  	ByteOrder   binary.ByteOrder // byte order
    50  }
    51  
    52  func (cfg *Config) validate() error {
    53  	if !isPowerOfTwo(cfg.IntSize) {
    54  		return errInvalidIntSize
    55  	}
    56  
    57  	if !isPowerOfTwo(cfg.SizeTSize) {
    58  		return errInvalidSizeTSize
    59  	}
    60  
    61  	if !isPowerOfTwo(cfg.IntegerSize) {
    62  		return errInvalidIntegerSize
    63  	}
    64  
    65  	if !(cfg.NumberSize == 4 || cfg.NumberSize == 8) {
    66  		return errInvalidNumberSize
    67  	}
    68  
    69  	if cfg.ByteOrder == nil {
    70  		return errInvalidByteOrder
    71  	}
    72  
    73  	return nil
    74  }
    75  
    76  func isPowerOfTwo(x int) bool {
    77  	return x&(x-1) == 0
    78  	// for x%2 == 0 && x > 1 {
    79  	// x /= 2
    80  	// }
    81  	// return x == 1
    82  }
    83  
    84  func (cfg *Config) DumpTo(w io.Writer, p *object.Proto, mode Mode) (err error) {
    85  	err = cfg.validate()
    86  	if err != nil {
    87  		return err
    88  	}
    89  
    90  	d := &dumper{
    91  		w:       w,
    92  		mode:    mode,
    93  		cfg:     cfg,
    94  		int:     makeInt(cfg.IntSize),
    95  		sizeT:   makeInt(cfg.SizeTSize),
    96  		integer: makeInteger(cfg.IntegerSize),
    97  		number:  makeNumber(cfg.NumberSize),
    98  	}
    99  
   100  	if bw, ok := w.(io.ByteWriter); ok {
   101  		d.byte = func(b byte) error {
   102  			return bw.WriteByte(b)
   103  		}
   104  	} else {
   105  		d.byte = func(b byte) error {
   106  			_, err := w.Write([]byte{b})
   107  
   108  			return err
   109  		}
   110  	}
   111  
   112  	if sw, ok := w.(stringWriter); ok {
   113  		d.str = func(s string) error {
   114  			_, err := sw.WriteString(s)
   115  			return err
   116  		}
   117  	} else {
   118  		d.str = func(s string) error {
   119  			_, err := w.Write([]byte(s))
   120  			return err
   121  		}
   122  	}
   123  
   124  	err = d.dumpHeader()
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	err = d.dumpByte(len(p.Upvalues))
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	err = d.dumpFunction(p, "")
   135  	if err != nil {
   136  		return err
   137  	}
   138  
   139  	return
   140  }
   141  
   142  type stringWriter interface {
   143  	WriteString(string) (int, error)
   144  }
   145  
   146  type dumper struct {
   147  	w       io.Writer
   148  	mode    Mode
   149  	cfg     *Config
   150  	byte    func(byte) error
   151  	str     func(string) error
   152  	int     func(*dumper, int) error
   153  	sizeT   func(*dumper, int) error
   154  	integer func(*dumper, object.Integer) error
   155  	number  func(*dumper, object.Number) error
   156  }
   157  
   158  func (d *dumper) dumpByte(x int) error {
   159  	if x < 0 || x > 255 {
   160  		return errByteOverflow
   161  	}
   162  	return d.byte(byte(x))
   163  }
   164  
   165  func (d *dumper) dumpBool(b bool) error {
   166  	if b {
   167  		return d.dumpByte(1)
   168  	}
   169  
   170  	return d.dumpByte(0)
   171  }
   172  
   173  func (d *dumper) dumpInt(x int) error {
   174  	return d.int(d, x)
   175  }
   176  
   177  func (d *dumper) dumpSizeT(x int) error {
   178  	return d.sizeT(d, x)
   179  }
   180  
   181  func (d *dumper) dumpStr(s string) error {
   182  	return d.str(s)
   183  }
   184  
   185  func (d *dumper) dumpNumber(x object.Number) error {
   186  	return d.number(d, x)
   187  }
   188  
   189  func (d *dumper) dumpInteger(x object.Integer) error {
   190  	return d.integer(d, x)
   191  }
   192  
   193  func (d *dumper) dumpString(s string) error {
   194  	length := len(s)
   195  	if length == 0 {
   196  		return d.dumpByte(0)
   197  	}
   198  
   199  	length++ // trailing '\0'
   200  
   201  	var err error
   202  	if length < 0xFF {
   203  		err = d.dumpByte(length)
   204  	} else {
   205  		err = d.dumpByte(0xFF)
   206  		if err != nil {
   207  			return err
   208  		}
   209  		err = d.dumpSizeT(length)
   210  	}
   211  
   212  	if err != nil {
   213  		return err
   214  	}
   215  
   216  	return d.dumpStr(string(s))
   217  }
   218  
   219  func (d *dumper) dumpCode(p *object.Proto) error {
   220  	err := d.dumpInt(len(p.Code))
   221  	if err != nil {
   222  		return err
   223  	}
   224  
   225  	return binary.Write(d.w, d.cfg.ByteOrder, p.Code)
   226  }
   227  
   228  func (d *dumper) dumpConstants(p *object.Proto) error {
   229  	err := d.dumpInt(len(p.Constants))
   230  	if err != nil {
   231  		return err
   232  	}
   233  
   234  	for _, c := range p.Constants {
   235  		switch c := c.(type) {
   236  		case nil:
   237  			// do nothing
   238  			err = d.dumpByte(int(object.TNIL))
   239  		case object.Boolean:
   240  			err = d.dumpByte(int(object.TBOOLEAN))
   241  			if err == nil {
   242  				err = d.dumpBool(bool(c))
   243  			}
   244  		case object.Integer:
   245  			err = d.dumpByte(int(object.TNUMINT))
   246  			if err == nil {
   247  				err = d.dumpInteger(c)
   248  			}
   249  		case object.Number:
   250  			err = d.dumpByte(int(object.TNUMFLT))
   251  			if err == nil {
   252  				err = d.dumpNumber(c)
   253  			}
   254  		case object.String:
   255  			err = d.dumpByte(int(object.TSTRING))
   256  			if err == nil {
   257  				err = d.dumpString(string(c))
   258  			}
   259  		default:
   260  			panic("unreachable")
   261  		}
   262  
   263  		if err != nil {
   264  			return err
   265  		}
   266  	}
   267  
   268  	return nil
   269  }
   270  
   271  func (d *dumper) dumpUpvalues(p *object.Proto) error {
   272  	err := d.dumpInt(len(p.Upvalues))
   273  	if err != nil {
   274  		return err
   275  	}
   276  
   277  	for _, u := range p.Upvalues {
   278  		d.dumpBool(u.Instack)
   279  		d.dumpByte(u.Index)
   280  	}
   281  
   282  	return nil
   283  }
   284  
   285  func (d *dumper) dumpProtos(p *object.Proto) error {
   286  	err := d.dumpInt(len(p.Protos))
   287  	if err != nil {
   288  		return err
   289  	}
   290  
   291  	for _, p := range p.Protos {
   292  		err = d.dumpFunction(p, p.Source)
   293  		if err != nil {
   294  			return err
   295  		}
   296  	}
   297  
   298  	return nil
   299  }
   300  
   301  func (d *dumper) dumpDebug(p *object.Proto) (err error) {
   302  	if d.mode&StripDebugInfo != 0 {
   303  		err = d.dumpInt(0) // len(p.LineInfo)
   304  		if err != nil {
   305  			return err
   306  		}
   307  		err = d.dumpInt(0) // len(p.LocVars)
   308  		if err != nil {
   309  			return err
   310  		}
   311  		err = d.dumpInt(0) // len(p.Upvalues)
   312  
   313  		return
   314  	}
   315  
   316  	err = d.dumpInt(len(p.LineInfo))
   317  	if err != nil {
   318  		return err
   319  	}
   320  	for _, line := range p.LineInfo {
   321  		err = d.dumpInt(line)
   322  		if err != nil {
   323  			return err
   324  		}
   325  	}
   326  
   327  	err = d.dumpInt(len(p.LocVars))
   328  	if err != nil {
   329  		return err
   330  	}
   331  	for _, l := range p.LocVars {
   332  		err = d.dumpString(l.Name)
   333  		if err != nil {
   334  			return err
   335  		}
   336  		err = d.dumpInt(l.StartPC)
   337  		if err != nil {
   338  			return err
   339  		}
   340  		err = d.dumpInt(l.EndPC)
   341  		if err != nil {
   342  			return err
   343  		}
   344  	}
   345  
   346  	err = d.dumpInt(len(p.Upvalues))
   347  	if err != nil {
   348  		return err
   349  	}
   350  	for _, u := range p.Upvalues {
   351  		err = d.dumpString(u.Name)
   352  		if err != nil {
   353  			return err
   354  		}
   355  	}
   356  
   357  	return
   358  }
   359  
   360  func (d *dumper) dumpFunction(p *object.Proto, psource string) (err error) {
   361  	if d.mode&StripDebugInfo != 0 || p.Source == psource {
   362  		err = d.dumpString("")
   363  	} else {
   364  		err = d.dumpString(p.Source)
   365  	}
   366  	if err != nil {
   367  		return err
   368  	}
   369  
   370  	err = d.dumpInt(p.LineDefined)
   371  	if err != nil {
   372  		return err
   373  	}
   374  	err = d.dumpInt(p.LastLineDefined)
   375  	if err != nil {
   376  		return err
   377  	}
   378  	err = d.dumpByte(p.NParams)
   379  	if err != nil {
   380  		return err
   381  	}
   382  	err = d.dumpBool(p.IsVararg)
   383  	if err != nil {
   384  		return err
   385  	}
   386  	err = d.dumpByte(p.MaxStackSize)
   387  	if err != nil {
   388  		return err
   389  	}
   390  	err = d.dumpCode(p)
   391  	if err != nil {
   392  		return err
   393  	}
   394  	err = d.dumpConstants(p)
   395  	if err != nil {
   396  		return err
   397  	}
   398  	err = d.dumpUpvalues(p)
   399  	if err != nil {
   400  		return err
   401  	}
   402  	err = d.dumpProtos(p)
   403  	if err != nil {
   404  		return err
   405  	}
   406  	err = d.dumpDebug(p)
   407  
   408  	return
   409  }
   410  
   411  func (d *dumper) dumpHeader() (err error) {
   412  	err = d.dumpStr(version.LUA_SIGNATURE)
   413  	if err != nil {
   414  		return err
   415  	}
   416  	err = d.dumpByte(version.LUAC_VERSION)
   417  	if err != nil {
   418  		return err
   419  	}
   420  	err = d.dumpByte(version.LUAC_FORMAT)
   421  	if err != nil {
   422  		return err
   423  	}
   424  	err = d.dumpStr(version.LUAC_DATA)
   425  	if err != nil {
   426  		return err
   427  	}
   428  	err = d.dumpByte(d.cfg.IntSize)
   429  	if err != nil {
   430  		return err
   431  	}
   432  	err = d.dumpByte(d.cfg.SizeTSize)
   433  	if err != nil {
   434  		return err
   435  	}
   436  	err = d.dumpByte(opcode.InstructionSize)
   437  	if err != nil {
   438  		return err
   439  	}
   440  	err = d.dumpByte(d.cfg.IntegerSize)
   441  	if err != nil {
   442  		return err
   443  	}
   444  	err = d.dumpByte(d.cfg.NumberSize)
   445  	if err != nil {
   446  		return err
   447  	}
   448  	err = d.dumpInteger(version.LUAC_INT)
   449  	if err != nil {
   450  		return err
   451  	}
   452  	err = d.dumpNumber(version.LUAC_NUM)
   453  
   454  	return
   455  }
   456  
   457  func makeInt(size int) (f func(*dumper, int) error) {
   458  	switch size {
   459  	case 1:
   460  		f = func(d *dumper, x int) error {
   461  			if x < limits.MinInt8 || x > limits.MaxInt8 {
   462  				return errIntegerOverflow
   463  			}
   464  			return binary.Write(d.w, d.cfg.ByteOrder, int8(x))
   465  		}
   466  	case 2:
   467  		f = func(d *dumper, x int) error {
   468  			if x < limits.MinInt16 || x > limits.MaxInt16 {
   469  				return errIntegerOverflow
   470  			}
   471  			return binary.Write(d.w, d.cfg.ByteOrder, int16(x))
   472  		}
   473  	case 4:
   474  		f = func(d *dumper, x int) error {
   475  			if x < limits.MinInt32 || x > limits.MaxInt32 {
   476  				return errIntegerOverflow
   477  			}
   478  			return binary.Write(d.w, d.cfg.ByteOrder, int32(x))
   479  		}
   480  	case 8:
   481  		f = func(d *dumper, x int) error {
   482  			return binary.Write(d.w, d.cfg.ByteOrder, int64(x))
   483  		}
   484  	default:
   485  		panic("unreachable")
   486  	}
   487  
   488  	return
   489  }
   490  
   491  func makeInteger(size int) (f func(*dumper, object.Integer) error) {
   492  	switch size {
   493  	case 1:
   494  		f = func(d *dumper, x object.Integer) error {
   495  			if x < limits.MinInt8 || x > limits.MaxInt8 {
   496  				return errIntegerOverflow
   497  			}
   498  			return binary.Write(d.w, d.cfg.ByteOrder, int8(x))
   499  		}
   500  	case 2:
   501  		f = func(d *dumper, x object.Integer) error {
   502  			if x < limits.MinInt16 || x > limits.MaxInt16 {
   503  				return errIntegerOverflow
   504  			}
   505  			return binary.Write(d.w, d.cfg.ByteOrder, int16(x))
   506  		}
   507  	case 4:
   508  		f = func(d *dumper, x object.Integer) error {
   509  			if x < limits.MinInt32 || x > limits.MaxInt32 {
   510  				return errIntegerOverflow
   511  			}
   512  			return binary.Write(d.w, d.cfg.ByteOrder, int32(x))
   513  		}
   514  	case 8:
   515  		f = func(d *dumper, x object.Integer) error {
   516  			return binary.Write(d.w, d.cfg.ByteOrder, int64(x))
   517  		}
   518  	default:
   519  		panic("unreachable")
   520  	}
   521  
   522  	return
   523  }
   524  
   525  func makeNumber(size int) (f func(*dumper, object.Number) error) {
   526  	switch size {
   527  	case 4:
   528  		f = func(d *dumper, x object.Number) error {
   529  			abs := x
   530  			if x < 0 {
   531  				abs = -x
   532  			}
   533  
   534  			if abs > limits.MaxFloat32 {
   535  				return errFloatOverflow
   536  			}
   537  
   538  			if abs < limits.SmallestNonzeroFloat32 {
   539  				return errFloatUnderflow
   540  			}
   541  
   542  			return binary.Write(d.w, d.cfg.ByteOrder, float32(x))
   543  		}
   544  	case 8:
   545  		f = func(d *dumper, x object.Number) error {
   546  			return binary.Write(d.w, d.cfg.ByteOrder, float64(x))
   547  		}
   548  	default:
   549  		panic("unreachable")
   550  	}
   551  
   552  	return
   553  }