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 }