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

     1  package runtime
     2  
     3  import (
     4  	"unsafe"
     5  
     6  	"github.com/arnodel/golua/code"
     7  )
     8  
     9  // Code represents the code for a Lua function together with all the constants
    10  // that this function uses.  It can be turned into a closure by adding upvalues.
    11  type Code struct {
    12  	source, name string
    13  	code         []code.Opcode
    14  	lines        []int32
    15  	consts       []Value
    16  	UpvalueCount int16
    17  	UpNames      []string
    18  	RegCount     int16
    19  	CellCount    int16
    20  }
    21  
    22  // RefactorConsts returns an equivalent *Code this consts "refactored", which
    23  // means that the consts are slimmed down to only contains the constants
    24  // required for the function.
    25  func (r *Runtime) RefactorCodeConsts(c *Code) *Code {
    26  	r.RequireArrSize(unsafe.Sizeof(code.Opcode(0)), len(c.code))
    27  	opcodes := make([]code.Opcode, len(c.code))
    28  	var consts []Value
    29  	constMap := map[code.KIndex]code.KIndex{}
    30  
    31  	// Require CPU for the loop below
    32  	r.RequireCPU(uint64(len(c.code)))
    33  
    34  	for i, op := range c.code {
    35  		if op.TypePfx() == code.Type3Pfx {
    36  			unop := op.GetY()
    37  			if unop.LoadsK() {
    38  				// We are loading a constant
    39  				n := op.GetKIndex()
    40  				m, ok := constMap[n]
    41  				if !ok {
    42  					m = code.KIndexFromInt(len(consts))
    43  					constMap[n] = m
    44  					newConst := c.consts[n]
    45  					if unop == code.OpClosureK {
    46  						// It's a closure so we need to refactor its consts
    47  						newConst = CodeValue(r.RefactorCodeConsts(newConst.AsCode()))
    48  					}
    49  					r.RequireSize(unsafe.Sizeof(Value{}))
    50  					consts = append(consts, newConst)
    51  				}
    52  				op = op.SetKIndex(m)
    53  			}
    54  		}
    55  		opcodes[i] = op
    56  	}
    57  	cc := *c
    58  	cc.code = opcodes
    59  	cc.consts = consts
    60  	return &cc
    61  }
    62  
    63  // LoadLuaUnit turns a code unit into a closure given an environment env.
    64  func (r *Runtime) LoadLuaUnit(unit *code.Unit, env Value) *Closure {
    65  	r.RequireArrSize(unsafe.Sizeof(Value{}), len(unit.Constants))
    66  	constants := make([]Value, len(unit.Constants))
    67  
    68  	// Require memory for all the code at once, rather than in bits in the
    69  	// code.Code case below
    70  	r.RequireArrSize(unsafe.Sizeof(code.Opcode(0)), len(unit.Code))
    71  	r.RequireArrSize(4, len(unit.Lines))
    72  
    73  	// Require CPU for the loop below
    74  	r.RequireCPU(uint64(len(unit.Constants)))
    75  
    76  	for i, ck := range unit.Constants {
    77  		switch k := ck.(type) {
    78  		case code.Int:
    79  			constants[i] = IntValue(int64(k))
    80  		case code.Float:
    81  			constants[i] = FloatValue(float64(k))
    82  		case code.String:
    83  			// The strings are already accounted for memory-wise
    84  			constants[i] = StringValue(string(k))
    85  		case code.Bool:
    86  			constants[i] = BoolValue(bool(k))
    87  		case code.NilType:
    88  			// Do nothing as constants[i] == nil
    89  		case code.Code:
    90  			r.RequireSize(unsafe.Sizeof(Code{}))
    91  			var lines []int32
    92  			if unit.Lines != nil {
    93  				lines = unit.Lines[k.StartOffset:k.EndOffset]
    94  			}
    95  			constants[i] = CodeValue(&Code{
    96  				source:       unit.Source,
    97  				name:         k.Name,
    98  				code:         unit.Code[k.StartOffset:k.EndOffset],
    99  				lines:        lines,
   100  				consts:       constants,
   101  				UpvalueCount: k.UpvalueCount,
   102  				UpNames:      k.UpNames,
   103  				RegCount:     k.RegCount,
   104  				CellCount:    k.CellCount,
   105  			})
   106  		default:
   107  			panic("Unsupported constant type")
   108  		}
   109  	}
   110  	mainCode := constants[0].AsCode() // It must be some code
   111  	clos := NewClosure(r, mainCode)
   112  	if mainCode.UpvalueCount > 0 {
   113  		clos.AddUpvalue(Cell{&env})
   114  	}
   115  	return clos
   116  }