diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index eed3390c..5695a96f 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,27 +1,19 @@ -on: [push, pull_request] +on: [push, pull_request, workflow_dispatch] name: test jobs: test: strategy: fail-fast: false matrix: - go-version: [1.18.x, 1.19.x] - platform: [ubuntu-latest, macos-latest, windows-latest] + go-version: [1.24.x, 1.25.x] + platform: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.platform }} steps: - name: Install Go - uses: actions/setup-go@v1 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} - name: Checkout code - uses: actions/checkout@v1 + uses: actions/checkout@v5 - name: Run tests - run: ./_tools/go-inline *.go && go fmt . && go test -v ./... -covermode=count -coverprofile=coverage.out -coverpkg=$(go list ./... | sed 's/\n/,/g') - - name: Send coverage - if: "matrix.platform == 'ubuntu-latest'" - env: - COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - GO111MODULE=off go get github.com/mattn/goveralls - ./_tools/go-inline *.go && go fmt . - $(go env GOPATH)/bin/goveralls -coverprofile=coverage.out -service=github + run: make test diff --git a/Makefile b/Makefile index 6d9e55c3..feafc1f9 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,16 @@ .PHONY: build test glua build: - ./_tools/go-inline *.go && go fmt . && go build + ./_tools/go-inline *.go + go fmt . + go vet . + go build glua: *.go pm/*.go cmd/glua/glua.go ./_tools/go-inline *.go && go fmt . && go build cmd/glua/glua.go test: - ./_tools/go-inline *.go && go fmt . && go test + ./_tools/go-inline *.go + go fmt . + go vet . + go test diff --git a/_state.go b/_state.go index 6e9febfb..a7a1fa3c 100644 --- a/_state.go +++ b/_state.go @@ -11,6 +11,7 @@ import ( "sync" "sync/atomic" "time" + "unsafe" "github.com/yuin/gopher-lua/parse" ) @@ -394,6 +395,15 @@ func (rg *registry) resize(requiredSize int) { // +inline-start } // +inline-end func (rg *registry) forceResize(newSize int) { + // Track memory growth for registry resize + oldSize := len(rg.array) + if newSize > oldSize { + additionalBytes := int64(newSize-oldSize) * 16 // LValue is 16 bytes + if ls, ok := rg.handler.(*LState); ok { + ls.TrackAlloc(additionalBytes) + } + } + newSlice := make([]LValue, newSize) copy(newSlice, rg.array[:rg.top]) // should we copy the area beyond top? there shouldn't be any valid values there so it shouldn't be necessary. rg.array = newSlice @@ -589,6 +599,42 @@ func newLState(options Options) *LState { return ls } +/* Memory tracking {{{ */ + +// TrackAlloc adds bytes to the memory allocation counter and checks against the limit. +// Raises a Lua error if the allocation would exceed the memory limit. +func (ls *LState) TrackAlloc(bytes int64) { + ls.allocatedBytes += bytes + + // Only check limit if one is set + if ls.maxBytes > 0 && ls.allocatedBytes > ls.maxBytes { + ls.RaiseError("memory limit exceeded: %d bytes allocated, limit is %d bytes", + ls.allocatedBytes, ls.maxBytes) + } +} + +// ResetMemoryUsage resets the allocated bytes counter to zero. +func (ls *LState) ResetMemoryUsage() { + ls.allocatedBytes = 0 +} + +// SetMemoryLimit sets the maximum memory limit in bytes. Set to 0 to disable limiting. +func (ls *LState) SetMemoryLimit(maxBytes int64) { + ls.maxBytes = maxBytes +} + +// GetAllocatedBytes returns the current number of tracked allocated bytes. +func (ls *LState) GetAllocatedBytes() int64 { + return ls.allocatedBytes +} + +// GetMemoryLimit returns the current memory limit in bytes (0 if no limit). +func (ls *LState) GetMemoryLimit() int64 { + return ls.maxBytes +} + +/* }}} */ + func (ls *LState) printReg() { println("-------------------------") println("thread:", ls) @@ -995,7 +1041,7 @@ func (ls *LState) initCallFrame(cf *callFrame) { // +inline-start if CompatVarArg { ls.reg.SetTop(cf.LocalBase + nargs + np + 1) if (proto.IsVarArg & VarArgNeedsArg) != 0 { - argtb := newLTable(nvarargs, 0) + argtb := ls.newLTable(nvarargs, 0) for i := 0; i < nvarargs; i++ { argtb.RawSetInt(i+1, ls.reg.Get(cf.LocalBase+np+i)) } @@ -1333,7 +1379,6 @@ func (ls *LState) Get(idx int) LValue { return LNil } } - return LNil } func (ls *LState) Push(value LValue) { @@ -1389,11 +1434,11 @@ func (ls *LState) Remove(index int) { /* object allocation {{{ */ func (ls *LState) NewTable() *LTable { - return newLTable(defaultArrayCap, defaultHashCap) + return ls.newLTable(defaultArrayCap, defaultHashCap) } func (ls *LState) CreateTable(acap, hcap int) *LTable { - return newLTable(acap, hcap) + return ls.newLTable(acap, hcap) } // NewThread returns a new LState that shares with the original state all global objects. @@ -1412,10 +1457,13 @@ func (ls *LState) NewThread() (*LState, context.CancelFunc) { } func (ls *LState) NewFunctionFromProto(proto *FunctionProto) *LFunction { - return newLFunctionL(proto, ls.Env, int(proto.NumUpvalues)) + return ls.newLFunctionL(proto, ls.Env, int(proto.NumUpvalues)) } func (ls *LState) NewUserData() *LUserData { + size := int64(unsafe.Sizeof(LUserData{})) + ls.TrackAlloc(size) + return &LUserData{ Env: ls.currentEnv(), Metatable: LNil, @@ -1423,11 +1471,11 @@ func (ls *LState) NewUserData() *LUserData { } func (ls *LState) NewFunction(fn LGFunction) *LFunction { - return newLFunctionG(fn, ls.currentEnv(), 0) + return ls.newLFunctionG(fn, ls.currentEnv(), 0) } func (ls *LState) NewClosure(fn LGFunction, upvalues ...LValue) *LFunction { - cl := newLFunctionG(fn, ls.currentEnv(), len(upvalues)) + cl := ls.newLFunctionG(fn, ls.currentEnv(), len(upvalues)) for i, lv := range upvalues { cl.Upvalues[i] = &Upvalue{} cl.Upvalues[i].Close() @@ -1806,7 +1854,7 @@ func (ls *LState) Load(reader io.Reader, name string) (*LFunction, error) { if err != nil { return nil, newApiErrorE(ApiErrorSyntax, err) } - return newLFunctionL(proto, ls.currentEnv(), 0), nil + return ls.newLFunctionL(proto, ls.currentEnv(), 0), nil } func (ls *LState) Call(nargs, nret int) { @@ -1882,7 +1930,7 @@ func (ls *LState) PCall(nargs, nret int, errfunc *LFunction) (err error) { } func (ls *LState) GPCall(fn LGFunction, data LValue) error { - ls.Push(newLFunctionG(fn, ls.currentEnv(), 0)) + ls.Push(ls.newLFunctionG(fn, ls.currentEnv(), 0)) ls.Push(data) return ls.PCall(1, MultRet, nil) } diff --git a/_vm.go b/_vm.go index ee2be040..92df9087 100644 --- a/_vm.go +++ b/_vm.go @@ -166,24 +166,24 @@ var jumpTable [opCodeMax + 1]instFunc func init() { jumpTable = [opCodeMax + 1]instFunc{ - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_MOVE + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_MOVE reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB v := reg.Get(lbase + B) // +inline-call reg.Set RA v return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_MOVEN + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_MOVEN reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + A := int(inst>>18) & 0xff // GETA + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC v := reg.Get(lbase + B) // +inline-call reg.Set lbase+A v code := cf.Fn.Proto.Code @@ -191,33 +191,33 @@ func init() { for i := 0; i < C; i++ { inst = code[pc] pc++ - A = int(inst>>18) & 0xff //GETA - B = int(inst & 0x1ff) //GETB + A = int(inst>>18) & 0xff // GETA + B = int(inst & 0x1ff) // GETB v := reg.Get(lbase + B) // +inline-call reg.Set lbase+A v } cf.Pc = pc return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LOADK + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_LOADK reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - Bx := int(inst & 0x3ffff) //GETBX + Bx := int(inst & 0x3ffff) // GETBX v := cf.Fn.Proto.Constants[Bx] // +inline-call reg.Set RA v return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LOADBOOL + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_LOADBOOL reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC if B != 0 { // +inline-call reg.Set RA LTrue } else { @@ -228,128 +228,128 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LOADNIL + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_LOADNIL reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB for i := RA; i <= lbase+B; i++ { // +inline-call reg.Set i LNil } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_GETUPVAL + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_GETUPVAL reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB v := cf.Fn.Upvalues[B].Value() // +inline-call reg.Set RA v return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_GETGLOBAL + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_GETGLOBAL reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - Bx := int(inst & 0x3ffff) //GETBX - //reg.Set(RA, L.getField(cf.Fn.Env, cf.Fn.Proto.Constants[Bx])) + Bx := int(inst & 0x3ffff) // GETBX + // reg.Set(RA, L.getField(cf.Fn.Env, cf.Fn.Proto.Constants[Bx])) v := L.getFieldString(cf.Fn.Env, cf.Fn.Proto.stringConstants[Bx]) // +inline-call reg.Set RA v return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_GETTABLE + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_GETTABLE reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC v := L.getField(reg.Get(lbase+B), L.rkValue(C)) // +inline-call reg.Set RA v return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_GETTABLEKS + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_GETTABLEKS reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC v := L.getFieldString(reg.Get(lbase+B), L.rkString(C)) // +inline-call reg.Set RA v return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SETGLOBAL + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_SETGLOBAL reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - Bx := int(inst & 0x3ffff) //GETBX - //L.setField(cf.Fn.Env, cf.Fn.Proto.Constants[Bx], reg.Get(RA)) + Bx := int(inst & 0x3ffff) // GETBX + // L.setField(cf.Fn.Env, cf.Fn.Proto.Constants[Bx], reg.Get(RA)) L.setFieldString(cf.Fn.Env, cf.Fn.Proto.stringConstants[Bx], reg.Get(RA)) return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SETUPVAL + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_SETUPVAL reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB cf.Fn.Upvalues[B].SetValue(reg.Get(RA)) return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SETTABLE + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_SETTABLE reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC L.setField(reg.Get(RA), L.rkValue(B), L.rkValue(C)) return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SETTABLEKS + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_SETTABLEKS reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC L.setFieldString(reg.Get(RA), L.rkString(B), L.rkValue(C)) return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_NEWTABLE + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_NEWTABLE reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC - v := newLTable(B, C) + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC + v := L.newLTable(B, C) // +inline-call reg.Set RA v return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SELF + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_SELF reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC selfobj := reg.Get(lbase + B) v := L.getFieldString(selfobj, L.rkString(C)) // +inline-call reg.Set RA v @@ -362,13 +362,13 @@ func init() { opArith, // OP_DIV opArith, // OP_MOD opArith, // OP_POW - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_UNM + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_UNM reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB unaryv := L.rkValue(B) if nm, ok := unaryv.(LNumber); ok { // +inline-call reg.Set RA -nm @@ -391,13 +391,13 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_NOT + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_NOT reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB if LVIsFalse(reg.Get(lbase + B)) { // +inline-call reg.Set RA LTrue } else { @@ -405,13 +405,13 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LEN + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_LEN reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB switch lv := L.rkValue(B).(type) { case LString: // +inline-call reg.SetNumber RA LNumber(len(lv)) @@ -436,31 +436,31 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_CONCAT + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_CONCAT reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC RC := lbase + C RB := lbase + B v := stringConcat(L, RC-RB+1, RC) // +inline-call reg.Set RA v return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_JMP + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_JMP cf := L.currentFrame - Sbx := int(inst&0x3ffff) - opMaxArgSbx //GETSBX + Sbx := int(inst&0x3ffff) - opMaxArgSbx // GETSBX cf.Pc += Sbx return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_EQ + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_EQ cf := L.currentFrame - A := int(inst>>18) & 0xff //GETA - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + A := int(inst>>18) & 0xff // GETA + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC ret := equals(L, L.rkValue(B), L.rkValue(C), false) v := 1 if ret { @@ -471,11 +471,11 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LT + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_LT cf := L.currentFrame - A := int(inst>>18) & 0xff //GETA - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + A := int(inst>>18) & 0xff // GETA + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC ret := lessThan(L, L.rkValue(B), L.rkValue(C)) v := 1 if ret { @@ -486,11 +486,11 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LE + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_LE cf := L.currentFrame - A := int(inst>>18) & 0xff //GETA - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + A := int(inst>>18) & 0xff // GETA + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC lhs := L.rkValue(B) rhs := L.rkValue(C) ret := false @@ -529,26 +529,26 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_TEST + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_TEST reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - C := int(inst>>9) & 0x1ff //GETC + C := int(inst>>9) & 0x1ff // GETC if LVAsBool(reg.Get(RA)) == (C == 0) { cf.Pc++ } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_TESTSET + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_TESTSET reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC if value := reg.Get(lbase + B); LVAsBool(value) != (C == 0) { // +inline-call reg.Set RA value } else { @@ -556,14 +556,14 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_CALL + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_CALL reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC nargs := B - 1 if B == 0 { nargs = reg.Top() - (RA + 1) @@ -584,13 +584,13 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_TAILCALL + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_TAILCALL reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB nargs := B - 1 if B == 0 { nargs = reg.Top() - (RA + 1) @@ -633,9 +633,9 @@ func init() { cf.Pc = 0 cf.Base = RA cf.LocalBase = RA + 1 - cf.ReturnBase = cf.ReturnBase + // cf.ReturnBase = cf.ReturnBase cf.NArgs = nargs - cf.NRet = cf.NRet + // cf.NRet = cf.NRet cf.TailCall++ lbase := cf.LocalBase if meta { @@ -649,13 +649,13 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_RETURN + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_RETURN reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB // +inline-call L.closeUpvalues lbase nret := B - 1 if B == 0 { @@ -679,11 +679,11 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_FORLOOP + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_FORLOOP reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A if init, ok1 := reg.Get(RA).(LNumber); ok1 { if limit, ok2 := reg.Get(RA + 1).(LNumber); ok2 { @@ -692,7 +692,7 @@ func init() { v := LNumber(init) // +inline-call reg.SetNumber RA v if (step > 0 && init <= limit) || (step <= 0 && init >= limit) { - Sbx := int(inst&0x3ffff) - opMaxArgSbx //GETSBX + Sbx := int(inst&0x3ffff) - opMaxArgSbx // GETSBX cf.Pc += Sbx // +inline-call reg.SetNumber RA+3 v } else { @@ -709,13 +709,13 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_FORPREP + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_FORPREP reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - Sbx := int(inst&0x3ffff) - opMaxArgSbx //GETSBX + Sbx := int(inst&0x3ffff) - opMaxArgSbx // GETSBX if init, ok1 := reg.Get(RA).(LNumber); ok1 { if step, ok2 := reg.Get(RA + 2).(LNumber); ok2 { // +inline-call reg.SetNumber RA LNumber(init-step) @@ -728,13 +728,13 @@ func init() { cf.Pc += Sbx return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_TFORLOOP + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_TFORLOOP reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - C := int(inst>>9) & 0x1ff //GETC + C := int(inst>>9) & 0x1ff // GETC nret := C // +inline-call reg.SetTop RA+3+2 // +inline-call reg.Set RA+3+2 reg.Get(RA+2) @@ -749,14 +749,14 @@ func init() { cf.Pc++ return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SETLIST + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_SETLIST reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC if C == 0 { C = int(cf.Fn.Proto.Code[cf.Pc]) cf.Pc++ @@ -772,23 +772,23 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_CLOSE + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_CLOSE cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A // +inline-call L.closeUpvalues RA return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_CLOSURE + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_CLOSURE reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - Bx := int(inst & 0x3ffff) //GETBX + Bx := int(inst & 0x3ffff) // GETBX proto := cf.Fn.Proto.FunctionPrototypes[Bx] - closure := newLFunctionL(proto, cf.Fn.Env, int(proto.NumUpvalues)) + closure := L.newLFunctionL(proto, cf.Fn.Env, int(proto.NumUpvalues)) // +inline-call reg.Set RA closure for i := 0; i < int(proto.NumUpvalues); i++ { inst = cf.Fn.Proto.Code[cf.Pc] @@ -803,13 +803,13 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_VARARG + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_VARARG reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB nparams := int(cf.Fn.Proto.NumParameters) nvarargs := cf.NArgs - nparams if nvarargs < 0 { @@ -822,21 +822,21 @@ func init() { // +inline-call reg.CopyRange RA cf.Base+nparams+1 cf.LocalBase nwant return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_NOP + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_NOP return 0 }, } } -func opArith(L *LState, inst uint32, baseframe *callFrame) int { //OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, OP_POW +func opArith(L *LState, inst uint32, baseframe *callFrame) int { // OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, OP_POW reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - opcode := int(inst >> 26) //GETOPCODE - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + opcode := int(inst >> 26) // GETOPCODE + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC lhs := L.rkValue(B) rhs := L.rkValue(C) v1, ok1 := lhs.(LNumber) @@ -879,7 +879,6 @@ func numberArith(L *LState, opcode int, lhs, rhs LNumber) LNumber { return LNumber(math.Pow(flhs, frhs)) } panic("should not reach here") - return LNumber(0) } func objectArith(L *LState, opcode int, lhs, rhs LValue) LValue { @@ -958,7 +957,9 @@ func stringConcat(L *LState, total, last int) LValue { i-- total-- } - rhs = LString(strings.Join(buf, "")) + result := strings.Join(buf, "") + L.TrackAlloc(int64(len(result))) + rhs = LString(result) } } return rhs diff --git a/function.go b/function.go index 169e5407..d6fd52ae 100644 --- a/function.go +++ b/function.go @@ -3,6 +3,7 @@ package lua import ( "fmt" "strings" + "unsafe" ) const ( @@ -152,7 +153,13 @@ func (fp *FunctionProto) str(level int, count int) string { /* LFunction {{{ */ -func newLFunctionL(proto *FunctionProto, env *LTable, nupvalue int) *LFunction { +// newLFunctionL creates a new Lua function with memory tracking. +func (ls *LState) newLFunctionL(proto *FunctionProto, env *LTable, nupvalue int) *LFunction { + // Calculate memory: base struct + upvalues slice + size := int64(unsafe.Sizeof(LFunction{})) + int64(nupvalue)*8 + + ls.TrackAlloc(size) + return &LFunction{ IsG: false, Env: env, @@ -163,7 +170,13 @@ func newLFunctionL(proto *FunctionProto, env *LTable, nupvalue int) *LFunction { } } -func newLFunctionG(gfunc LGFunction, env *LTable, nupvalue int) *LFunction { +// newLFunctionG creates a new Go function with memory tracking. +func (ls *LState) newLFunctionG(gfunc LGFunction, env *LTable, nupvalue int) *LFunction { + // Calculate memory: base struct + upvalues slice + size := int64(unsafe.Sizeof(LFunction{})) + int64(nupvalue)*8 + + ls.TrackAlloc(size) + return &LFunction{ IsG: true, Env: env, diff --git a/memory_limit_test.go b/memory_limit_test.go new file mode 100644 index 00000000..946354d5 --- /dev/null +++ b/memory_limit_test.go @@ -0,0 +1,797 @@ +package lua + +import ( + "strings" + "testing" +) + +func TestMemoryLimit_TableAllocation(t *testing.T) { + L := NewState() + defer L.Close() + + // Set a 10KB limit + L.ResetMemoryUsage() + L.SetMemoryLimit(10 * 1024) + + err := L.DoString(` + local t = {} + for i = 1, 100 do + t[i] = i + end + return #t + `) + + if err != nil { + t.Fatalf("Expected success, got error: %v", err) + } + + allocated := L.GetAllocatedBytes() + if allocated == 0 { + t.Error("Expected non-zero memory allocation") + } + + // Create tables until we hit the limit + err = L.DoString(` + local tables = {} + for i = 1, 1000 do + tables[i] = {} + for j = 1, 100 do + tables[i][j] = j + end + end + `) + + if err == nil { + t.Fatal("Expected memory limit error, got nil") + } + + if !strings.Contains(err.Error(), "memory limit exceeded") { + t.Errorf("Expected 'memory limit exceeded' error, got: %v", err) + } +} + +func TestMemoryLimit_TableInsert(t *testing.T) { + L := NewState() + defer L.Close() + + L.SetMemoryLimit(20 * 1024) // 20KB limit + L.ResetMemoryUsage() + + script := ` + local t = {} + for i = 1, 200 do + table.insert(t, i) + end + return #t + ` + + if err := L.DoString(script); err != nil { + t.Fatalf("Expected success, got error: %v", err) + } + + allocated := L.GetAllocatedBytes() + if allocated == 0 { + t.Error("Expected non-zero memory allocation for table.insert") + } +} + +func TestMemoryLimit_ArrayGrowth(t *testing.T) { + L := NewState() + defer L.Close() + + L.SetMemoryLimit(50 * 1024) // 50KB limit + L.ResetMemoryUsage() + + script := ` + local t = {} + for i = 1, 500 do + t[i] = "value" .. i + end + return #t + ` + + if err := L.DoString(script); err != nil { + t.Fatalf("Expected success, got error: %v", err) + } + + allocated := L.GetAllocatedBytes() + if allocated == 0 { + t.Error("Expected non-zero memory allocation for array growth") + } +} + +func TestMemoryLimit_HashTable(t *testing.T) { + L := NewState() + defer L.Close() + + L.SetMemoryLimit(30 * 1024) // 30KB limit + L.ResetMemoryUsage() + + script := ` + local t = {} + for i = 1, 100 do + t["key" .. i] = "value" .. i + end + local count = 0 + for k, v in pairs(t) do + count = count + 1 + end + return count + ` + + if err := L.DoString(script); err != nil { + t.Fatalf("Expected success, got error: %v", err) + } + + allocated := L.GetAllocatedBytes() + if allocated == 0 { + t.Error("Expected non-zero memory allocation for hash table") + } +} + +func TestMemoryLimit_NestedTables(t *testing.T) { + L := NewState() + defer L.Close() + + L.SetMemoryLimit(100 * 1024) // 100KB limit + L.ResetMemoryUsage() + + script := ` + local t = {} + for i = 1, 50 do + t[i] = {} + for j = 1, 50 do + t[i][j] = i * j + end + end + return #t + ` + + if err := L.DoString(script); err != nil { + t.Fatalf("Expected success, got error: %v", err) + } + + allocated := L.GetAllocatedBytes() + if allocated == 0 { + t.Error("Expected non-zero memory allocation for nested tables") + } +} + +func TestMemoryLimit_TableConcat(t *testing.T) { + L := NewState() + defer L.Close() + + L.SetMemoryLimit(200 * 1024) // 200KB limit + L.ResetMemoryUsage() + + script := ` + local t = {} + for i = 1, 1000 do + t[i] = "item" .. i + end + local result = table.concat(t, ",") + return #result + ` + + if err := L.DoString(script); err != nil { + t.Fatalf("Expected success, got error: %v", err) + } + + allocated := L.GetAllocatedBytes() + if allocated == 0 { + t.Error("Expected non-zero memory allocation for table.concat") + } +} + +func TestMemoryLimit_StringConcat(t *testing.T) { + L := NewState() + defer L.Close() + + L.ResetMemoryUsage() + L.SetMemoryLimit(5 * 1024) + + // Concatenate strings until we hit the limit + err := L.DoString(` + local str = "a" + for i = 1, 20 do + str = str .. str -- doubles each time + end + `) + + if err == nil { + t.Fatalf("Expected memory limit error, got nil: %d", L.allocatedBytes) + } + + if !strings.Contains(err.Error(), "memory limit exceeded") { + t.Errorf("Expected 'memory limit exceeded' error, got: %v", err) + } +} + +func TestMemoryLimit_StringRepeat(t *testing.T) { + L := NewState() + defer L.Close() + + L.ResetMemoryUsage() + L.SetMemoryLimit(5 * 1024) + + err := L.DoString(` + local s = string.rep("x", 100) + return #s + `) + + if err != nil { + t.Fatalf("Expected success, got error: %v", err) + } + + allocated := L.GetAllocatedBytes() + if allocated == 0 { + t.Error("Expected non-zero memory allocation for string.rep") + } + + // Concatenate strings until we hit the limit + err = L.DoString(` + local str = string.rep("a", 1024 * 10) + return str + `) + + if err == nil { + t.Fatal("Expected memory limit error, got nil") + } + + if !strings.Contains(err.Error(), "memory limit exceeded") { + t.Errorf("Expected 'memory limit exceeded' error, got: %v", err) + } +} + +func TestMemoryLimit_GetUsage(t *testing.T) { + L := NewState() + defer L.Close() + + L.ResetMemoryUsage() + L.SetMemoryLimit(100 * 1024) // 100KB + + initialUsage := L.GetAllocatedBytes() + if initialUsage != 0 { + t.Errorf("Expected initial usage to be 0 after reset, got %d", initialUsage) + } + + // Allocate some memory + err := L.DoString(` + local t = {} + for i = 1, 100 do + t[i] = i + end + `) + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + usageAfter := L.GetAllocatedBytes() + if usageAfter <= 0 { + t.Errorf("Expected usage after allocation to be > 0, got %d", usageAfter) + } +} + +func TestMemoryLimit_ResetUsage(t *testing.T) { + L := NewState() + defer L.Close() + + L.ResetMemoryUsage() + L.SetMemoryLimit(100 * 1024) // 100KB + + // Allocate some memory + err := L.DoString(` + local t = {} + for i = 1, 50 do + t[i] = i + end + `) + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + usageBefore := L.GetAllocatedBytes() + if usageBefore <= 0 { + t.Errorf("Expected usage to be > 0 before reset, got %d", usageBefore) + } + + L.ResetMemoryUsage() + + usageAfter := L.GetAllocatedBytes() + if usageAfter != 0 { + t.Errorf("Expected usage to be 0 after reset, got %d", usageAfter) + } + + // Should be able to allocate again after reset + err = L.DoString(` + local t = {} + for i = 1, 50 do + t[i] = i + end + `) + + if err != nil { + t.Fatalf("Unexpected error after reset: %v", err) + } +} + +func TestMemoryLimit_NoLimit(t *testing.T) { + L := NewState() + defer L.Close() + + // Should not fail even with large allocations + err := L.DoString(` + local t = {} + for i = 1, 1000 do + t[i] = {} + end + `) + + if err != nil { + t.Fatalf("Unexpected error with no limit: %v", err) + } + + usage := L.GetAllocatedBytes() + if usage <= 0 { + t.Errorf("Expected usage to be tracked even without limit, got %d", usage) + } +} + +func TestMemoryLimit_DisableLimit(t *testing.T) { + L := NewState() + defer L.Close() + + // Reset usage after initialization, then set a very low limit + L.ResetMemoryUsage() + L.SetMemoryLimit(100) + + // Should fail quickly when adding values to table + err := L.DoString(` + local t = {} + for i = 1, 10 do + t[i] = i + end + `) + if err == nil { + t.Fatal("Expected memory limit error with low limit") + } + + // Disable limit by setting to 0 + L.SetMemoryLimit(0) + L.ResetMemoryUsage() + + // Should now succeed + err = L.DoString(` + local t = {} + for i = 1, 100 do + t[i] = i + end + `) + + if err != nil { + t.Fatalf("Unexpected error after disabling limit: %v", err) + } +} + +func TestMemoryLimit_StringUpper(t *testing.T) { + L := NewState() + defer L.Close() + + L.ResetMemoryUsage() + L.SetMemoryLimit(5 * 1024) // 5KB limit + + err := L.DoString(` + local s = string.rep("a", 1024) + for i = 1, 10 do + s = string.upper(s) -- Each creates a copy + end + `) + + if err == nil { + t.Fatal("Expected memory limit error, got nil") + } + + if !strings.Contains(err.Error(), "memory limit exceeded") { + t.Errorf("Expected 'memory limit exceeded' error, got: %v", err) + } +} + +func TestMemoryLimit_StringLower(t *testing.T) { + L := NewState() + defer L.Close() + + L.ResetMemoryUsage() + L.SetMemoryLimit(5 * 1024) // 5KB limit + + err := L.DoString(` + local s = string.rep("A", 1024) + for i = 1, 10 do + s = string.lower(s) -- Each creates a copy + end + `) + + if err == nil { + t.Fatal("Expected memory limit error, got nil") + } + + if !strings.Contains(err.Error(), "memory limit exceeded") { + t.Errorf("Expected 'memory limit exceeded' error, got: %v", err) + } +} + +func TestMemoryLimit_StringReverse(t *testing.T) { + L := NewState() + defer L.Close() + + L.ResetMemoryUsage() + L.SetMemoryLimit(5 * 1024) // 5KB limit + + err := L.DoString(` + local s = string.rep("abc", 300) + for i = 1, 10 do + s = string.reverse(s) -- Each creates a copy + end + `) + + if err == nil { + t.Fatal("Expected memory limit error, got nil") + } + + if !strings.Contains(err.Error(), "memory limit exceeded") { + t.Errorf("Expected 'memory limit exceeded' error, got: %v", err) + } +} + +func TestMemoryLimit_StringChar(t *testing.T) { + L := NewState() + defer L.Close() + + L.ResetMemoryUsage() + L.SetMemoryLimit(2 * 1024) // 2KB limit + + err := L.DoString(` + local results = {} + for i = 1, 1000 do + results[i] = string.char(65, 66, 67, 68, 69, 70) -- Creates small strings + end + `) + + if err == nil { + t.Fatal("Expected memory limit error, got nil") + } + + if !strings.Contains(err.Error(), "memory limit exceeded") { + t.Errorf("Expected 'memory limit exceeded' error, got: %v", err) + } +} + +func TestMemoryLimit_StringFormat(t *testing.T) { + L := NewState() + defer L.Close() + + L.ResetMemoryUsage() + L.SetMemoryLimit(5 * 1024) // 5KB limit + + err := L.DoString(` + local t = {} + for i = 1, 10 do + t[i] = string.format("Item %03d: value=%d, squared=%d", i, i*10, i*i) + end + return #t + `) + + if err != nil { + t.Fatalf("Expected success, got error: %v", err) + } + + allocated := L.GetAllocatedBytes() + if allocated == 0 { + t.Error("Expected non-zero memory allocation for string.format") + } + + err = L.DoString(` + local results = {} + for i = 1, 1000 do + results[i] = string.format("%1000s", "x") -- Creates 1000-byte strings + end + `) + + if err == nil { + t.Fatal("Expected memory limit error, got nil") + } + + if !strings.Contains(err.Error(), "memory limit exceeded") { + t.Errorf("Expected 'memory limit exceeded' error, got: %v", err) + } +} + +func TestMemoryLimit_StringSub(t *testing.T) { + L := NewState() + defer L.Close() + + L.ResetMemoryUsage() + L.SetMemoryLimit(3 * 1024) // 3KB limit + + err := L.DoString(` + local base = string.rep("x", 500) + local results = {} + for i = 1, 100 do + results[i] = string.sub(base, 1, 400) -- Creates 400-byte substrings + end + `) + + if err == nil { + t.Fatal("Expected memory limit error, got nil") + } + + if !strings.Contains(err.Error(), "memory limit exceeded") { + t.Errorf("Expected 'memory limit exceeded' error, got: %v", err) + } +} + +func TestMemoryLimit_StringGsub(t *testing.T) { + L := NewState() + defer L.Close() + + L.ResetMemoryUsage() + L.SetMemoryLimit(3 * 1024) + + err := L.DoString(` + local s = string.rep("a", 1000) + s = string.gsub(s, "a", "bbb") -- Triples size + `) + + if err == nil { + t.Fatal("Expected memory limit error, got nil") + } + + if !strings.Contains(err.Error(), "memory limit exceeded") { + t.Errorf("Expected 'memory limit exceeded' error, got: %v", err) + } +} + +func TestMemoryLimit_StringMatch(t *testing.T) { + L := NewState() + defer L.Close() + + L.ResetMemoryUsage() + L.SetMemoryLimit(3 * 1024) // 3KB limit + + err := L.DoString(` + local base = string.rep("test123", 100) + local results = {} + for i = 1, 1000 do + local cap1, cap2 = string.match(base, "(test)(%d+)") + results[i] = cap1 .. cap2 -- Captured strings + end + `) + + if err == nil { + t.Fatal("Expected memory limit error, got nil") + } + + if !strings.Contains(err.Error(), "memory limit exceeded") { + t.Errorf("Expected 'memory limit exceeded' error, got: %v", err) + } +} + +func TestMemoryLimit_StringFind(t *testing.T) { + L := NewState() + defer L.Close() + + L.ResetMemoryUsage() + L.SetMemoryLimit(3 * 1024) // 3KB limit + + err := L.DoString(` + local base = string.rep("hello world ", 50) + local results = {} + for i = 1, 1000 do + local _, _, cap = string.find(base, "(hello)") + results[i] = cap -- Captured strings + end + `) + + if err == nil { + t.Fatal("Expected memory limit error, got nil") + } + + if !strings.Contains(err.Error(), "memory limit exceeded") { + t.Errorf("Expected 'memory limit exceeded' error, got: %v", err) + } +} + +func TestMemoryLimit_StringOperationsTracking(t *testing.T) { + L := NewState() + defer L.Close() + + L.ResetMemoryUsage() + L.SetMemoryLimit(100 * 1024) // 100KB limit + + err := L.DoString(` + -- Test that memory is being tracked for various string operations + local s1 = string.upper("test") + local s2 = string.lower("TEST") + local s3 = string.reverse("hello") + local s4 = string.char(65, 66, 67) + local s5 = string.format("test %s", "value") + local s6 = string.sub("hello world", 1, 5) + local s7 = string.gsub("aaa", "a", "b") + local s8 = string.match("test123", "(%d+)") + local _, _, s9 = string.find("test", "(test)") + `) + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + usage := L.GetAllocatedBytes() + if usage <= 0 { + t.Errorf("Expected memory usage to be tracked for string operations, got %d", usage) + } +} + +func TestMemoryLimit_TableGrowth(t *testing.T) { + L := NewState() + defer L.Close() + + L.ResetMemoryUsage() + L.SetMemoryLimit(10 * 1024) // 10KB limit + + err := L.DoString(` + local t = {} + for i = 1, 10000 do + t[i] = i + end + `) + + if err == nil { + t.Fatal("Expected memory limit error for large table, got nil") + } + + if !strings.Contains(err.Error(), "memory limit exceeded") { + t.Errorf("Expected 'memory limit exceeded' error, got: %v", err) + } +} + +func TestMemoryLimit_Closures(t *testing.T) { + L := NewState() + defer L.Close() + + L.SetMemoryLimit(50 * 1024) // 50KB limit + L.ResetMemoryUsage() + + script := ` + local functions = {} + for i = 1, 100 do + local x = i + functions[i] = function() + return x * 2 + end + end + return functions[50]() + ` + + if err := L.DoString(script); err != nil { + t.Fatalf("Expected success, got error: %v", err) + } + + allocated := L.GetAllocatedBytes() + if allocated == 0 { + t.Error("Expected non-zero memory allocation for closures") + } +} + +func TestMemoryLimit_MixedOperations(t *testing.T) { + L := NewState() + defer L.Close() + + L.SetMemoryLimit(500 * 1024) // 500KB limit + L.ResetMemoryUsage() + + script := ` + -- Mix of array, hash, nested tables, strings, and functions + local data = {} + + -- Array part + for i = 1, 50 do + data[i] = i * 2 + end + + -- Hash part + for i = 1, 50 do + data["key" .. i] = "value" .. i + end + + -- Nested tables + data.nested = {} + for i = 1, 20 do + data.nested[i] = { x = i, y = i * 2, name = "item" .. i } + end + + -- Functions + data.functions = {} + for i = 1, 30 do + local val = i + data.functions[i] = function() return val * 3 end + end + + -- String operations + data.strings = {} + for i = 1, 50 do + data.strings[i] = string.rep("x", 100) .. i + end + + return data.functions[10]() + ` + + if err := L.DoString(script); err != nil { + t.Fatalf("Expected success, got error: %v", err) + } + + result := L.Get(-1) + if num, ok := result.(LNumber); !ok || num != 30 { + t.Errorf("Expected function result 30, got %v", result) + } + L.Pop(1) + + allocated := L.GetAllocatedBytes() + if allocated == 0 { + t.Error("Expected non-zero memory allocation for mixed operations") + } +} + +func TestMemoryLimit_LargeArraySparse(t *testing.T) { + L := NewState() + defer L.Close() + + L.SetMemoryLimit(500 * 1024) + L.ResetMemoryUsage() + + script := ` + local t = {} + -- Sparse array - only set specific indices + for i = 1, 100 do + t[i * 100] = i + end + return t[5000] + ` + + if err := L.DoString(script); err != nil { + t.Fatalf("Expected success, got error: %v", err) + } + + allocated := L.GetAllocatedBytes() + if allocated == 0 { + t.Error("Expected non-zero memory allocation for sparse array") + } +} + +func TestMemoryLimit_RecursiveTable(t *testing.T) { + L := NewState() + defer L.Close() + + L.SetMemoryLimit(60 * 1024) // 60KB limit (increased for nested tables) + L.ResetMemoryUsage() + + script := ` + local t = {} + t.self = t -- Recursive reference + + for i = 1, 50 do + t[i] = { parent = t, value = i } + end + + return #t + ` + + if err := L.DoString(script); err != nil { + t.Fatalf("Expected success, got error: %v", err) + } + + allocated := L.GetAllocatedBytes() + if allocated == 0 { + t.Error("Expected non-zero memory allocation for recursive tables") + } +} diff --git a/state.go b/state.go index 292f93b4..2253cc24 100644 --- a/state.go +++ b/state.go @@ -15,6 +15,7 @@ import ( "sync" "sync/atomic" "time" + "unsafe" "github.com/yuin/gopher-lua/parse" ) @@ -398,6 +399,15 @@ func (rg *registry) resize(requiredSize int) { // +inline-start } // +inline-end func (rg *registry) forceResize(newSize int) { + // Track memory growth for registry resize + oldSize := len(rg.array) + if newSize > oldSize { + additionalBytes := int64(newSize-oldSize) * 16 // LValue is 16 bytes + if ls, ok := rg.handler.(*LState); ok { + ls.TrackAlloc(additionalBytes) + } + } + newSlice := make([]LValue, newSize) copy(newSlice, rg.array[:rg.top]) // should we copy the area beyond top? there shouldn't be any valid values there so it shouldn't be necessary. rg.array = newSlice @@ -689,6 +699,42 @@ func newLState(options Options) *LState { return ls } +/* Memory tracking {{{ */ + +// TrackAlloc adds bytes to the memory allocation counter and checks against the limit. +// Raises a Lua error if the allocation would exceed the memory limit. +func (ls *LState) TrackAlloc(bytes int64) { + ls.allocatedBytes += bytes + + // Only check limit if one is set + if ls.maxBytes > 0 && ls.allocatedBytes > ls.maxBytes { + ls.RaiseError("memory limit exceeded: %d bytes allocated, limit is %d bytes", + ls.allocatedBytes, ls.maxBytes) + } +} + +// ResetMemoryUsage resets the allocated bytes counter to zero. +func (ls *LState) ResetMemoryUsage() { + ls.allocatedBytes = 0 +} + +// SetMemoryLimit sets the maximum memory limit in bytes. Set to 0 to disable limiting. +func (ls *LState) SetMemoryLimit(maxBytes int64) { + ls.maxBytes = maxBytes +} + +// GetAllocatedBytes returns the current number of tracked allocated bytes. +func (ls *LState) GetAllocatedBytes() int64 { + return ls.allocatedBytes +} + +// GetMemoryLimit returns the current memory limit in bytes (0 if no limit). +func (ls *LState) GetMemoryLimit() int64 { + return ls.maxBytes +} + +/* }}} */ + func (ls *LState) printReg() { println("-------------------------") println("thread:", ls) @@ -1111,7 +1157,7 @@ func (ls *LState) initCallFrame(cf *callFrame) { // +inline-start if CompatVarArg { ls.reg.SetTop(cf.LocalBase + nargs + np + 1) if (proto.IsVarArg & VarArgNeedsArg) != 0 { - argtb := newLTable(nvarargs, 0) + argtb := ls.newLTable(nvarargs, 0) for i := 0; i < nvarargs; i++ { argtb.RawSetInt(i+1, ls.reg.Get(cf.LocalBase+np+i)) } @@ -1223,7 +1269,7 @@ func (ls *LState) pushCallFrame(cf callFrame, fn LValue, meta bool) { // +inline if CompatVarArg { ls.reg.SetTop(cf.LocalBase + nargs + np + 1) if (proto.IsVarArg & VarArgNeedsArg) != 0 { - argtb := newLTable(nvarargs, 0) + argtb := ls.newLTable(nvarargs, 0) for i := 0; i < nvarargs; i++ { argtb.RawSetInt(i+1, ls.reg.Get(cf.LocalBase+np+i)) } @@ -1546,7 +1592,6 @@ func (ls *LState) Get(idx int) LValue { return LNil } } - return LNil } func (ls *LState) Push(value LValue) { @@ -1602,11 +1647,11 @@ func (ls *LState) Remove(index int) { /* object allocation {{{ */ func (ls *LState) NewTable() *LTable { - return newLTable(defaultArrayCap, defaultHashCap) + return ls.newLTable(defaultArrayCap, defaultHashCap) } func (ls *LState) CreateTable(acap, hcap int) *LTable { - return newLTable(acap, hcap) + return ls.newLTable(acap, hcap) } // NewThread returns a new LState that shares with the original state all global objects. @@ -1625,10 +1670,13 @@ func (ls *LState) NewThread() (*LState, context.CancelFunc) { } func (ls *LState) NewFunctionFromProto(proto *FunctionProto) *LFunction { - return newLFunctionL(proto, ls.Env, int(proto.NumUpvalues)) + return ls.newLFunctionL(proto, ls.Env, int(proto.NumUpvalues)) } func (ls *LState) NewUserData() *LUserData { + size := int64(unsafe.Sizeof(LUserData{})) + ls.TrackAlloc(size) + return &LUserData{ Env: ls.currentEnv(), Metatable: LNil, @@ -1636,11 +1684,11 @@ func (ls *LState) NewUserData() *LUserData { } func (ls *LState) NewFunction(fn LGFunction) *LFunction { - return newLFunctionG(fn, ls.currentEnv(), 0) + return ls.newLFunctionG(fn, ls.currentEnv(), 0) } func (ls *LState) NewClosure(fn LGFunction, upvalues ...LValue) *LFunction { - cl := newLFunctionG(fn, ls.currentEnv(), len(upvalues)) + cl := ls.newLFunctionG(fn, ls.currentEnv(), len(upvalues)) for i, lv := range upvalues { cl.Upvalues[i] = &Upvalue{} cl.Upvalues[i].Close() @@ -2019,7 +2067,7 @@ func (ls *LState) Load(reader io.Reader, name string) (*LFunction, error) { if err != nil { return nil, newApiErrorE(ApiErrorSyntax, err) } - return newLFunctionL(proto, ls.currentEnv(), 0), nil + return ls.newLFunctionL(proto, ls.currentEnv(), 0), nil } func (ls *LState) Call(nargs, nret int) { @@ -2095,7 +2143,7 @@ func (ls *LState) PCall(nargs, nret int, errfunc *LFunction) (err error) { } func (ls *LState) GPCall(fn LGFunction, data LValue) error { - ls.Push(newLFunctionG(fn, ls.currentEnv(), 0)) + ls.Push(ls.newLFunctionG(fn, ls.currentEnv(), 0)) ls.Push(data) return ls.PCall(1, MultRet, nil) } diff --git a/state_test.go b/state_test.go index 7b7aa53d..7a196bfb 100644 --- a/state_test.go +++ b/state_test.go @@ -281,7 +281,6 @@ func TestPCall(t *testing.T) { defer L.Close() L.Register("f1", func(L *LState) int { panic("panic!") - return 0 }) errorIfScriptNotFail(t, L, `f1()`, "panic!") L.Push(L.GetGlobal("f1")) @@ -301,7 +300,6 @@ func TestPCall(t *testing.T) { L.Push(L.GetGlobal("f1")) err = L.PCall(0, 0, L.NewFunction(func(L *LState) int { panic("panicc!") - return 1 })) errorIfFalse(t, strings.Contains(err.Error(), "panicc!"), "") diff --git a/stringlib.go b/stringlib.go index f484c2b3..10ab24e2 100644 --- a/stringlib.go +++ b/stringlib.go @@ -11,15 +11,15 @@ const emptyLString LString = LString("") func OpenString(L *LState) int { var mod *LTable - //_, ok := L.G.builtinMts[int(LTString)] - //if !ok { + // _, ok := L.G.builtinMts[int(LTString)] + // if !ok { mod = L.RegisterModule(StringLibName, strFuncs).(*LTable) gmatch := L.NewClosure(strGmatch, L.NewFunction(strGmatchIter)) mod.RawSetString("gmatch", gmatch) mod.RawSetString("gfind", gmatch) mod.RawSetString("__index", mod) L.G.builtinMts[int(LTString)] = mod - //} + // } L.Push(mod) return 1 } @@ -78,7 +78,9 @@ func strChar(L *LState) int { for i := 1; i <= top; i++ { bytes[i-1] = uint8(L.CheckInt(i)) } - L.Push(LString(string(bytes))) + result := string(bytes) + L.TrackAlloc(int64(len(result))) + L.Push(LString(result)) return 1 } @@ -127,7 +129,9 @@ func strFind(L *LState) int { if md.IsPosCapture(i) { L.Push(LNumber(md.Capture(i))) } else { - L.Push(LString(str[md.Capture(i):md.Capture(i+1)])) + capture := str[md.Capture(i):md.Capture(i+1)] + L.TrackAlloc(int64(len(capture))) + L.Push(LString(capture)) } } return md.CaptureLength()/2 + 1 @@ -141,7 +145,9 @@ func strFormat(L *LState) int { args[i-2] = L.Get(i) } npat := strings.Count(str, "%") - strings.Count(str, "%%") - L.Push(LString(fmt.Sprintf(str, args[:intMin(npat, len(args))]...))) + result := fmt.Sprintf(str, args[:intMin(npat, len(args))]...) + L.TrackAlloc(int64(len(result))) + L.Push(LString(result)) return 1 } @@ -161,14 +167,17 @@ func strGsub(L *LState) int { L.Push(LNumber(0)) return 2 } + var result string switch lv := repl.(type) { case LString: - L.Push(LString(strGsubStr(L, str, string(lv), mds))) + result = strGsubStr(L, str, string(lv), mds) case *LTable: - L.Push(LString(strGsubTable(L, str, lv, mds))) + result = strGsubTable(L, str, lv, mds) case *LFunction: - L.Push(LString(strGsubFunc(L, str, lv, mds))) + result = strGsubFunc(L, str, lv, mds) } + L.TrackAlloc(int64(len(result))) + L.Push(LString(result)) L.Push(LNumber(len(mds))) return 2 } @@ -310,7 +319,9 @@ func strGmatchIter(L *LState) int { L.Push(L.Get(1)) match := matches[idx] if match.CaptureLength() == 2 { - L.Push(LString(str[match.Capture(0):match.Capture(1)])) + capture := str[match.Capture(0):match.Capture(1)] + L.TrackAlloc(int64(len(capture))) + L.Push(LString(capture)) return 1 } @@ -318,7 +329,9 @@ func strGmatchIter(L *LState) int { if match.IsPosCapture(i) { L.Push(LNumber(match.Capture(i))) } else { - L.Push(LString(str[match.Capture(i):match.Capture(i+1)])) + capture := str[match.Capture(i):match.Capture(i+1)] + L.TrackAlloc(int64(len(capture))) + L.Push(LString(capture)) } } return match.CaptureLength()/2 - 1 @@ -346,7 +359,9 @@ func strLen(L *LState) int { func strLower(L *LState) int { str := L.CheckString(1) - L.Push(LString(strings.ToLower(str))) + result := strings.ToLower(str) + L.TrackAlloc(int64(len(result))) + L.Push(LString(result)) return 1 } @@ -375,14 +390,18 @@ func strMatch(L *LState) int { nsubs := md.CaptureLength() / 2 switch nsubs { case 1: - L.Push(LString(str[md.Capture(0):md.Capture(1)])) + capture := str[md.Capture(0):md.Capture(1)] + L.TrackAlloc(int64(len(capture))) + L.Push(LString(capture)) return 1 default: for i := 2; i < md.CaptureLength(); i += 2 { if md.IsPosCapture(i) { L.Push(LNumber(md.Capture(i))) } else { - L.Push(LString(str[md.Capture(i):md.Capture(i+1)])) + capture := str[md.Capture(i):md.Capture(i+1)] + L.TrackAlloc(int64(len(capture))) + L.Push(LString(capture)) } } return nsubs - 1 @@ -395,7 +414,9 @@ func strRep(L *LState) int { if n < 0 { L.Push(emptyLString) } else { - L.Push(LString(strings.Repeat(str, n))) + L.TrackAlloc(int64(len(str) * n)) + result := strings.Repeat(str, n) + L.Push(LString(result)) } return 1 } @@ -407,7 +428,9 @@ func strReverse(L *LState) int { for i, j := 0, len(bts)-1; j >= 0; i, j = i+1, j-1 { out[i] = bts[j] } - L.Push(LString(string(out))) + result := string(out) + L.TrackAlloc(int64(len(result))) + L.Push(LString(result)) return 1 } @@ -419,14 +442,18 @@ func strSub(L *LState) int { if start >= l || end < start { L.Push(emptyLString) } else { - L.Push(LString(str[start:end])) + result := str[start:end] + L.TrackAlloc(int64(len(result))) + L.Push(LString(result)) } return 1 } func strUpper(L *LState) int { str := L.CheckString(1) - L.Push(LString(strings.ToUpper(str))) + result := strings.ToUpper(str) + L.TrackAlloc(int64(len(result))) + L.Push(LString(result)) return 1 } diff --git a/table.go b/table.go index ddf14dd8..f4622938 100644 --- a/table.go +++ b/table.go @@ -1,5 +1,7 @@ package lua +import "unsafe" + const defaultArrayCap = 32 const defaultHashCap = 32 @@ -28,6 +30,47 @@ func (lv lValueArraySorter) Less(i, j int) bool { return lessThan(lv.L, lv.Values[i], lv.Values[j]) } +// newLTable creates a new table with memory tracking. +// acap is the array capacity hint, hcap is the hash capacity hint. +func (ls *LState) newLTable(acap int, hcap int) *LTable { + if acap < 0 { + acap = 0 + } + if hcap < 0 { + hcap = 0 + } + + // Calculate memory allocation size + // Base struct + array slice + hash map + size := int64(unsafe.Sizeof(LTable{})) + if acap != 0 { + // LValue is an interface: 16 bytes (2 pointers) on 64-bit + size += int64(acap) * 16 + } + if hcap != 0 { + // Map overhead: approximately 48 bytes per entry for string -> LValue map + size += int64(hcap) * 48 + } + + // Track allocation before creating the table + ls.TrackAlloc(size) + + tb := <able{} + tb.Metatable = LNil + tb.ls = ls + tb.allocBytes = size + + if acap != 0 { + tb.array = make([]LValue, 0, acap) + } + if hcap != 0 { + tb.strdict = make(map[string]LValue, hcap) + } + return tb +} + +// newLTable creates a table without memory tracking. +// Used only for internal registry tables created during LState initialization. func newLTable(acap int, hcap int) *LTable { if acap < 0 { acap = 0 @@ -46,6 +89,16 @@ func newLTable(acap int, hcap int) *LTable { return tb } +// trackGrowth tracks memory allocation for table growth. +// Raises a Lua error if the allocation would exceed the memory limit. +func (tb *LTable) trackGrowth(additionalBytes int64) { + if tb.ls == nil { + return // No tracking if not associated with LState + } + tb.ls.TrackAlloc(additionalBytes) + tb.allocBytes += additionalBytes +} + // Len returns length of this LTable without using __len. func (tb *LTable) Len() int { if tb.array == nil { @@ -68,9 +121,19 @@ func (tb *LTable) Append(value LValue) { return } if tb.array == nil { + tb.trackGrowth(int64(defaultArrayCap) * 16) tb.array = make([]LValue, 0, defaultArrayCap) } if len(tb.array) == 0 || tb.array[len(tb.array)-1] != LNil { + // Track growth if append will exceed capacity + if len(tb.array) >= cap(tb.array) { + newCap := cap(tb.array) * 2 + if newCap == 0 { + newCap = 1 + } + additionalBytes := int64(newCap-cap(tb.array)) * 16 + tb.trackGrowth(additionalBytes) + } tb.array = append(tb.array, value) } else { i := len(tb.array) - 2 @@ -86,6 +149,7 @@ func (tb *LTable) Append(value LValue) { // Insert inserts a given LValue at position `i` in this table. func (tb *LTable) Insert(i int, value LValue) { if tb.array == nil { + tb.trackGrowth(int64(defaultArrayCap) * 16) tb.array = make([]LValue, 0, defaultArrayCap) } if i > len(tb.array) { @@ -97,6 +161,15 @@ func (tb *LTable) Insert(i int, value LValue) { return } i -= 1 + // Track growth if append will exceed capacity + if len(tb.array) >= cap(tb.array) { + newCap := cap(tb.array) * 2 + if newCap == 0 { + newCap = 1 + } + additionalBytes := int64(newCap-cap(tb.array)) * 16 + tb.trackGrowth(additionalBytes) + } tb.array = append(tb.array, LNil) copy(tb.array[i+1:], tb.array[i:]) tb.array[i] = value @@ -149,14 +222,36 @@ func (tb *LTable) RawSet(key LValue, value LValue) { case LNumber: if isArrayKey(v) { if tb.array == nil { + tb.trackGrowth(int64(defaultArrayCap) * 16) tb.array = make([]LValue, 0, defaultArrayCap) } index := int(v) - 1 alen := len(tb.array) switch { case index == alen: + // Track growth if append will exceed capacity + if len(tb.array) >= cap(tb.array) { + newCap := cap(tb.array) * 2 + if newCap == 0 { + newCap = 1 + } + additionalBytes := int64(newCap-cap(tb.array)) * 16 + tb.trackGrowth(additionalBytes) + } tb.array = append(tb.array, value) case index > alen: + neededLen := index + 1 + if neededLen > cap(tb.array) { + newCap := cap(tb.array) + if newCap == 0 { + newCap = 1 + } + for newCap < neededLen { + newCap *= 2 + } + additionalBytes := int64(newCap-cap(tb.array)) * 16 + tb.trackGrowth(additionalBytes) + } for i := 0; i < (index - alen); i++ { tb.array = append(tb.array, LNil) } @@ -181,14 +276,36 @@ func (tb *LTable) RawSetInt(key int, value LValue) { return } if tb.array == nil { + tb.trackGrowth(int64(32) * 16) tb.array = make([]LValue, 0, 32) } index := key - 1 alen := len(tb.array) switch { case index == alen: + // Track growth if append will exceed capacity + if len(tb.array) >= cap(tb.array) { + newCap := cap(tb.array) * 2 + if newCap == 0 { + newCap = 1 + } + additionalBytes := int64(newCap-cap(tb.array)) * 16 + tb.trackGrowth(additionalBytes) + } tb.array = append(tb.array, value) case index > alen: + neededLen := index + 1 + if neededLen > cap(tb.array) { + newCap := cap(tb.array) + if newCap == 0 { + newCap = 1 + } + for newCap < neededLen { + newCap *= 2 + } + additionalBytes := int64(newCap-cap(tb.array)) * 16 + tb.trackGrowth(additionalBytes) + } for i := 0; i < (index - alen); i++ { tb.array = append(tb.array, LNil) } @@ -201,9 +318,11 @@ func (tb *LTable) RawSetInt(key int, value LValue) { // RawSetString sets a given LValue to a given string index without the __newindex metamethod. func (tb *LTable) RawSetString(key string, value LValue) { if tb.strdict == nil { + tb.trackGrowth(int64(defaultHashCap) * 48) tb.strdict = make(map[string]LValue, defaultHashCap) } if tb.keys == nil { + tb.trackGrowth(int64(defaultHashCap) * 24) // keys array + k2i map overhead tb.keys = []LValue{} tb.k2i = map[LValue]int{} } @@ -228,9 +347,15 @@ func (tb *LTable) RawSetH(key LValue, value LValue) { return } if tb.dict == nil { - tb.dict = make(map[LValue]LValue, len(tb.strdict)) + dictCap := len(tb.strdict) + if dictCap == 0 { + dictCap = defaultHashCap + } + tb.trackGrowth(int64(dictCap) * 48) + tb.dict = make(map[LValue]LValue, dictCap) } if tb.keys == nil { + tb.trackGrowth(int64(defaultHashCap) * 24) // keys array + k2i map overhead tb.keys = []LValue{} tb.k2i = map[LValue]int{} } diff --git a/value.go b/value.go index 4156e9d5..80716e68 100644 --- a/value.go +++ b/value.go @@ -148,6 +148,10 @@ type LTable struct { strdict map[string]LValue keys []LValue k2i map[LValue]int + + // Memory tracking + ls *LState + allocBytes int64 } func (tb *LTable) String() string { return fmt.Sprintf("table: %p", tb) } @@ -195,6 +199,10 @@ type LState struct { mainLoop func(*LState, *callFrame) ctx context.Context ctxCancelFn context.CancelFunc + + // Memory tracking + allocatedBytes int64 + maxBytes int64 } func (ls *LState) String() string { return fmt.Sprintf("thread: %p", ls) } diff --git a/vm.go b/vm.go index 97335a75..480d976a 100644 --- a/vm.go +++ b/vm.go @@ -300,13 +300,13 @@ var jumpTable [opCodeMax + 1]instFunc func init() { jumpTable = [opCodeMax + 1]instFunc{ - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_MOVE + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_MOVE reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB v := reg.Get(lbase + B) // this section is inlined by go-inline // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' @@ -330,13 +330,13 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_MOVEN + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_MOVEN reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + A := int(inst>>18) & 0xff // GETA + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC v := reg.Get(lbase + B) // this section is inlined by go-inline // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' @@ -363,8 +363,8 @@ func init() { for i := 0; i < C; i++ { inst = code[pc] pc++ - A = int(inst>>18) & 0xff //GETA - B = int(inst & 0x1ff) //GETB + A = int(inst>>18) & 0xff // GETA + B = int(inst & 0x1ff) // GETB v := reg.Get(lbase + B) // this section is inlined by go-inline // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' @@ -390,13 +390,13 @@ func init() { cf.Pc = pc return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LOADK + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_LOADK reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - Bx := int(inst & 0x3ffff) //GETBX + Bx := int(inst & 0x3ffff) // GETBX v := cf.Fn.Proto.Constants[Bx] // this section is inlined by go-inline // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' @@ -420,14 +420,14 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LOADBOOL + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_LOADBOOL reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC if B != 0 { // this section is inlined by go-inline // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' @@ -476,13 +476,13 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LOADNIL + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_LOADNIL reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB for i := RA; i <= lbase+B; i++ { // this section is inlined by go-inline // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' @@ -507,13 +507,13 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_GETUPVAL + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_GETUPVAL reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB v := cf.Fn.Upvalues[B].Value() // this section is inlined by go-inline // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' @@ -537,14 +537,14 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_GETGLOBAL + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_GETGLOBAL reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - Bx := int(inst & 0x3ffff) //GETBX - //reg.Set(RA, L.getField(cf.Fn.Env, cf.Fn.Proto.Constants[Bx])) + Bx := int(inst & 0x3ffff) // GETBX + // reg.Set(RA, L.getField(cf.Fn.Env, cf.Fn.Proto.Constants[Bx])) v := L.getFieldString(cf.Fn.Env, cf.Fn.Proto.stringConstants[Bx]) // this section is inlined by go-inline // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' @@ -568,14 +568,14 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_GETTABLE + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_GETTABLE reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC v := L.getField(reg.Get(lbase+B), L.rkValue(C)) // this section is inlined by go-inline // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' @@ -599,14 +599,14 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_GETTABLEKS + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_GETTABLEKS reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC v := L.getFieldString(reg.Get(lbase+B), L.rkString(C)) // this section is inlined by go-inline // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' @@ -630,58 +630,58 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SETGLOBAL + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_SETGLOBAL reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - Bx := int(inst & 0x3ffff) //GETBX - //L.setField(cf.Fn.Env, cf.Fn.Proto.Constants[Bx], reg.Get(RA)) + Bx := int(inst & 0x3ffff) // GETBX + // L.setField(cf.Fn.Env, cf.Fn.Proto.Constants[Bx], reg.Get(RA)) L.setFieldString(cf.Fn.Env, cf.Fn.Proto.stringConstants[Bx], reg.Get(RA)) return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SETUPVAL + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_SETUPVAL reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB cf.Fn.Upvalues[B].SetValue(reg.Get(RA)) return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SETTABLE + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_SETTABLE reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC L.setField(reg.Get(RA), L.rkValue(B), L.rkValue(C)) return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SETTABLEKS + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_SETTABLEKS reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC L.setFieldString(reg.Get(RA), L.rkString(B), L.rkValue(C)) return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_NEWTABLE + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_NEWTABLE reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC - v := newLTable(B, C) + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC + v := L.newLTable(B, C) // this section is inlined by go-inline // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' { @@ -704,14 +704,14 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SELF + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_SELF reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC selfobj := reg.Get(lbase + B) v := L.getFieldString(selfobj, L.rkString(C)) // this section is inlined by go-inline @@ -762,13 +762,13 @@ func init() { opArith, // OP_DIV opArith, // OP_MOD opArith, // OP_POW - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_UNM + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_UNM reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB unaryv := L.rkValue(B) if nm, ok := unaryv.(LNumber); ok { // this section is inlined by go-inline @@ -848,13 +848,13 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_NOT + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_NOT reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB if LVIsFalse(reg.Get(lbase + B)) { // this section is inlined by go-inline // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' @@ -900,13 +900,13 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LEN + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_LEN reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB switch lv := L.rkValue(B).(type) { case LString: // this section is inlined by go-inline @@ -1007,14 +1007,14 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_CONCAT + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_CONCAT reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC RC := lbase + C RB := lbase + B v := stringConcat(L, RC-RB+1, RC) @@ -1040,17 +1040,17 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_JMP + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_JMP cf := L.currentFrame - Sbx := int(inst&0x3ffff) - opMaxArgSbx //GETSBX + Sbx := int(inst&0x3ffff) - opMaxArgSbx // GETSBX cf.Pc += Sbx return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_EQ + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_EQ cf := L.currentFrame - A := int(inst>>18) & 0xff //GETA - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + A := int(inst>>18) & 0xff // GETA + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC ret := equals(L, L.rkValue(B), L.rkValue(C), false) v := 1 if ret { @@ -1061,11 +1061,11 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LT + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_LT cf := L.currentFrame - A := int(inst>>18) & 0xff //GETA - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + A := int(inst>>18) & 0xff // GETA + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC ret := lessThan(L, L.rkValue(B), L.rkValue(C)) v := 1 if ret { @@ -1076,11 +1076,11 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LE + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_LE cf := L.currentFrame - A := int(inst>>18) & 0xff //GETA - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + A := int(inst>>18) & 0xff // GETA + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC lhs := L.rkValue(B) rhs := L.rkValue(C) ret := false @@ -1119,26 +1119,26 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_TEST + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_TEST reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - C := int(inst>>9) & 0x1ff //GETC + C := int(inst>>9) & 0x1ff // GETC if LVAsBool(reg.Get(RA)) == (C == 0) { cf.Pc++ } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_TESTSET + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_TESTSET reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC if value := reg.Get(lbase + B); LVAsBool(value) != (C == 0) { // this section is inlined by go-inline // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' @@ -1165,14 +1165,14 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_CALL + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_CALL reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC nargs := B - 1 if B == 0 { nargs = reg.Top() - (RA + 1) @@ -1286,7 +1286,7 @@ func init() { if CompatVarArg { ls.reg.SetTop(cf.LocalBase + nargs + np + 1) if (proto.IsVarArg & VarArgNeedsArg) != 0 { - argtb := newLTable(nvarargs, 0) + argtb := ls.newLTable(nvarargs, 0) for i := 0; i < nvarargs; i++ { argtb.RawSetInt(i+1, ls.reg.Get(cf.LocalBase+np+i)) } @@ -1310,13 +1310,13 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_TAILCALL + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_TAILCALL reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB nargs := B - 1 if B == 0 { nargs = reg.Top() - (RA + 1) @@ -1378,9 +1378,9 @@ func init() { cf.Pc = 0 cf.Base = RA cf.LocalBase = RA + 1 - cf.ReturnBase = cf.ReturnBase + // cf.ReturnBase = cf.ReturnBase cf.NArgs = nargs - cf.NRet = cf.NRet + // cf.NRet = cf.NRet cf.TailCall++ lbase := cf.LocalBase if meta { @@ -1468,7 +1468,7 @@ func init() { if CompatVarArg { ls.reg.SetTop(cf.LocalBase + nargs + np + 1) if (proto.IsVarArg & VarArgNeedsArg) != 0 { - argtb := newLTable(nvarargs, 0) + argtb := ls.newLTable(nvarargs, 0) for i := 0; i < nvarargs; i++ { argtb.RawSetInt(i+1, ls.reg.Get(cf.LocalBase+np+i)) } @@ -1530,13 +1530,13 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_RETURN + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_RETURN reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB // this section is inlined by go-inline // source function is 'func (ls *LState) closeUpvalues(idx int) ' in '_state.go' { @@ -1789,11 +1789,11 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_FORLOOP + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_FORLOOP reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A if init, ok1 := reg.Get(RA).(LNumber); ok1 { if limit, ok2 := reg.Get(RA + 1).(LNumber); ok2 { @@ -1821,7 +1821,7 @@ func init() { } } if (step > 0 && init <= limit) || (step <= 0 && init >= limit) { - Sbx := int(inst&0x3ffff) - opMaxArgSbx //GETSBX + Sbx := int(inst&0x3ffff) - opMaxArgSbx // GETSBX cf.Pc += Sbx // this section is inlined by go-inline // source function is 'func (rg *registry) SetNumber(regi int, vali LNumber) ' in '_state.go' @@ -1886,13 +1886,13 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_FORPREP + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_FORPREP reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - Sbx := int(inst&0x3ffff) - opMaxArgSbx //GETSBX + Sbx := int(inst&0x3ffff) - opMaxArgSbx // GETSBX if init, ok1 := reg.Get(RA).(LNumber); ok1 { if step, ok2 := reg.Get(RA + 2).(LNumber); ok2 { // this section is inlined by go-inline @@ -1924,13 +1924,13 @@ func init() { cf.Pc += Sbx return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_TFORLOOP + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_TFORLOOP reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - C := int(inst>>9) & 0x1ff //GETC + C := int(inst>>9) & 0x1ff // GETC nret := C // this section is inlined by go-inline // source function is 'func (rg *registry) SetTop(topi int) ' in '_state.go' @@ -2050,14 +2050,14 @@ func init() { cf.Pc++ return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SETLIST + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_SETLIST reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC if C == 0 { C = int(cf.Fn.Proto.Code[cf.Pc]) cf.Pc++ @@ -2073,10 +2073,10 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_CLOSE + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_CLOSE cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A // this section is inlined by go-inline // source function is 'func (ls *LState) closeUpvalues(idx int) ' in '_state.go' @@ -2100,15 +2100,15 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_CLOSURE + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_CLOSURE reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - Bx := int(inst & 0x3ffff) //GETBX + Bx := int(inst & 0x3ffff) // GETBX proto := cf.Fn.Proto.FunctionPrototypes[Bx] - closure := newLFunctionL(proto, cf.Fn.Env, int(proto.NumUpvalues)) + closure := L.newLFunctionL(proto, cf.Fn.Env, int(proto.NumUpvalues)) // this section is inlined by go-inline // source function is 'func (rg *registry) Set(regi int, vali LValue) ' in '_state.go' { @@ -2142,13 +2142,13 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_VARARG + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_VARARG reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - B := int(inst & 0x1ff) //GETB + B := int(inst & 0x1ff) // GETB nparams := int(cf.Fn.Proto.NumParameters) nvarargs := cf.NArgs - nparams if nvarargs < 0 { @@ -2200,21 +2200,21 @@ func init() { } return 0 }, - func(L *LState, inst uint32, baseframe *callFrame) int { //OP_NOP + func(L *LState, inst uint32, baseframe *callFrame) int { // OP_NOP return 0 }, } } -func opArith(L *LState, inst uint32, baseframe *callFrame) int { //OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, OP_POW +func opArith(L *LState, inst uint32, baseframe *callFrame) int { // OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, OP_POW reg := L.reg cf := L.currentFrame lbase := cf.LocalBase - A := int(inst>>18) & 0xff //GETA + A := int(inst>>18) & 0xff // GETA RA := lbase + A - opcode := int(inst >> 26) //GETOPCODE - B := int(inst & 0x1ff) //GETB - C := int(inst>>9) & 0x1ff //GETC + opcode := int(inst >> 26) // GETOPCODE + B := int(inst & 0x1ff) // GETB + C := int(inst>>9) & 0x1ff // GETC lhs := L.rkValue(B) rhs := L.rkValue(C) v1, ok1 := lhs.(LNumber) @@ -2295,7 +2295,6 @@ func numberArith(L *LState, opcode int, lhs, rhs LNumber) LNumber { return LNumber(math.Pow(flhs, frhs)) } panic("should not reach here") - return LNumber(0) } func objectArith(L *LState, opcode int, lhs, rhs LValue) LValue { @@ -2374,7 +2373,9 @@ func stringConcat(L *LState, total, last int) LValue { i-- total-- } - rhs = LString(strings.Join(buf, "")) + result := strings.Join(buf, "") + L.TrackAlloc(int64(len(result))) + rhs = LString(result) } } return rhs