github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/code/unit.go (about)

     1  package code
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  )
     7  
     8  // A Unit is a chunk of code with associated constants.
     9  type Unit struct {
    10  	Source    string     // Shows were the unit comes from (e.g. a filename) - only for information.
    11  	Code      []Opcode   // The code
    12  	Lines     []int32    // Optional: source code line for the corresponding opcode
    13  	Constants []Constant // All the constants required for running the code
    14  }
    15  
    16  // Disassemble outputs the disassembly of the unit code into the given
    17  // io.Writer.
    18  func (u *Unit) Disassemble(w io.Writer) {
    19  	newUnitDisassembler(u).disassemble(w)
    20  }
    21  
    22  type unitDisassembler struct {
    23  	unit   *Unit
    24  	labels map[int]string
    25  	spans  map[int]string
    26  }
    27  
    28  var _ OpcodeDisassembler = (*unitDisassembler)(nil)
    29  
    30  func newUnitDisassembler(unit *Unit) *unitDisassembler {
    31  	return &unitDisassembler{
    32  		unit:   unit,
    33  		labels: make(map[int]string),
    34  		spans:  make(map[int]string),
    35  	}
    36  }
    37  
    38  func (d *unitDisassembler) disassemble(w io.Writer) {
    39  	fmt.Fprintf(w, "==CONSTANTS==\n\n")
    40  	for i, k := range d.unit.Constants {
    41  		fmt.Fprintf(w, "K%d = %s\n", i, k.ShortString())
    42  		if sg, ok := k.(spanGetter); ok {
    43  			d.setSpan(sg.GetSpan())
    44  		}
    45  	}
    46  	disCode := make([]string, len(d.unit.Code))
    47  	maxSpanLen := 10
    48  	for i, opcode := range d.unit.Code {
    49  		disCode[i] = opcode.Disassemble(d, i)
    50  		if l := len(d.spans[i]); l > maxSpanLen {
    51  			maxSpanLen = l
    52  		}
    53  	}
    54  	fmt.Fprintf(w, "\n==CODE==\n\n")
    55  	for i, dis := range disCode {
    56  		fmt.Fprintf(w, "%6d  %-6s  %-*s  %6d  %08x  %s\n", d.unit.Lines[i], d.labels[i], maxSpanLen, d.spans[i], i, d.unit.Code[i], dis)
    57  	}
    58  }
    59  
    60  func (d *unitDisassembler) GetLabel(offset int) string {
    61  	lbl, ok := d.labels[offset]
    62  	if !ok {
    63  		lbl = fmt.Sprintf("L%d", len(d.labels))
    64  		d.labels[offset] = lbl
    65  	}
    66  	return lbl
    67  }
    68  
    69  func (d *unitDisassembler) setSpan(name string, startOffset, endOffset int) {
    70  	d.spans[startOffset] = name
    71  	for {
    72  		startOffset++
    73  		d.spans[startOffset] = " |"
    74  		if startOffset == endOffset {
    75  			d.spans[endOffset] = `  \`
    76  			return
    77  		}
    78  	}
    79  }
    80  
    81  func (d *unitDisassembler) ShortKString(ki KIndex) string {
    82  	k := d.unit.Constants[ki]
    83  	return k.ShortString()
    84  }
    85  
    86  // Interface for constants that contain code to notify the disassemble of where
    87  // this code is (and how to label it).
    88  type spanGetter interface {
    89  	GetSpan() (string, int, int)
    90  }