ledisdb/vendor/github.com/yuin/gopher-lua/_state.go

1788 lines
37 KiB
Go

package lua
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")
}
// +inline-call ls.stack.Push cf
newcf := ls.stack.Last()
// +inline-call ls.initCallFrame newcf
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
}
/* }}} */
/* }}} */
//