@@ -11,6 +11,7 @@ import (
1111 "sync"
1212 "sync/atomic"
1313 "time"
14+ "unsafe"
1415
1516 "github.com/yuin/gopher-lua/parse"
1617)
@@ -394,6 +395,15 @@ func (rg *registry) resize(requiredSize int) { // +inline-start
394395} // +inline-end
395396
396397func (rg * registry ) forceResize (newSize int ) {
398+ // Track memory growth for registry resize
399+ oldSize := len (rg .array )
400+ if newSize > oldSize {
401+ additionalBytes := int64 (newSize - oldSize ) * 16 // LValue is 16 bytes
402+ if ls , ok := rg .handler .(* LState ); ok {
403+ ls .TrackAlloc (additionalBytes )
404+ }
405+ }
406+
397407 newSlice := make ([]LValue , newSize )
398408 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.
399409 rg .array = newSlice
@@ -589,6 +599,42 @@ func newLState(options Options) *LState {
589599 return ls
590600}
591601
602+ /* Memory tracking {{{ */
603+
604+ // TrackAlloc adds bytes to the memory allocation counter and checks against the limit.
605+ // Raises a Lua error if the allocation would exceed the memory limit.
606+ func (ls * LState ) TrackAlloc (bytes int64 ) {
607+ ls .allocatedBytes += bytes
608+
609+ // Only check limit if one is set
610+ if ls .maxBytes > 0 && ls .allocatedBytes > ls .maxBytes {
611+ ls .RaiseError ("memory limit exceeded: %d bytes allocated, limit is %d bytes" ,
612+ ls .allocatedBytes , ls .maxBytes )
613+ }
614+ }
615+
616+ // ResetMemoryUsage resets the allocated bytes counter to zero.
617+ func (ls * LState ) ResetMemoryUsage () {
618+ ls .allocatedBytes = 0
619+ }
620+
621+ // SetMemoryLimit sets the maximum memory limit in bytes. Set to 0 to disable limiting.
622+ func (ls * LState ) SetMemoryLimit (maxBytes int64 ) {
623+ ls .maxBytes = maxBytes
624+ }
625+
626+ // GetAllocatedBytes returns the current number of tracked allocated bytes.
627+ func (ls * LState ) GetAllocatedBytes () int64 {
628+ return ls .allocatedBytes
629+ }
630+
631+ // GetMemoryLimit returns the current memory limit in bytes (0 if no limit).
632+ func (ls * LState ) GetMemoryLimit () int64 {
633+ return ls .maxBytes
634+ }
635+
636+ /* }}} */
637+
592638func (ls * LState ) printReg () {
593639 println ("-------------------------" )
594640 println ("thread:" , ls )
@@ -995,7 +1041,7 @@ func (ls *LState) initCallFrame(cf *callFrame) { // +inline-start
9951041 if CompatVarArg {
9961042 ls .reg .SetTop (cf .LocalBase + nargs + np + 1 )
9971043 if (proto .IsVarArg & VarArgNeedsArg ) != 0 {
998- argtb := newLTable (nvarargs , 0 )
1044+ argtb := ls . newLTable (nvarargs , 0 )
9991045 for i := 0 ; i < nvarargs ; i ++ {
10001046 argtb .RawSetInt (i + 1 , ls .reg .Get (cf .LocalBase + np + i ))
10011047 }
@@ -1333,7 +1379,6 @@ func (ls *LState) Get(idx int) LValue {
13331379 return LNil
13341380 }
13351381 }
1336- return LNil
13371382}
13381383
13391384func (ls * LState ) Push (value LValue ) {
@@ -1389,11 +1434,11 @@ func (ls *LState) Remove(index int) {
13891434/* object allocation {{{ */
13901435
13911436func (ls * LState ) NewTable () * LTable {
1392- return newLTable (defaultArrayCap , defaultHashCap )
1437+ return ls . newLTable (defaultArrayCap , defaultHashCap )
13931438}
13941439
13951440func (ls * LState ) CreateTable (acap , hcap int ) * LTable {
1396- return newLTable (acap , hcap )
1441+ return ls . newLTable (acap , hcap )
13971442}
13981443
13991444// NewThread returns a new LState that shares with the original state all global objects.
@@ -1412,22 +1457,25 @@ func (ls *LState) NewThread() (*LState, context.CancelFunc) {
14121457}
14131458
14141459func (ls * LState ) NewFunctionFromProto (proto * FunctionProto ) * LFunction {
1415- return newLFunctionL (proto , ls .Env , int (proto .NumUpvalues ))
1460+ return ls . newLFunctionL (proto , ls .Env , int (proto .NumUpvalues ))
14161461}
14171462
14181463func (ls * LState ) NewUserData () * LUserData {
1464+ size := int64 (unsafe .Sizeof (LUserData {}))
1465+ ls .TrackAlloc (size )
1466+
14191467 return & LUserData {
14201468 Env : ls .currentEnv (),
14211469 Metatable : LNil ,
14221470 }
14231471}
14241472
14251473func (ls * LState ) NewFunction (fn LGFunction ) * LFunction {
1426- return newLFunctionG (fn , ls .currentEnv (), 0 )
1474+ return ls . newLFunctionG (fn , ls .currentEnv (), 0 )
14271475}
14281476
14291477func (ls * LState ) NewClosure (fn LGFunction , upvalues ... LValue ) * LFunction {
1430- cl := newLFunctionG (fn , ls .currentEnv (), len (upvalues ))
1478+ cl := ls . newLFunctionG (fn , ls .currentEnv (), len (upvalues ))
14311479 for i , lv := range upvalues {
14321480 cl .Upvalues [i ] = & Upvalue {}
14331481 cl .Upvalues [i ].Close ()
@@ -1806,7 +1854,7 @@ func (ls *LState) Load(reader io.Reader, name string) (*LFunction, error) {
18061854 if err != nil {
18071855 return nil , newApiErrorE (ApiErrorSyntax , err )
18081856 }
1809- return newLFunctionL (proto , ls .currentEnv (), 0 ), nil
1857+ return ls . newLFunctionL (proto , ls .currentEnv (), 0 ), nil
18101858}
18111859
18121860func (ls * LState ) Call (nargs , nret int ) {
@@ -1882,7 +1930,7 @@ func (ls *LState) PCall(nargs, nret int, errfunc *LFunction) (err error) {
18821930}
18831931
18841932func (ls * LState ) GPCall (fn LGFunction , data LValue ) error {
1885- ls .Push (newLFunctionG (fn , ls .currentEnv (), 0 ))
1933+ ls .Push (ls . newLFunctionG (fn , ls .currentEnv (), 0 ))
18861934 ls .Push (data )
18871935 return ls .PCall (1 , MultRet , nil )
18881936}
0 commit comments