package lua

////////////////////////////////////////////////////////
// This file was generated by go-inline. DO NOT EDIT. //
////////////////////////////////////////////////////////

import (
	"context"
	"fmt"
	"github.com/yuin/gopher-lua/parse"
	"io"
	"math"
	"os"
	"runtime"
	"strings"
	"sync/atomic"
	"time"
)

const MultRet = -1
const RegistryIndex = -10000
const EnvironIndex = -10001
const GlobalsIndex = -10002

/* ApiError {{{ */

type ApiError struct {
	Type       ApiErrorType
	Object     LValue
	StackTrace string
	// Underlying error. This attribute is set only if the Type is ApiErrorFile or ApiErrorSyntax
	Cause error
}

func newApiError(code ApiErrorType, object LValue) *ApiError {
	return &ApiError{code, object, "", nil}
}

func newApiErrorS(code ApiErrorType, message string) *ApiError {
	return newApiError(code, LString(message))
}

func newApiErrorE(code ApiErrorType, err error) *ApiError {
	return &ApiError{code, LString(err.Error()), "", err}
}

func (e *ApiError) Error() string {
	if len(e.StackTrace) > 0 {
		return fmt.Sprintf("%s\n%s", e.Object.String(), e.StackTrace)
	}
	return e.Object.String()
}

type ApiErrorType int

const (
	ApiErrorSyntax ApiErrorType = iota
	ApiErrorFile
	ApiErrorRun
	ApiErrorError
	ApiErrorPanic
)

/* }}} */

/* ResumeState {{{ */

type ResumeState int

const (
	ResumeOK ResumeState = iota
	ResumeYield
	ResumeError
)

/* }}} */

/* P {{{ */

type P struct {
	Fn      LValue
	NRet    int
	Protect bool
	Handler *LFunction
}

/* }}} */

/* Options {{{ */

// Options is a configuration that is used to create a new LState.
type Options struct {
	// Call stack size. This defaults to `lua.CallStackSize`.
	CallStackSize int
	// Data stack size. This defaults to `lua.RegistrySize`.
	RegistrySize int
	// Controls whether or not libraries are opened by default
	SkipOpenLibs bool
	// Tells whether a Go stacktrace should be included in a Lua stacktrace when panics occur.
	IncludeGoStackTrace bool
}

/* }}} */

/* Debug {{{ */

type Debug struct {
	frame           *callFrame
	Name            string
	What            string
	Source          string
	CurrentLine     int
	NUpvalues       int
	LineDefined     int
	LastLineDefined int
}

/* }}} */

/* callFrame {{{ */

type callFrame struct {
	Idx        int
	Fn         *LFunction
	Parent     *callFrame
	Pc         int
	Base       int
	LocalBase  int
	ReturnBase int
	NArgs      int
	NRet       int
	TailCall   int
}

type callFrameStack struct {
	array []callFrame
	sp    int
}

func newCallFrameStack(size int) *callFrameStack {
	return &callFrameStack{
		array: make([]callFrame, size),
		sp:    0,
	}
}

func (cs *callFrameStack) IsEmpty() bool { return cs.sp == 0 }

func (cs *callFrameStack) Clear() {
	cs.sp = 0
}

func (cs *callFrameStack) Push(v callFrame) { // +inline-start
	cs.array[cs.sp] = v
	cs.array[cs.sp].Idx = cs.sp
	cs.sp++
} // +inline-end

func (cs *callFrameStack) Remove(sp int) {
	psp := sp - 1
	nsp := sp + 1
	var pre *callFrame
	var next *callFrame
	if psp > 0 {
		pre = &cs.array[psp]
	}
	if nsp < cs.sp {
		next = &cs.array[nsp]
	}
	if next != nil {
		next.Parent = pre
	}
	for i := sp; i+1 < cs.sp; i++ {
		cs.array[i] = cs.array[i+1]
		cs.array[i].Idx = i
		cs.sp = i
	}
	cs.sp++
}

func (cs *callFrameStack) Sp() int {
	return cs.sp
}

func (cs *callFrameStack) SetSp(sp int) {
	cs.sp = sp
}

func (cs *callFrameStack) Last() *callFrame {
	if cs.sp == 0 {
		return nil
	}
	return &cs.array[cs.sp-1]
}

func (cs *callFrameStack) At(sp int) *callFrame {
	return &cs.array[sp]
}

func (cs *callFrameStack) Pop() *callFrame {
	cs.sp--
	return &cs.array[cs.sp]
}

/* }}} */

/* registry {{{ */

type registry struct {
	array []LValue
	top   int
	alloc *allocator
}

func newRegistry(size int, alloc *allocator) *registry {
	return &registry{make([]LValue, size), 0, alloc}
}

func (rg *registry) SetTop(top int) {
	oldtop := rg.top
	rg.top = top
	for i := oldtop; i < rg.top; i++ {
		rg.array[i] = LNil
	}
	for i := rg.top; i < oldtop; i++ {
		rg.array[i] = LNil
	}
}

func (rg *registry) Top() int {
	return rg.top
}

func (rg *registry) Push(v LValue) {
	rg.array[rg.top] = v
	rg.top++
}

func (rg *registry) Pop() LValue {
	v := rg.array[rg.top-1]
	rg.array[rg.top-1] = LNil
	rg.top--
	return v
}

func (rg *registry) Get(reg int) LValue {
	return rg.array[reg]
}

func (rg *registry) CopyRange(regv, start, limit, n int) { // +inline-start
	for i := 0; i < n; i++ {
		if tidx := start + i; tidx >= rg.top || limit > -1 && tidx >= limit || tidx < 0 {
			rg.array[regv+i] = LNil
		} else {
			rg.array[regv+i] = rg.array[tidx]
		}
	}
	rg.top = regv + n
} // +inline-end

func (rg *registry) FillNil(regm, n int) { // +inline-start
	for i := 0; i < n; i++ {
		rg.array[regm+i] = LNil
	}
	rg.top = regm + n
} // +inline-end

func (rg *registry) Insert(value LValue, reg int) {
	top := rg.Top()
	if reg >= top {
		rg.Set(reg, value)
		return
	}
	top--
	for ; top >= reg; top-- {
		rg.Set(top+1, rg.Get(top))
	}
	rg.Set(reg, value)
}

func (rg *registry) Set(reg int, val LValue) {
	rg.array[reg] = val
	if reg >= rg.top {
		rg.top = reg + 1
	}
}

func (rg *registry) SetNumber(reg int, val LNumber) {
	rg.array[reg] = rg.alloc.LNumber2I(val)
	if reg >= rg.top {
		rg.top = reg + 1
	}
} /* }}} */

/* Global {{{ */

func newGlobal() *Global {
	return &Global{
		MainThread: nil,
		Registry:   newLTable(0, 32),
		Global:     newLTable(0, 64),
		builtinMts: make(map[int]LValue),
		tempFiles:  make([]*os.File, 0, 10),
	}
}

/* }}} */

/* package local methods {{{ */

func panicWithTraceback(L *LState) {
	err := newApiError(ApiErrorRun, L.Get(-1))
	err.StackTrace = L.stackTrace(0)
	panic(err)
}

func panicWithoutTraceback(L *LState) {
	err := newApiError(ApiErrorRun, L.Get(-1))
	panic(err)
}

func newLState(options Options) *LState {
	al := newAllocator(32)
	ls := &LState{
		G:       newGlobal(),
		Parent:  nil,
		Panic:   panicWithTraceback,
		Dead:    false,
		Options: options,

		stop:         0,
		reg:          newRegistry(options.RegistrySize, al),
		stack:        newCallFrameStack(options.CallStackSize),
		alloc:        al,
		currentFrame: nil,
		wrapped:      false,
		uvcache:      nil,
		hasErrorFunc: false,
		mainLoop:     mainLoop,
		ctx:          nil,
	}
	ls.Env = ls.G.Global
	return ls
}

func (ls *LState) printReg() {
	println("-------------------------")
	println("thread:", ls)
	println("top:", ls.reg.Top())
	if ls.currentFrame != nil {
		println("function base:", ls.currentFrame.Base)
		println("return base:", ls.currentFrame.ReturnBase)
	} else {
		println("(vm not started)")
	}
	println("local base:", ls.currentLocalBase())
	for i := 0; i < ls.reg.Top(); i++ {
		println(i, ls.reg.Get(i).String())
	}
	println("-------------------------")
}

func (ls *LState) printCallStack() {
	println("-------------------------")
	for i := 0; i < ls.stack.Sp(); i++ {
		print(i)
		print(" ")
		frame := ls.stack.At(i)
		if frame == nil {
			break
		}
		if frame.Fn.IsG {
			println("IsG:", true, "Frame:", frame, "Fn:", frame.Fn)
		} else {
			println("IsG:", false, "Frame:", frame, "Fn:", frame.Fn, "pc:", frame.Pc)
		}
	}
	println("-------------------------")
}

func (ls *LState) closeAllUpvalues() { // +inline-start
	for cf := ls.currentFrame; cf != nil; cf = cf.Parent {
		if !cf.Fn.IsG {
			ls.closeUpvalues(cf.LocalBase)
		}
	}
} // +inline-end

func (ls *LState) raiseError(level int, format string, args ...interface{}) {
	if !ls.hasErrorFunc {
		ls.closeAllUpvalues()
	}
	message := format
	if len(args) > 0 {
		message = fmt.Sprintf(format, args...)
	}
	if level > 0 {
		message = fmt.Sprintf("%v %v", ls.where(level-1, true), message)
	}
	ls.reg.Push(LString(message))
	ls.Panic(ls)
}

func (ls *LState) findLocal(frame *callFrame, no int) string {
	fn := frame.Fn
	if !fn.IsG {
		if name, ok := fn.LocalName(no, frame.Pc-1); ok {
			return name
		}
	}
	var top int
	if ls.currentFrame == frame {
		top = ls.reg.Top()
	} else if frame.Idx+1 < ls.stack.Sp() {
		top = ls.stack.At(frame.Idx + 1).Base
	} else {
		return ""
	}
	if top-frame.LocalBase >= no {
		return "(*temporary)"
	}
	return ""
}

func (ls *LState) where(level int, skipg bool) string {
	dbg, ok := ls.GetStack(level)
	if !ok {
		return ""
	}
	cf := dbg.frame
	proto := cf.Fn.Proto
	sourcename := "[G]"
	if proto != nil {
		sourcename = proto.SourceName
	} else if skipg {
		return ls.where(level+1, skipg)
	}
	line := ""
	if proto != nil {
		line = fmt.Sprintf("%v:", proto.DbgSourcePositions[cf.Pc-1])
	}
	return fmt.Sprintf("%v:%v", sourcename, line)
}

func (ls *LState) stackTrace(level int) string {
	buf := []string{}
	header := "stack traceback:"
	if ls.currentFrame != nil {
		i := 0
		for dbg, ok := ls.GetStack(i); ok; dbg, ok = ls.GetStack(i) {
			cf := dbg.frame
			buf = append(buf, fmt.Sprintf("\t%v in %v", ls.Where(i), ls.formattedFrameFuncName(cf)))
			if !cf.Fn.IsG && cf.TailCall > 0 {
				for tc := cf.TailCall; tc > 0; tc-- {
					buf = append(buf, "\t(tailcall): ?")
					i++
				}
			}
			i++
		}
	}
	buf = append(buf, fmt.Sprintf("\t%v: %v", "[G]", "?"))
	buf = buf[intMax(0, intMin(level, len(buf))):len(buf)]
	if len(buf) > 20 {
		newbuf := make([]string, 0, 20)
		newbuf = append(newbuf, buf[0:7]...)
		newbuf = append(newbuf, "\t...")
		newbuf = append(newbuf, buf[len(buf)-7:len(buf)]...)
		buf = newbuf
	}
	return fmt.Sprintf("%s\n%s", header, strings.Join(buf, "\n"))
}

func (ls *LState) formattedFrameFuncName(fr *callFrame) string {
	name, ischunk := ls.frameFuncName(fr)
	if ischunk {
		return name
	}
	if name[0] != '(' && name[0] != '<' {
		return fmt.Sprintf("function '%s'", name)
	}
	return fmt.Sprintf("function %s", name)
}

func (ls *LState) rawFrameFuncName(fr *callFrame) string {
	name, _ := ls.frameFuncName(fr)
	return name
}

func (ls *LState) frameFuncName(fr *callFrame) (string, bool) {
	frame := fr.Parent
	if frame == nil {
		if ls.Parent == nil {
			return "main chunk", true
		} else {
			return "corountine", true
		}
	}
	if !frame.Fn.IsG {
		pc := frame.Pc - 1
		for _, call := range frame.Fn.Proto.DbgCalls {
			if call.Pc == pc {
				name := call.Name
				if (name == "?" || fr.TailCall > 0) && !fr.Fn.IsG {
					name = fmt.Sprintf("<%v:%v>", fr.Fn.Proto.SourceName, fr.Fn.Proto.LineDefined)
				}
				return name, false
			}
		}
	}
	if !fr.Fn.IsG {
		return fmt.Sprintf("<%v:%v>", fr.Fn.Proto.SourceName, fr.Fn.Proto.LineDefined), false
	}
	return "(anonymous)", false
}

func (ls *LState) isStarted() bool {
	return ls.currentFrame != nil
}

func (ls *LState) kill() {
	ls.Dead = true
}

func (ls *LState) indexToReg(idx int) int {
	base := ls.currentLocalBase()
	if idx > 0 {
		return base + idx - 1
	} else if idx == 0 {
		return -1
	} else {
		tidx := ls.reg.Top() + idx
		if tidx < base {
			return -1
		}
		return tidx
	}
}

func (ls *LState) currentLocalBase() int {
	base := 0
	if ls.currentFrame != nil {
		base = ls.currentFrame.LocalBase
	}
	return base
}

func (ls *LState) currentEnv() *LTable {
	return ls.Env
	/*
		if ls.currentFrame == nil {
			return ls.Env
		}
		return ls.currentFrame.Fn.Env
	*/
}

func (ls *LState) rkValue(idx int) LValue {
	/*
		if OpIsK(idx) {
			return ls.currentFrame.Fn.Proto.Constants[opIndexK(idx)]
		}
		return ls.reg.Get(ls.currentFrame.LocalBase + idx)
	*/
	if (idx & opBitRk) != 0 {
		return ls.currentFrame.Fn.Proto.Constants[idx & ^opBitRk]
	}
	return ls.reg.array[ls.currentFrame.LocalBase+idx]
}

func (ls *LState) rkString(idx int) string {
	if (idx & opBitRk) != 0 {
		return ls.currentFrame.Fn.Proto.stringConstants[idx & ^opBitRk]
	}
	return string(ls.reg.array[ls.currentFrame.LocalBase+idx].(LString))
}

func (ls *LState) closeUpvalues(idx int) { // +inline-start
	if ls.uvcache != nil {
		var prev *Upvalue
		for uv := ls.uvcache; uv != nil; uv = uv.next {
			if uv.index >= idx {
				if prev != nil {
					prev.next = nil
				} else {
					ls.uvcache = nil
				}
				uv.Close()
			}
			prev = uv
		}
	}
} // +inline-end

func (ls *LState) findUpvalue(idx int) *Upvalue {
	var prev *Upvalue
	var next *Upvalue
	if ls.uvcache != nil {
		for uv := ls.uvcache; uv != nil; uv = uv.next {
			if uv.index == idx {
				return uv
			}
			if uv.index > idx {
				next = uv
				break
			}
			prev = uv
		}
	}
	uv := &Upvalue{reg: ls.reg, index: idx, closed: false}
	if prev != nil {
		prev.next = uv
	} else {
		ls.uvcache = uv
	}
	if next != nil {
		uv.next = next
	}
	return uv
}

func (ls *LState) metatable(lvalue LValue, rawget bool) LValue {
	var metatable LValue = LNil
	switch obj := lvalue.(type) {
	case *LTable:
		metatable = obj.Metatable
	case *LUserData:
		metatable = obj.Metatable
	default:
		if table, ok := ls.G.builtinMts[int(obj.Type())]; ok {
			metatable = table
		}
	}

	if !rawget && metatable != LNil {
		oldmt := metatable
		if tb, ok := metatable.(*LTable); ok {
			metatable = tb.RawGetString("__metatable")
			if metatable == LNil {
				metatable = oldmt
			}
		}
	}

	return metatable
}

func (ls *LState) metaOp1(lvalue LValue, event string) LValue {
	if mt := ls.metatable(lvalue, true); mt != LNil {
		if tb, ok := mt.(*LTable); ok {
			return tb.RawGetString(event)
		}
	}
	return LNil
}

func (ls *LState) metaOp2(value1, value2 LValue, event string) LValue {
	if mt := ls.metatable(value1, true); mt != LNil {
		if tb, ok := mt.(*LTable); ok {
			if ret := tb.RawGetString(event); ret != LNil {
				return ret
			}
		}
	}
	if mt := ls.metatable(value2, true); mt != LNil {
		if tb, ok := mt.(*LTable); ok {
			return tb.RawGetString(event)
		}
	}
	return LNil
}

func (ls *LState) metaCall(lvalue LValue) (*LFunction, bool) {
	if fn, ok := lvalue.(*LFunction); ok {
		return fn, false
	}
	if fn, ok := ls.metaOp1(lvalue, "__call").(*LFunction); ok {
		return fn, true
	}
	return nil, false
}

func (ls *LState) initCallFrame(cf *callFrame) { // +inline-start
	if cf.Fn.IsG {
		ls.reg.SetTop(cf.LocalBase + cf.NArgs)
	} else {
		proto := cf.Fn.Proto
		nargs := cf.NArgs
		np := int(proto.NumParameters)
		for i := nargs; i < np; i++ {
			ls.reg.array[cf.LocalBase+i] = LNil
			nargs = np
		}

		if (proto.IsVarArg & VarArgIsVarArg) == 0 {
			if nargs < int(proto.NumUsedRegisters) {
				nargs = int(proto.NumUsedRegisters)
			}
			for i := np; i < nargs; i++ {
				ls.reg.array[cf.LocalBase+i] = LNil
			}
			ls.reg.top = cf.LocalBase + int(proto.NumUsedRegisters)
		} else {
			/* swap vararg positions:
					   closure
					   namedparam1 <- lbase
					   namedparam2
					   vararg1
					   vararg2

			           TO

					   closure
					   nil
					   nil
					   vararg1
					   vararg2
					   namedparam1 <- lbase
					   namedparam2
			*/
			nvarargs := nargs - np
			if nvarargs < 0 {
				nvarargs = 0
			}

			ls.reg.SetTop(cf.LocalBase + nargs + np)
			for i := 0; i < np; i++ {
				//ls.reg.Set(cf.LocalBase+nargs+i, ls.reg.Get(cf.LocalBase+i))
				ls.reg.array[cf.LocalBase+nargs+i] = ls.reg.array[cf.LocalBase+i]
				//ls.reg.Set(cf.LocalBase+i, LNil)
				ls.reg.array[cf.LocalBase+i] = LNil
			}

			if CompatVarArg {
				ls.reg.SetTop(cf.LocalBase + nargs + np + 1)
				if (proto.IsVarArg & VarArgNeedsArg) != 0 {
					argtb := newLTable(nvarargs, 0)
					for i := 0; i < nvarargs; i++ {
						argtb.RawSetInt(i+1, ls.reg.Get(cf.LocalBase+np+i))
					}
					argtb.RawSetString("n", LNumber(nvarargs))
					//ls.reg.Set(cf.LocalBase+nargs+np, argtb)
					ls.reg.array[cf.LocalBase+nargs+np] = argtb
				} else {
					ls.reg.array[cf.LocalBase+nargs+np] = LNil
				}
			}
			cf.LocalBase += nargs
			maxreg := cf.LocalBase + int(proto.NumUsedRegisters)
			ls.reg.SetTop(maxreg)
		}
	}
} // +inline-end

func (ls *LState) pushCallFrame(cf callFrame, fn LValue, meta bool) { // +inline-start
	if meta {
		cf.NArgs++
		ls.reg.Insert(fn, cf.LocalBase)
	}
	if cf.Fn == nil {
		ls.RaiseError("attempt to call a non-function object")
	}
	if ls.stack.sp == ls.Options.CallStackSize {
		ls.RaiseError("stack overflow")
	}
	// this section is inlined by go-inline
	// source function is 'func (cs *callFrameStack) Push(v callFrame) ' in '_state.go'
	{
		cs := ls.stack
		v := cf
		cs.array[cs.sp] = v
		cs.array[cs.sp].Idx = cs.sp
		cs.sp++
	}
	newcf := ls.stack.Last()
	// this section is inlined by go-inline
	// source function is 'func (ls *LState) initCallFrame(cf *callFrame) ' in '_state.go'
	{
		cf := newcf
		if cf.Fn.IsG {
			ls.reg.SetTop(cf.LocalBase + cf.NArgs)
		} else {
			proto := cf.Fn.Proto
			nargs := cf.NArgs
			np := int(proto.NumParameters)
			for i := nargs; i < np; i++ {
				ls.reg.array[cf.LocalBase+i] = LNil
				nargs = np
			}

			if (proto.IsVarArg & VarArgIsVarArg) == 0 {
				if nargs < int(proto.NumUsedRegisters) {
					nargs = int(proto.NumUsedRegisters)
				}
				for i := np; i < nargs; i++ {
					ls.reg.array[cf.LocalBase+i] = LNil
				}
				ls.reg.top = cf.LocalBase + int(proto.NumUsedRegisters)
			} else {
				/* swap vararg positions:
						   closure
						   namedparam1 <- lbase
						   namedparam2
						   vararg1
						   vararg2

				           TO

						   closure
						   nil
						   nil
						   vararg1
						   vararg2
						   namedparam1 <- lbase
						   namedparam2
				*/
				nvarargs := nargs - np
				if nvarargs < 0 {
					nvarargs = 0
				}

				ls.reg.SetTop(cf.LocalBase + nargs + np)
				for i := 0; i < np; i++ {
					//ls.reg.Set(cf.LocalBase+nargs+i, ls.reg.Get(cf.LocalBase+i))
					ls.reg.array[cf.LocalBase+nargs+i] = ls.reg.array[cf.LocalBase+i]
					//ls.reg.Set(cf.LocalBase+i, LNil)
					ls.reg.array[cf.LocalBase+i] = LNil
				}

				if CompatVarArg {
					ls.reg.SetTop(cf.LocalBase + nargs + np + 1)
					if (proto.IsVarArg & VarArgNeedsArg) != 0 {
						argtb := newLTable(nvarargs, 0)
						for i := 0; i < nvarargs; i++ {
							argtb.RawSetInt(i+1, ls.reg.Get(cf.LocalBase+np+i))
						}
						argtb.RawSetString("n", LNumber(nvarargs))
						//ls.reg.Set(cf.LocalBase+nargs+np, argtb)
						ls.reg.array[cf.LocalBase+nargs+np] = argtb
					} else {
						ls.reg.array[cf.LocalBase+nargs+np] = LNil
					}
				}
				cf.LocalBase += nargs
				maxreg := cf.LocalBase + int(proto.NumUsedRegisters)
				ls.reg.SetTop(maxreg)
			}
		}
	}
	ls.currentFrame = newcf
} // +inline-end

func (ls *LState) callR(nargs, nret, rbase int) {
	base := ls.reg.Top() - nargs - 1
	if rbase < 0 {
		rbase = base
	}
	lv := ls.reg.Get(base)
	fn, meta := ls.metaCall(lv)
	ls.pushCallFrame(callFrame{
		Fn:         fn,
		Pc:         0,
		Base:       base,
		LocalBase:  base + 1,
		ReturnBase: rbase,
		NArgs:      nargs,
		NRet:       nret,
		Parent:     ls.currentFrame,
		TailCall:   0,
	}, lv, meta)
	if ls.G.MainThread == nil {
		ls.G.MainThread = ls
		ls.G.CurrentThread = ls
		ls.mainLoop(ls, nil)
	} else {
		ls.mainLoop(ls, ls.currentFrame)
	}
	if nret != MultRet {
		ls.reg.SetTop(rbase + nret)
	}
}

func (ls *LState) getField(obj LValue, key LValue) LValue {
	curobj := obj
	for i := 0; i < MaxTableGetLoop; i++ {
		tb, istable := curobj.(*LTable)
		if istable {
			ret := tb.RawGet(key)
			if ret != LNil {
				return ret
			}
		}
		metaindex := ls.metaOp1(curobj, "__index")
		if metaindex == LNil {
			if !istable {
				ls.RaiseError("attempt to index a non-table object(%v)", curobj.Type().String())
			}
			return LNil
		}
		if metaindex.Type() == LTFunction {
			ls.reg.Push(metaindex)
			ls.reg.Push(curobj)
			ls.reg.Push(key)
			ls.Call(2, 1)
			return ls.reg.Pop()
		} else {
			curobj = metaindex
		}
	}
	ls.RaiseError("too many recursions in gettable")
	return nil
}

func (ls *LState) getFieldString(obj LValue, key string) LValue {
	curobj := obj
	for i := 0; i < MaxTableGetLoop; i++ {
		tb, istable := curobj.(*LTable)
		if istable {
			ret := tb.RawGetString(key)
			if ret != LNil {
				return ret
			}
		}
		metaindex := ls.metaOp1(curobj, "__index")
		if metaindex == LNil {
			if !istable {
				ls.RaiseError("attempt to index a non-table object(%v)", curobj.Type().String())
			}
			return LNil
		}
		if metaindex.Type() == LTFunction {
			ls.reg.Push(metaindex)
			ls.reg.Push(curobj)
			ls.reg.Push(LString(key))
			ls.Call(2, 1)
			return ls.reg.Pop()
		} else {
			curobj = metaindex
		}
	}
	ls.RaiseError("too many recursions in gettable")
	return nil
}

func (ls *LState) setField(obj LValue, key LValue, value LValue) {
	curobj := obj
	for i := 0; i < MaxTableGetLoop; i++ {
		tb, istable := curobj.(*LTable)
		if istable {
			if tb.RawGet(key) != LNil {
				ls.RawSet(tb, key, value)
				return
			}
		}
		metaindex := ls.metaOp1(curobj, "__newindex")
		if metaindex == LNil {
			if !istable {
				ls.RaiseError("attempt to index a non-table object(%v)", curobj.Type().String())
			}
			ls.RawSet(tb, key, value)
			return
		}
		if metaindex.Type() == LTFunction {
			ls.reg.Push(metaindex)
			ls.reg.Push(curobj)
			ls.reg.Push(key)
			ls.reg.Push(value)
			ls.Call(3, 0)
			return
		} else {
			curobj = metaindex
		}
	}
	ls.RaiseError("too many recursions in settable")
}

func (ls *LState) setFieldString(obj LValue, key string, value LValue) {
	curobj := obj
	for i := 0; i < MaxTableGetLoop; i++ {
		tb, istable := curobj.(*LTable)
		if istable {
			if tb.RawGetString(key) != LNil {
				tb.RawSetString(key, value)
				return
			}
		}
		metaindex := ls.metaOp1(curobj, "__newindex")
		if metaindex == LNil {
			if !istable {
				ls.RaiseError("attempt to index a non-table object(%v)", curobj.Type().String())
			}
			tb.RawSetString(key, value)
			return
		}
		if metaindex.Type() == LTFunction {
			ls.reg.Push(metaindex)
			ls.reg.Push(curobj)
			ls.reg.Push(LString(key))
			ls.reg.Push(value)
			ls.Call(3, 0)
			return
		} else {
			curobj = metaindex
		}
	}
	ls.RaiseError("too many recursions in settable")
}

/* }}} */

/* api methods {{{ */

func NewState(opts ...Options) *LState {
	var ls *LState
	if len(opts) == 0 {
		ls = newLState(Options{
			CallStackSize: CallStackSize,
			RegistrySize:  RegistrySize,
		})
		ls.OpenLibs()
	} else {
		if opts[0].CallStackSize < 1 {
			opts[0].CallStackSize = CallStackSize
		}
		if opts[0].RegistrySize < 128 {
			opts[0].RegistrySize = RegistrySize
		}
		ls = newLState(opts[0])
		if !opts[0].SkipOpenLibs {
			ls.OpenLibs()
		}
	}
	return ls
}

func (ls *LState) Close() {
	atomic.AddInt32(&ls.stop, 1)
	for _, file := range ls.G.tempFiles {
		// ignore errors in these operations
		file.Close()
		os.Remove(file.Name())
	}
}

/* registry operations {{{ */

func (ls *LState) GetTop() int {
	return ls.reg.Top() - ls.currentLocalBase()
}

func (ls *LState) SetTop(idx int) {
	base := ls.currentLocalBase()
	newtop := ls.indexToReg(idx) + 1
	if newtop < base {
		ls.reg.SetTop(base)
	} else {
		ls.reg.SetTop(newtop)
	}
}

func (ls *LState) Replace(idx int, value LValue) {
	base := ls.currentLocalBase()
	if idx > 0 {
		reg := base + idx - 1
		if reg < ls.reg.Top() {
			ls.reg.Set(reg, value)
		}
	} else if idx == 0 {
	} else if idx > RegistryIndex {
		if tidx := ls.reg.Top() + idx; tidx >= base {
			ls.reg.Set(tidx, value)
		}
	} else {
		switch idx {
		case RegistryIndex:
			if tb, ok := value.(*LTable); ok {
				ls.G.Registry = tb
			} else {
				ls.RaiseError("registry must be a table(%v)", value.Type().String())
			}
		case EnvironIndex:
			if ls.currentFrame == nil {
				ls.RaiseError("no calling environment")
			}
			if tb, ok := value.(*LTable); ok {
				ls.currentFrame.Fn.Env = tb
			} else {
				ls.RaiseError("environment must be a table(%v)", value.Type().String())
			}
		case GlobalsIndex:
			if tb, ok := value.(*LTable); ok {
				ls.G.Global = tb
			} else {
				ls.RaiseError("_G must be a table(%v)", value.Type().String())
			}
		default:
			fn := ls.currentFrame.Fn
			index := GlobalsIndex - idx - 1
			if index < len(fn.Upvalues) {
				fn.Upvalues[index].SetValue(value)
			}
		}
	}
}

func (ls *LState) Get(idx int) LValue {
	base := ls.currentLocalBase()
	if idx > 0 {
		reg := base + idx - 1
		if reg < ls.reg.Top() {
			return ls.reg.Get(reg)
		}
		return LNil
	} else if idx == 0 {
		return LNil
	} else if idx > RegistryIndex {
		tidx := ls.reg.Top() + idx
		if tidx < base {
			return LNil
		}
		return ls.reg.Get(tidx)
	} else {
		switch idx {
		case RegistryIndex:
			return ls.G.Registry
		case EnvironIndex:
			if ls.currentFrame == nil {
				return ls.Env
			}
			return ls.currentFrame.Fn.Env
		case GlobalsIndex:
			return ls.G.Global
		default:
			fn := ls.currentFrame.Fn
			index := GlobalsIndex - idx - 1
			if index < len(fn.Upvalues) {
				return fn.Upvalues[index].Value()
			}
			return LNil
		}
	}
	return LNil
}

func (ls *LState) Push(value LValue) {
	ls.reg.Push(value)
}

func (ls *LState) Pop(n int) {
	for i := 0; i < n; i++ {
		if ls.GetTop() == 0 {
			ls.RaiseError("register underflow")
		}
		ls.reg.Pop()
	}
}

func (ls *LState) Insert(value LValue, index int) {
	reg := ls.indexToReg(index)
	top := ls.reg.Top()
	if reg >= top {
		ls.reg.Set(reg, value)
		return
	}
	if reg <= ls.currentLocalBase() {
		reg = ls.currentLocalBase()
	}
	top--
	for ; top >= reg; top-- {
		ls.reg.Set(top+1, ls.reg.Get(top))
	}
	ls.reg.Set(reg, value)
}

func (ls *LState) Remove(index int) {
	reg := ls.indexToReg(index)
	top := ls.reg.Top()
	switch {
	case reg >= top:
		return
	case reg < ls.currentLocalBase():
		return
	case reg == top-1:
		ls.Pop(1)
		return
	}
	for i := reg; i < top-1; i++ {
		ls.reg.Set(i, ls.reg.Get(i+1))
	}
	ls.reg.SetTop(top - 1)
}

/* }}} */

/* object allocation {{{ */

func (ls *LState) NewTable() *LTable {
	return newLTable(defaultArrayCap, defaultHashCap)
}

func (ls *LState) CreateTable(acap, hcap int) *LTable {
	return newLTable(acap, hcap)
}

// NewThread returns a new LState that shares with the original state all global objects.
// If the original state has context.Context, the new state has a new child context of the original state and this function returns its cancel function.
func (ls *LState) NewThread() (*LState, context.CancelFunc) {
	thread := newLState(ls.Options)
	thread.G = ls.G
	thread.Env = ls.Env
	var f context.CancelFunc = nil
	if ls.ctx != nil {
		thread.mainLoop = mainLoopWithContext
		thread.ctx, f = context.WithCancel(ls.ctx)
	}
	return thread, f
}

func (ls *LState) NewUserData() *LUserData {
	return &LUserData{
		Env:       ls.currentEnv(),
		Metatable: LNil,
	}
}

func (ls *LState) NewFunction(fn LGFunction) *LFunction {
	return newLFunctionG(fn, ls.currentEnv(), 0)
}

func (ls *LState) NewClosure(fn LGFunction, upvalues ...LValue) *LFunction {
	cl := newLFunctionG(fn, ls.currentEnv(), len(upvalues))
	for i, lv := range upvalues {
		cl.Upvalues[i] = &Upvalue{}
		cl.Upvalues[i].Close()
		cl.Upvalues[i].SetValue(lv)
	}
	return cl
}

/* }}} */

/* toType {{{ */

func (ls *LState) ToBool(n int) bool {
	return LVAsBool(ls.Get(n))
}

func (ls *LState) ToInt(n int) int {
	if lv, ok := ls.Get(n).(LNumber); ok {
		return int(lv)
	}
	if lv, ok := ls.Get(n).(LString); ok {
		if num, err := parseNumber(string(lv)); err == nil {
			return int(num)
		}
	}
	return 0
}

func (ls *LState) ToInt64(n int) int64 {
	if lv, ok := ls.Get(n).(LNumber); ok {
		return int64(lv)
	}
	if lv, ok := ls.Get(n).(LString); ok {
		if num, err := parseNumber(string(lv)); err == nil {
			return int64(num)
		}
	}
	return 0
}

func (ls *LState) ToNumber(n int) LNumber {
	return LVAsNumber(ls.Get(n))
}

func (ls *LState) ToString(n int) string {
	return LVAsString(ls.Get(n))
}

func (ls *LState) ToTable(n int) *LTable {
	if lv, ok := ls.Get(n).(*LTable); ok {
		return lv
	}
	return nil
}

func (ls *LState) ToFunction(n int) *LFunction {
	if lv, ok := ls.Get(n).(*LFunction); ok {
		return lv
	}
	return nil
}

func (ls *LState) ToUserData(n int) *LUserData {
	if lv, ok := ls.Get(n).(*LUserData); ok {
		return lv
	}
	return nil
}

func (ls *LState) ToThread(n int) *LState {
	if lv, ok := ls.Get(n).(*LState); ok {
		return lv
	}
	return nil
}

/* }}} */

/* error & debug operations {{{ */

// This function is equivalent to luaL_error( http://www.lua.org/manual/5.1/manual.html#luaL_error ).
func (ls *LState) RaiseError(format string, args ...interface{}) {
	ls.raiseError(1, format, args...)
}

// This function is equivalent to lua_error( http://www.lua.org/manual/5.1/manual.html#lua_error ).
func (ls *LState) Error(lv LValue, level int) {
	if str, ok := lv.(LString); ok {
		ls.raiseError(level, string(str))
	} else {
		if !ls.hasErrorFunc {
			ls.closeAllUpvalues()
		}
		ls.Push(lv)
		ls.Panic(ls)
	}
}

func (ls *LState) GetInfo(what string, dbg *Debug, fn LValue) (LValue, error) {
	if !strings.HasPrefix(what, ">") {
		fn = dbg.frame.Fn
	} else {
		what = what[1:]
	}
	f, ok := fn.(*LFunction)
	if !ok {
		return LNil, newApiErrorS(ApiErrorRun, "can not get debug info(an object in not a function)")
	}

	retfn := false
	for _, c := range what {
		switch c {
		case 'f':
			retfn = true
		case 'S':
			if dbg.frame != nil && dbg.frame.Parent == nil {
				dbg.What = "main"
			} else if f.IsG {
				dbg.What = "G"
			} else if dbg.frame != nil && dbg.frame.TailCall > 0 {
				dbg.What = "tail"
			} else {
				dbg.What = "Lua"
			}
			if !f.IsG {
				dbg.Source = f.Proto.SourceName
				dbg.LineDefined = f.Proto.LineDefined
				dbg.LastLineDefined = f.Proto.LastLineDefined
			}
		case 'l':
			if !f.IsG && dbg.frame != nil {
				if dbg.frame.Pc > 0 {
					dbg.CurrentLine = f.Proto.DbgSourcePositions[dbg.frame.Pc-1]
				}
			} else {
				dbg.CurrentLine = -1
			}
		case 'u':
			dbg.NUpvalues = len(f.Upvalues)
		case 'n':
			if dbg.frame != nil {
				dbg.Name = ls.rawFrameFuncName(dbg.frame)
			}
		default:
			return LNil, newApiErrorS(ApiErrorRun, "invalid what: "+string(c))
		}
	}

	if retfn {
		return f, nil
	}
	return LNil, nil

}

func (ls *LState) GetStack(level int) (*Debug, bool) {
	frame := ls.currentFrame
	for ; level > 0 && frame != nil; frame = frame.Parent {
		level--
		if !frame.Fn.IsG {
			level -= frame.TailCall
		}
	}

	if level == 0 && frame != nil {
		return &Debug{frame: frame}, true
	} else if level < 0 && ls.stack.Sp() > 0 {
		return &Debug{frame: ls.stack.At(0)}, true
	}
	return &Debug{}, false
}

func (ls *LState) GetLocal(dbg *Debug, no int) (string, LValue) {
	frame := dbg.frame
	if name := ls.findLocal(frame, no); len(name) > 0 {
		return name, ls.reg.Get(frame.LocalBase + no - 1)
	}
	return "", LNil
}

func (ls *LState) SetLocal(dbg *Debug, no int, lv LValue) string {
	frame := dbg.frame
	if name := ls.findLocal(frame, no); len(name) > 0 {
		ls.reg.Set(frame.LocalBase+no-1, lv)
		return name
	}
	return ""
}

func (ls *LState) GetUpvalue(fn *LFunction, no int) (string, LValue) {
	if fn.IsG {
		return "", LNil
	}

	no--
	if no >= 0 && no < len(fn.Upvalues) {
		return fn.Proto.DbgUpvalues[no], fn.Upvalues[no].Value()
	}
	return "", LNil
}

func (ls *LState) SetUpvalue(fn *LFunction, no int, lv LValue) string {
	if fn.IsG {
		return ""
	}

	no--
	if no >= 0 && no < len(fn.Upvalues) {
		fn.Upvalues[no].SetValue(lv)
		return fn.Proto.DbgUpvalues[no]
	}
	return ""
}

/* }}} */

/* env operations {{{ */

func (ls *LState) GetFEnv(obj LValue) LValue {
	switch lv := obj.(type) {
	case *LFunction:
		return lv.Env
	case *LUserData:
		return lv.Env
	case *LState:
		return lv.Env
	}
	return LNil
}

func (ls *LState) SetFEnv(obj LValue, env LValue) {
	tb, ok := env.(*LTable)
	if !ok {
		ls.RaiseError("cannot use %v as an environment", env.Type().String())
	}

	switch lv := obj.(type) {
	case *LFunction:
		lv.Env = tb
	case *LUserData:
		lv.Env = tb
	case *LState:
		lv.Env = tb
	}
	/* do nothing */
}

/* }}} */

/* table operations {{{ */

func (ls *LState) RawGet(tb *LTable, key LValue) LValue {
	return tb.RawGet(key)
}

func (ls *LState) RawGetInt(tb *LTable, key int) LValue {
	return tb.RawGetInt(key)
}

func (ls *LState) GetField(obj LValue, skey string) LValue {
	return ls.getFieldString(obj, skey)
}

func (ls *LState) GetTable(obj LValue, key LValue) LValue {
	return ls.getField(obj, key)
}

func (ls *LState) RawSet(tb *LTable, key LValue, value LValue) {
	if n, ok := key.(LNumber); ok && math.IsNaN(float64(n)) {
		ls.RaiseError("table index is NaN")
	} else if key == LNil {
		ls.RaiseError("table index is nil")
	}
	tb.RawSet(key, value)
}

func (ls *LState) RawSetInt(tb *LTable, key int, value LValue) {
	tb.RawSetInt(key, value)
}

func (ls *LState) SetField(obj LValue, key string, value LValue) {
	ls.setFieldString(obj, key, value)
}

func (ls *LState) SetTable(obj LValue, key LValue, value LValue) {
	ls.setField(obj, key, value)
}

func (ls *LState) ForEach(tb *LTable, cb func(LValue, LValue)) {
	tb.ForEach(cb)
}

func (ls *LState) GetGlobal(name string) LValue {
	return ls.GetField(ls.Get(GlobalsIndex), name)
}

func (ls *LState) SetGlobal(name string, value LValue) {
	ls.SetField(ls.Get(GlobalsIndex), name, value)
}

func (ls *LState) Next(tb *LTable, key LValue) (LValue, LValue) {
	return tb.Next(key)
}

/* }}} */

/* unary operations {{{ */

func (ls *LState) ObjLen(v1 LValue) int {
	if v1.Type() == LTString {
		return len(string(v1.(LString)))
	}
	op := ls.metaOp1(v1, "__len")
	if op.Type() == LTFunction {
		ls.Push(op)
		ls.Push(v1)
		ls.Call(1, 1)
		ret := ls.reg.Pop()
		if ret.Type() == LTNumber {
			return int(ret.(LNumber))
		}
	} else if v1.Type() == LTTable {
		return v1.(*LTable).Len()
	}
	return 0
}

/* }}} */

/* binary operations {{{ */

func (ls *LState) Concat(values ...LValue) string {
	top := ls.reg.Top()
	for _, value := range values {
		ls.reg.Push(value)
	}
	ret := stringConcat(ls, len(values), ls.reg.Top()-1)
	ls.reg.SetTop(top)
	return LVAsString(ret)
}

func (ls *LState) LessThan(lhs, rhs LValue) bool {
	return lessThan(ls, lhs, rhs)
}

func (ls *LState) Equal(lhs, rhs LValue) bool {
	return equals(ls, lhs, rhs, false)
}

func (ls *LState) RawEqual(lhs, rhs LValue) bool {
	return equals(ls, lhs, rhs, true)
}

/* }}} */

/* register operations {{{ */

func (ls *LState) Register(name string, fn LGFunction) {
	ls.SetGlobal(name, ls.NewFunction(fn))
}

/* }}} */

/* load and function call operations {{{ */

func (ls *LState) Load(reader io.Reader, name string) (*LFunction, error) {
	chunk, err := parse.Parse(reader, name)
	if err != nil {
		return nil, newApiErrorE(ApiErrorSyntax, err)
	}
	proto, err := Compile(chunk, name)
	if err != nil {
		return nil, newApiErrorE(ApiErrorSyntax, err)
	}
	return newLFunctionL(proto, ls.currentEnv(), 0), nil
}

func (ls *LState) Call(nargs, nret int) {
	ls.callR(nargs, nret, -1)
}

func (ls *LState) PCall(nargs, nret int, errfunc *LFunction) (err error) {
	err = nil
	sp := ls.stack.Sp()
	base := ls.reg.Top() - nargs - 1
	oldpanic := ls.Panic
	ls.Panic = panicWithoutTraceback
	if errfunc != nil {
		ls.hasErrorFunc = true
	}
	defer func() {
		ls.Panic = oldpanic
		ls.hasErrorFunc = false
		rcv := recover()
		if rcv != nil {
			if _, ok := rcv.(*ApiError); !ok {
				err = newApiErrorS(ApiErrorPanic, fmt.Sprint(rcv))
				if ls.Options.IncludeGoStackTrace {
					buf := make([]byte, 4096)
					runtime.Stack(buf, false)
					err.(*ApiError).StackTrace = strings.Trim(string(buf), "\000") + "\n" + ls.stackTrace(0)
				}
			} else {
				err = rcv.(*ApiError)
			}
			if errfunc != nil {
				ls.Push(errfunc)
				ls.Push(err.(*ApiError).Object)
				ls.Panic = panicWithoutTraceback
				defer func() {
					ls.Panic = oldpanic
					rcv := recover()
					if rcv != nil {
						if _, ok := rcv.(*ApiError); !ok {
							err = newApiErrorS(ApiErrorPanic, fmt.Sprint(rcv))
							if ls.Options.IncludeGoStackTrace {
								buf := make([]byte, 4096)
								runtime.Stack(buf, false)
								err.(*ApiError).StackTrace = strings.Trim(string(buf), "\000") + ls.stackTrace(0)
							}
						} else {
							err = rcv.(*ApiError)
							err.(*ApiError).StackTrace = ls.stackTrace(0)
						}
					}
				}()
				ls.Call(1, 1)
				err = newApiError(ApiErrorError, ls.Get(-1))
			} else if len(err.(*ApiError).StackTrace) == 0 {
				err.(*ApiError).StackTrace = ls.stackTrace(0)
			}
			ls.stack.SetSp(sp)
			ls.currentFrame = ls.stack.Last()
			ls.reg.SetTop(base)
		}
		ls.stack.SetSp(sp)
		if sp == 0 {
			ls.currentFrame = nil
		}
	}()

	ls.Call(nargs, nret)

	return
}

func (ls *LState) GPCall(fn LGFunction, data LValue) error {
	ls.Push(newLFunctionG(fn, ls.currentEnv(), 0))
	ls.Push(data)
	return ls.PCall(1, MultRet, nil)
}

func (ls *LState) CallByParam(cp P, args ...LValue) error {
	ls.Push(cp.Fn)
	for _, arg := range args {
		ls.Push(arg)
	}

	if cp.Protect {
		return ls.PCall(len(args), cp.NRet, cp.Handler)
	}
	ls.Call(len(args), cp.NRet)
	return nil
}

/* }}} */

/* metatable operations {{{ */

func (ls *LState) GetMetatable(obj LValue) LValue {
	return ls.metatable(obj, false)
}

func (ls *LState) SetMetatable(obj LValue, mt LValue) {
	switch mt.(type) {
	case *LNilType, *LTable:
	default:
		ls.RaiseError("metatable must be a table or nil, but got %v", mt.Type().String())
	}

	switch v := obj.(type) {
	case *LTable:
		v.Metatable = mt
	case *LUserData:
		v.Metatable = mt
	default:
		ls.G.builtinMts[int(obj.Type())] = mt
	}
}

/* }}} */

/* coroutine operations {{{ */

func (ls *LState) Status(th *LState) string {
	status := "suspended"
	if th.Dead {
		status = "dead"
	} else if ls.G.CurrentThread == th {
		status = "running"
	} else if ls.Parent == th {
		status = "normal"
	}
	return status
}

func (ls *LState) Resume(th *LState, fn *LFunction, args ...LValue) (ResumeState, error, []LValue) {
	isstarted := th.isStarted()
	if !isstarted {
		base := 0
		th.stack.Push(callFrame{
			Fn:         fn,
			Pc:         0,
			Base:       base,
			LocalBase:  base + 1,
			ReturnBase: base,
			NArgs:      0,
			NRet:       MultRet,
			Parent:     nil,
			TailCall:   0,
		})
	}

	if ls.G.CurrentThread == th {
		return ResumeError, newApiErrorS(ApiErrorRun, "can not resume a running thread"), nil
	}
	if th.Dead {
		return ResumeError, newApiErrorS(ApiErrorRun, "can not resume a dead thread"), nil
	}
	th.Parent = ls
	ls.G.CurrentThread = th
	if !isstarted {
		cf := th.stack.Last()
		th.currentFrame = cf
		th.SetTop(0)
		for _, arg := range args {
			th.Push(arg)
		}
		cf.NArgs = len(args)
		th.initCallFrame(cf)
		th.Panic = panicWithoutTraceback
	} else {
		for _, arg := range args {
			th.Push(arg)
		}
	}
	top := ls.GetTop()
	threadRun(th)
	haserror := LVIsFalse(ls.Get(top + 1))
	ret := make([]LValue, 0, ls.GetTop())
	for idx := top + 2; idx <= ls.GetTop(); idx++ {
		ret = append(ret, ls.Get(idx))
	}
	if len(ret) == 0 {
		ret = append(ret, LNil)
	}
	ls.SetTop(top)

	if haserror {
		return ResumeError, newApiError(ApiErrorRun, ret[0]), nil
	} else if th.stack.IsEmpty() {
		return ResumeOK, nil, ret
	}
	return ResumeYield, nil, ret
}

func (ls *LState) Yield(values ...LValue) int {
	ls.SetTop(0)
	for _, lv := range values {
		ls.Push(lv)
	}
	return -1
}

func (ls *LState) XMoveTo(other *LState, n int) {
	if ls == other {
		return
	}
	top := ls.GetTop()
	n = intMin(n, top)
	for i := n; i > 0; i-- {
		other.Push(ls.Get(top - i + 1))
	}
	ls.SetTop(top - n)
}

/* }}} */

/* GopherLua original APIs {{{ */

// Set maximum memory size. This function can only be called from the main thread.
func (ls *LState) SetMx(mx int) {
	if ls.Parent != nil {
		ls.RaiseError("sub threads are not allowed to set a memory limit")
	}
	go func() {
		limit := uint64(mx * 1024 * 1024) //MB
		var s runtime.MemStats
		for ls.stop == 0 {
			runtime.ReadMemStats(&s)
			if s.Alloc >= limit {
				fmt.Println("out of memory")
				os.Exit(3)
			}
			time.Sleep(100 * time.Millisecond)
		}
	}()
}

// SetContext set a context ctx to this LState. The provided ctx must be non-nil.
func (ls *LState) SetContext(ctx context.Context) {
	ls.mainLoop = mainLoopWithContext
	ls.ctx = ctx
}

// Context returns the LState's context. To change the context, use WithContext.
func (ls *LState) Context() context.Context {
	return ls.ctx
}

// RemoveContext removes the context associated with this LState and returns this context.
func (ls *LState) RemoveContext() context.Context {
	oldctx := ls.ctx
	ls.mainLoop = mainLoop
	ls.ctx = nil
	return oldctx
}

// Converts the Lua value at the given acceptable index to the chan LValue.
func (ls *LState) ToChannel(n int) chan LValue {
	if lv, ok := ls.Get(n).(LChannel); ok {
		return (chan LValue)(lv)
	}
	return nil
}

/* }}} */

/* }}} */

//
