mirror of
https://github.com/vxcontrol/golua.git
synced 2026-08-24 20:31:24 -04:00
1113 lines
26 KiB
Go
1113 lines
26 KiB
Go
// This package provides access to the excellent lua language interpreter from go code.
|
|
//
|
|
// Access to most of the functions in lua.h and lauxlib.h is provided as well as additional convenience functions to publish Go objects and functions to lua code.
|
|
//
|
|
// The documentation of this package is no substitute for the official lua documentation and in many instances methods are described only with the name of their C equivalent
|
|
package lua
|
|
|
|
/*
|
|
#cgo CFLAGS: -I ${SRCDIR}/lua
|
|
#cgo linux,386 LDFLAGS: -L ${SRCDIR}/lib/linux32 -lbundle -lluajit -lm -ldl
|
|
#cgo linux,amd64 LDFLAGS: -L ${SRCDIR}/lib/linux64 -lbundle -lluajit -lm -ldl
|
|
#cgo darwin,386 LDFLAGS: -L ${SRCDIR}/lib/osx32 -lbundle -lluajit
|
|
#cgo darwin,amd64 LDFLAGS: -L ${SRCDIR}/lib/osx64 -lbundle -lluajit
|
|
#cgo windows,386 LDFLAGS: -L ${SRCDIR}/lib/mingw32 -lbundle -lluajit -lmingwex -lmingw32
|
|
#cgo windows,amd64 LDFLAGS: -L ${SRCDIR}/lib/mingw64 -lbundle -lluajit -lmingwex -lmingw32
|
|
|
|
#include <lua.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "golua.h"
|
|
|
|
*/
|
|
import "C"
|
|
import (
|
|
"fmt"
|
|
"unsafe"
|
|
|
|
"github.com/vxcontrol/rmx"
|
|
)
|
|
|
|
type LuaStackEntry struct {
|
|
Name string
|
|
Source string
|
|
ShortSource string
|
|
CurrentLine int
|
|
}
|
|
|
|
func newState(L *C.lua_State) *State {
|
|
newstate := &State{
|
|
s: L,
|
|
r: &rmx.Mutex{},
|
|
Shared: newSharedByAllCoroutines(),
|
|
IsMainCoro: true,
|
|
CmainCo: L,
|
|
AllCoro: make(map[int]*State),
|
|
}
|
|
newstate.MainCo = newstate
|
|
defer newstate.r.Unlock()
|
|
newstate.r.Lock()
|
|
|
|
// only for main states, not additional coroutines:
|
|
registerGoState(newstate) // sets Index
|
|
newstate.Upos = int(C.clua_setgostate(L, C.size_t(newstate.Index)))
|
|
|
|
// assert(Upos == 1)
|
|
if newstate.Upos != 1 {
|
|
panic(fmt.Sprintf("assert violated: we expected newstate.Upos for the main coro to always be at index 1: our code depends on that!"))
|
|
}
|
|
newstate.MainCo.AllCoro[newstate.Upos] = newstate
|
|
C.clua_initstate(L)
|
|
return newstate
|
|
}
|
|
|
|
func (L *State) getFreeIndex() (index uint, ok bool) {
|
|
freelen := len(L.Shared.freeIndices)
|
|
//if there exist entries in the freelist
|
|
if freelen > 0 {
|
|
i := L.Shared.freeIndices[freelen-1] //get index
|
|
L.Shared.freeIndices = L.Shared.freeIndices[0 : freelen-1] //'pop' index from list
|
|
return i, true
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
//returns the registered function id
|
|
func (L *State) register(f interface{}) uint {
|
|
index, ok := L.getFreeIndex()
|
|
if ok {
|
|
// reuse
|
|
L.Shared.registry[index] = f
|
|
return index
|
|
}
|
|
// add a new index
|
|
L.Shared.registry = append(L.Shared.registry, f)
|
|
index = uint(len(L.Shared.registry)) - 1
|
|
return index
|
|
}
|
|
|
|
func (L *State) unregister(fid uint) {
|
|
if (fid < uint(len(L.Shared.registry))) && (L.Shared.registry[fid] != nil) {
|
|
L.Shared.registry[fid] = nil
|
|
L.Shared.freeIndices = append(L.Shared.freeIndices, fid)
|
|
}
|
|
}
|
|
|
|
// Like lua_pushcfunction pushes onto the stack a go function as user data
|
|
func (L *State) PushGoFunction(f LuaGoFunction) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
fid := L.register(f)
|
|
C.clua_pushgofunction(L.s, C.uint(fid))
|
|
}
|
|
|
|
// PushGoClosure pushes a lua.LuaGoFunction to the stack wrapped in a Closure.
|
|
// this permits the go function to reflect lua type 'function' when checking with type()
|
|
// this implements behaviour akin to lua_pushcfunction() in lua C API.
|
|
func (L *State) PushGoClosure(f LuaGoFunction) {
|
|
L.PushGoFunction(f) // leaves Go function userdata on stack
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.clua_pushcallback(L.s) // wraps the userdata object with a closure making it into a function
|
|
}
|
|
|
|
// Sets a metamethod to execute a go function
|
|
//
|
|
// The code:
|
|
//
|
|
// L.LGetMetaTable(tableName)
|
|
// L.SetMetaMethod(methodName, function)
|
|
//
|
|
// is the logical equivalent of:
|
|
//
|
|
// L.LGetMetaTable(tableName)
|
|
// L.PushGoFunction(function)
|
|
// L.SetField(-2, methodName)
|
|
//
|
|
// except this wouldn't work because pushing a go function results in user data not a cfunction
|
|
func (L *State) SetMetaMethod(methodName string, f LuaGoFunction) {
|
|
L.PushGoFunction(f) // leaves Go function userdata on stack
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.clua_pushcallback(L.s) // wraps the userdata object with a closure making it into a function
|
|
L.SetField(-2, methodName)
|
|
}
|
|
|
|
// Pushes a Go struct onto the stack as user data.
|
|
//
|
|
// The user data will be rigged so that lua code can access and change the public members of simple types directly
|
|
func (L *State) PushGoStruct(iface interface{}) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
iid := L.register(iface)
|
|
C.clua_pushgostruct(L.s, C.uint(iid))
|
|
}
|
|
|
|
// Push a pointer onto the stack as user data.
|
|
//
|
|
// This function doesn't save a reference to the interface, it is the responsibility of the caller of this function to insure that the interface outlasts the lifetime of the lua object that this function creates.
|
|
func (L *State) PushLightUserdata(ud *interface{}) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_pushlightuserdata(L.s, unsafe.Pointer(ud))
|
|
}
|
|
|
|
// Creates a new user data object of specified size and returns it
|
|
func (L *State) NewUserdata(size uintptr) unsafe.Pointer {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return unsafe.Pointer(C.lua_newuserdata(L.s, C.size_t(size)))
|
|
}
|
|
|
|
// Sets the AtPanic function, returns the old one
|
|
//
|
|
// BUG(everyone_involved): passing nil causes serious problems
|
|
func (L *State) AtPanic(panicf LuaGoFunction) (oldpanicf LuaGoFunction) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
fid := uint(0)
|
|
if panicf != nil {
|
|
fid = L.register(panicf)
|
|
}
|
|
oldres := interface{}(C.clua_atpanic(L.s, C.uint(fid)))
|
|
switch i := oldres.(type) {
|
|
case C.uint:
|
|
f := L.Shared.registry[uint(i)].(LuaGoFunction)
|
|
//free registry entry
|
|
L.unregister(uint(i))
|
|
return f
|
|
case C.lua_CFunction:
|
|
return func(L1 *State) int {
|
|
defer L1.r.Unlock()
|
|
L1.r.Lock()
|
|
return int(C.clua_callluacfunc(L1.s, i))
|
|
}
|
|
}
|
|
//generally we only get here if the panicf got set to something like nil
|
|
//potentially dangerous because we may silently fail
|
|
return nil
|
|
}
|
|
|
|
func (L *State) pcall(nargs, nresults, errfunc int) int {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return int(C.lua_pcall(L.s, C.int(nargs), C.int(nresults), C.int(errfunc)))
|
|
}
|
|
|
|
func (L *State) callEx(nargs, nresults int, catch bool) (err error) {
|
|
if catch {
|
|
defer func() {
|
|
if err2 := recover(); err2 != nil {
|
|
if _, ok := err2.(error); ok {
|
|
err = err2.(error)
|
|
}
|
|
return
|
|
}
|
|
}()
|
|
}
|
|
|
|
L.GetGlobal(C.GOLUA_DEFAULT_MSGHANDLER)
|
|
// We must record where we put the error handler in the stack otherwise it will be impossible to remove after the pcall when nresults == LUA_MULTRET
|
|
erridx := L.GetTop() - nargs - 1
|
|
L.Insert(erridx)
|
|
r := L.pcall(nargs, nresults, erridx)
|
|
L.Remove(erridx)
|
|
if r != 0 {
|
|
err = &LuaError{r, L.ToString(-1), L.StackTrace()}
|
|
if !catch {
|
|
panic(err)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// lua_call
|
|
func (L *State) Call(nargs, nresults int) (err error) {
|
|
return L.callEx(nargs, nresults, true)
|
|
}
|
|
|
|
// Like lua_call but panics on errors
|
|
func (L *State) MustCall(nargs, nresults int) {
|
|
L.callEx(nargs, nresults, false)
|
|
}
|
|
|
|
// lua_checkstack
|
|
func (L *State) CheckStack(extra int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return C.lua_checkstack(L.s, C.int(extra)) != 0
|
|
}
|
|
|
|
// lua_close
|
|
func (L *State) Close() {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_close(L.s)
|
|
unregisterGoState(L)
|
|
}
|
|
|
|
// lua_concat
|
|
func (L *State) Concat(n int) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_concat(L.s, C.int(n))
|
|
}
|
|
|
|
// lua_createtable
|
|
func (L *State) CreateTable(narr int, nrec int) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_createtable(L.s, C.int(narr), C.int(nrec))
|
|
}
|
|
|
|
// lua_equal
|
|
func (L *State) Equal(index1, index2 int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return C.lua_equal(L.s, C.int(index1), C.int(index2)) == 1
|
|
}
|
|
|
|
// lua_gc
|
|
func (L *State) GC(what, data int) int {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return int(C.lua_gc(L.s, C.int(what), C.int(data)))
|
|
}
|
|
|
|
// lua_getfenv
|
|
func (L *State) GetfEnv(index int) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_getfenv(L.s, C.int(index))
|
|
}
|
|
|
|
// lua_getfield
|
|
func (L *State) GetField(index int, k string) {
|
|
Ck := C.CString(k)
|
|
defer C.free(unsafe.Pointer(Ck))
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_getfield(L.s, C.int(index), Ck)
|
|
}
|
|
|
|
// Pushes on the stack the value of a global variable (lua_getglobal)
|
|
func (L *State) GetGlobal(name string) {
|
|
L.GetField(LUA_GLOBALSINDEX, name)
|
|
}
|
|
|
|
// lua_getmetatable
|
|
func (L *State) GetMetaTable(index int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return C.lua_getmetatable(L.s, C.int(index)) != 0
|
|
}
|
|
|
|
// lua_gettable
|
|
func (L *State) GetTable(index int) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_gettable(L.s, C.int(index))
|
|
}
|
|
|
|
// lua_gettop
|
|
func (L *State) GetTop() int {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return int(C.lua_gettop(L.s))
|
|
}
|
|
|
|
// lua_insert
|
|
func (L *State) Insert(index int) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_insert(L.s, C.int(index))
|
|
}
|
|
|
|
// Returns true if lua_type == LUA_TBOOLEAN
|
|
func (L *State) IsBoolean(index int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return LuaValType(C.lua_type(L.s, C.int(index))) == LUA_TBOOLEAN
|
|
}
|
|
|
|
// Returns true if the value at index is a LuaGoFunction
|
|
func (L *State) IsGoFunction(index int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return C.clua_isgofunction(L.s, C.int(index)) != 0
|
|
}
|
|
|
|
// Returns true if the value at index is user data pushed with PushGoStruct
|
|
func (L *State) IsGoStruct(index int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return C.clua_isgostruct(L.s, C.int(index)) != 0
|
|
}
|
|
|
|
// Returns true if the value at index is user data pushed with PushGoFunction
|
|
func (L *State) IsFunction(index int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return LuaValType(C.lua_type(L.s, C.int(index))) == LUA_TFUNCTION
|
|
}
|
|
|
|
// Returns true if the value at index is light user data
|
|
func (L *State) IsLightUserdata(index int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return LuaValType(C.lua_type(L.s, C.int(index))) == LUA_TLIGHTUSERDATA
|
|
}
|
|
|
|
// lua_isnil
|
|
func (L *State) IsNil(index int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return LuaValType(C.lua_type(L.s, C.int(index))) == LUA_TNIL
|
|
}
|
|
|
|
// lua_isnone
|
|
func (L *State) IsNone(index int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return LuaValType(C.lua_type(L.s, C.int(index))) == LUA_TNONE
|
|
}
|
|
|
|
// lua_isnoneornil
|
|
func (L *State) IsNoneOrNil(index int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return int(C.lua_type(L.s, C.int(index))) <= 0
|
|
}
|
|
|
|
// lua_isnumber
|
|
func (L *State) IsNumber(index int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return C.lua_isnumber(L.s, C.int(index)) == 1
|
|
}
|
|
|
|
// lua_isstring
|
|
func (L *State) IsString(index int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return C.lua_isstring(L.s, C.int(index)) == 1
|
|
}
|
|
|
|
// lua_istable
|
|
func (L *State) IsTable(index int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return LuaValType(C.lua_type(L.s, C.int(index))) == LUA_TTABLE
|
|
}
|
|
|
|
// lua_isthread
|
|
func (L *State) IsThread(index int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return LuaValType(C.lua_type(L.s, C.int(index))) == LUA_TTHREAD
|
|
}
|
|
|
|
// lua_isuserdata
|
|
func (L *State) IsUserdata(index int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return C.lua_isuserdata(L.s, C.int(index)) == 1
|
|
}
|
|
|
|
// lua_lessthan
|
|
func (L *State) LessThan(index1, index2 int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return C.lua_lessthan(L.s, C.int(index1), C.int(index2)) == 1
|
|
}
|
|
|
|
// Creates a new lua interpreter state with the given allocation function
|
|
func NewStateAlloc(f Alloc) *State {
|
|
// jea: ../example/alloc.go panics... hmm.
|
|
ls := C.clua_newstate(unsafe.Pointer(&f))
|
|
return newState(ls)
|
|
}
|
|
|
|
// lua_newtable
|
|
func (L *State) NewTable() {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_createtable(L.s, 0, 0)
|
|
}
|
|
|
|
// lua_newthread
|
|
func (L *State) NewThread() *State {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
s := C.lua_newthread(L.s)
|
|
return L.ToThreadHelper(s)
|
|
}
|
|
|
|
// lua_next
|
|
func (L *State) Next(index int) int {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return int(C.lua_next(L.s, C.int(index)))
|
|
}
|
|
|
|
// lua_objlen
|
|
//
|
|
// Returns the "length" of the value at the
|
|
// given acceptable index: for strings, this
|
|
// is the string length; for tables, this
|
|
// is the result of the length operator ('#');
|
|
// for userdata, this is the size of the block
|
|
// of memory allocated for the userdata;
|
|
// for other values, it is 0.
|
|
// jea note: Despite the misleading description
|
|
// above, in 5.1 or LuaJit, ObjLen does not call
|
|
// the metamethod __len(). In 5.2 it was split
|
|
// into len and rawlen, with len calling the __len
|
|
// metamethod.
|
|
//
|
|
func (L *State) ObjLen(index int) uint {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return uint(C.lua_objlen(L.s, C.int(index)))
|
|
}
|
|
|
|
// lua_pop
|
|
func (L *State) Pop(n int) {
|
|
//Why is this implemented this way? I don't get it... maybe it
|
|
// is just inlining manually the actual implementation.
|
|
//C.lua_pop(L.s, C.int(n))
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_settop(L.s, C.int(-n-1))
|
|
}
|
|
|
|
// lua_pushboolean
|
|
func (L *State) PushBoolean(b bool) {
|
|
var bint int
|
|
if b {
|
|
bint = 1
|
|
} else {
|
|
bint = 0
|
|
}
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_pushboolean(L.s, C.int(bint))
|
|
}
|
|
|
|
// lua_pushstring
|
|
func (L *State) PushString(str string) {
|
|
Cstr := C.CString(str)
|
|
defer C.free(unsafe.Pointer(Cstr))
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_pushlstring(L.s, Cstr, C.size_t(len(str)))
|
|
}
|
|
|
|
func (L *State) PushBytes(b []byte) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_pushlstring(L.s, (*C.char)(unsafe.Pointer(&b[0])), C.size_t(len(b)))
|
|
}
|
|
|
|
// lua_pushinteger
|
|
func (L *State) PushInteger(n int64) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_pushinteger(L.s, C.lua_Integer(n))
|
|
}
|
|
|
|
// lua_pushnil
|
|
func (L *State) PushNil() {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_pushnil(L.s)
|
|
}
|
|
|
|
// lua_pushnumber
|
|
func (L *State) PushNumber(n float64) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_pushnumber(L.s, C.lua_Number(n)) // lua_Number is a cast
|
|
}
|
|
|
|
// lua_pushthread
|
|
func (L *State) PushThread() (isMain bool) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return C.lua_pushthread(L.s) != 0
|
|
}
|
|
|
|
// lua_pushvalue
|
|
func (L *State) PushValue(index int) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_pushvalue(L.s, C.int(index))
|
|
}
|
|
|
|
// lua_rawequal
|
|
func (L *State) RawEqual(index1 int, index2 int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return C.lua_rawequal(L.s, C.int(index1), C.int(index2)) != 0
|
|
}
|
|
|
|
// lua_rawget
|
|
func (L *State) RawGet(index int) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_rawget(L.s, C.int(index))
|
|
}
|
|
|
|
// lua_rawgeti
|
|
func (L *State) RawGeti(index int, n int) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_rawgeti(L.s, C.int(index), C.int(n))
|
|
}
|
|
|
|
// lua_rawset
|
|
func (L *State) RawSet(index int) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_rawset(L.s, C.int(index))
|
|
}
|
|
|
|
// lua_rawseti
|
|
func (L *State) RawSeti(index int, n int) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_rawseti(L.s, C.int(index), C.int(n))
|
|
}
|
|
|
|
// Registers a Go function as a global variable
|
|
func (L *State) Register(name string, f LuaGoFunction) {
|
|
L.PushGoFunction(f)
|
|
L.SetGlobal(name)
|
|
}
|
|
|
|
// lua_remove
|
|
func (L *State) Remove(index int) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_remove(L.s, C.int(index))
|
|
}
|
|
|
|
// lua_replace
|
|
func (L *State) Replace(index int) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_replace(L.s, C.int(index))
|
|
}
|
|
|
|
// lua_resume
|
|
func (L *State) Resume(narg int) int {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return int(C.lua_resume(L.s, C.int(narg)))
|
|
}
|
|
|
|
// lua_setallocf
|
|
func (L *State) SetAllocf(f Alloc) {
|
|
C.clua_setallocf(L.s, unsafe.Pointer(&f))
|
|
}
|
|
|
|
// lua_setfenv
|
|
func (L *State) SetfEnv(index int) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_setfenv(L.s, C.int(index))
|
|
}
|
|
|
|
// lua_setfield
|
|
func (L *State) SetField(index int, k string) {
|
|
Ck := C.CString(k)
|
|
defer C.free(unsafe.Pointer(Ck))
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_setfield(L.s, C.int(index), Ck)
|
|
}
|
|
|
|
// lua_setglobal
|
|
func (L *State) SetGlobal(name string) {
|
|
Cname := C.CString(name)
|
|
defer C.free(unsafe.Pointer(Cname))
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_setfield(L.s, C.int(LUA_GLOBALSINDEX), Cname)
|
|
}
|
|
|
|
// lua_setmetatable
|
|
func (L *State) SetMetaTable(index int) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_setmetatable(L.s, C.int(index))
|
|
}
|
|
|
|
// lua_settable
|
|
func (L *State) SetTable(index int) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_settable(L.s, C.int(index))
|
|
}
|
|
|
|
// lua_settop
|
|
func (L *State) SetTop(index int) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.lua_settop(L.s, C.int(index))
|
|
}
|
|
|
|
// lua_status
|
|
func (L *State) Status() int {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return int(C.lua_status(L.s))
|
|
}
|
|
|
|
// lua_toboolean
|
|
func (L *State) ToBoolean(index int) bool {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return C.lua_toboolean(L.s, C.int(index)) != 0
|
|
}
|
|
|
|
// Returns the value at index as a Go function (it must be something pushed with PushGoFunction)
|
|
func (L *State) ToGoFunction(index int) (f LuaGoFunction) {
|
|
if !L.IsGoFunction(index) {
|
|
return nil
|
|
}
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
fid := C.clua_togofunction(L.s, C.int(index))
|
|
if fid < 0 {
|
|
return nil
|
|
}
|
|
return L.Shared.registry[fid].(LuaGoFunction)
|
|
}
|
|
|
|
// Returns the value at index as a Go Struct (it must be something pushed with PushGoStruct)
|
|
func (L *State) ToGoStruct(index int) (f interface{}) {
|
|
if !L.IsGoStruct(index) {
|
|
return nil
|
|
}
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
fid := C.clua_togostruct(L.s, C.int(index))
|
|
if fid < 0 {
|
|
return nil
|
|
}
|
|
return L.Shared.registry[fid]
|
|
}
|
|
|
|
// lua_tostring
|
|
func (L *State) ToString(index int) string {
|
|
var size C.size_t
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
r := C.lua_tolstring(L.s, C.int(index), &size)
|
|
return C.GoStringN(r, C.int(size))
|
|
}
|
|
|
|
func (L *State) ToBytes(index int) []byte {
|
|
var size C.size_t
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
b := C.lua_tolstring(L.s, C.int(index), &size)
|
|
return C.GoBytes(unsafe.Pointer(b), C.int(size))
|
|
}
|
|
|
|
// lua_tointeger
|
|
func (L *State) ToInteger(index int) int {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return int(C.lua_tointeger(L.s, C.int(index)))
|
|
}
|
|
|
|
// lua_tointeger
|
|
func (L *State) ToInteger32(index int) int32 {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return int32(C.lua_tointeger(L.s, C.int(index)))
|
|
}
|
|
|
|
// lua_tointeger
|
|
func (L *State) ToInteger64(index int) int64 {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return int64(C.lua_tointeger(L.s, C.int(index)))
|
|
}
|
|
|
|
// lua_tointeger
|
|
func (L *State) ToUInteger(index int) uint {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return uint(C.lua_tointeger(L.s, C.int(index)))
|
|
}
|
|
|
|
// lua_tointeger
|
|
func (L *State) ToUInteger32(index int) uint32 {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return uint32(C.lua_tointeger(L.s, C.int(index)))
|
|
}
|
|
|
|
// lua_tointeger
|
|
func (L *State) ToUInteger64(index int) uint64 {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return uint64(C.lua_tointeger(L.s, C.int(index)))
|
|
}
|
|
|
|
// lua_tointeger
|
|
func (L *State) ToFloat32(index int) float32 {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return float32(C.lua_tonumber(L.s, C.int(index)))
|
|
}
|
|
|
|
// lua_tointeger
|
|
func (L *State) ToFloat64(index int) float64 {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return float64(C.lua_tonumber(L.s, C.int(index)))
|
|
}
|
|
|
|
// lua_tonumber
|
|
func (L *State) ToNumber(index int) float64 {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return float64(C.lua_tonumber(L.s, C.int(index)))
|
|
}
|
|
|
|
func (L *State) CdataToInt64(index int) int64 {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return int64(C.lua_cdata_to_int64(L.s, C.int(index)))
|
|
}
|
|
|
|
func (L *State) CdataToInt32(index int) int32 {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return int32(C.lua_cdata_to_int32(L.s, C.int(index)))
|
|
}
|
|
|
|
func (L *State) CdataToUint64(index int) uint64 {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return uint64(C.lua_cdata_to_uint64(L.s, C.int(index)))
|
|
}
|
|
|
|
// lua_topointer
|
|
func (L *State) ToPointer(index int) uintptr {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return uintptr(C.lua_topointer(L.s, C.int(index)))
|
|
}
|
|
|
|
// lua_tothread
|
|
func (L *State) ToThread(index int) *State {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
ptr := (*C.lua_State)(unsafe.Pointer(C.lua_tothread(L.s, C.int(index))))
|
|
if ptr == nil {
|
|
return nil
|
|
}
|
|
return L.ToThreadHelper(ptr)
|
|
}
|
|
|
|
func (L *State) ToThreadHelper(ptr *C.lua_State) *State {
|
|
if ptr == nil {
|
|
return nil
|
|
}
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
upos := int(C.clua_dedup_coro(ptr))
|
|
already := L.MainCo.AllCoro[upos]
|
|
if already != nil {
|
|
return already
|
|
}
|
|
|
|
newstate := &State{
|
|
s: ptr,
|
|
r: L.r,
|
|
Shared: L.Shared,
|
|
MainCo: L.MainCo,
|
|
CmainCo: L.MainCo.s,
|
|
Index: -1, // not the main state/main thread.
|
|
Upos: upos,
|
|
}
|
|
// don't register non-main threads in gostates[].
|
|
|
|
// asserts that (Upos != 1)
|
|
if newstate.Upos == 1 {
|
|
panic(fmt.Sprintf("assert violated: we expected newstate.Upos to not be 1 for any non-main thread/coroutine stated! our code in c-golua.c depends on that"))
|
|
|
|
}
|
|
if newstate.Upos == -1 {
|
|
panic(fmt.Sprintf("assert violated: we expected newstate.Upos to not be -1 for any not known coroutine!"))
|
|
|
|
}
|
|
newstate.MainCo.AllCoro[newstate.Upos] = newstate
|
|
return newstate
|
|
}
|
|
|
|
// lua_touserdata
|
|
func (L *State) ToUserdata(index int) unsafe.Pointer {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return unsafe.Pointer(C.lua_touserdata(L.s, C.int(index)))
|
|
}
|
|
|
|
// lua_type
|
|
func (L *State) Type(index int) LuaValType {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return LuaValType(C.lua_type(L.s, C.int(index)))
|
|
}
|
|
|
|
// lua_typename
|
|
func (L *State) Typename(tp int) string {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return C.GoString(C.lua_typename(L.s, C.int(tp)))
|
|
}
|
|
|
|
// lua_xmove
|
|
func XMove(from *State, to *State, n int) {
|
|
defer from.r.Unlock()
|
|
defer to.r.Unlock()
|
|
from.r.Lock()
|
|
to.r.Lock()
|
|
C.lua_xmove(from.s, to.s, C.int(n))
|
|
}
|
|
|
|
// lua_yield
|
|
func (L *State) Yield(nresults int) int {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
return int(C.lua_yield(L.s, C.int(nresults)))
|
|
}
|
|
|
|
// Restricted library opens
|
|
|
|
// Calls luaopen_base
|
|
func (L *State) OpenBase() {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.clua_openbase(L.s)
|
|
}
|
|
|
|
// Calls luaopen_io
|
|
func (L *State) OpenIO() {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.clua_openio(L.s)
|
|
}
|
|
|
|
// Calls luaopen_math
|
|
func (L *State) OpenMath() {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.clua_openmath(L.s)
|
|
}
|
|
|
|
// Calls luaopen_package
|
|
func (L *State) OpenPackage() {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.clua_openpackage(L.s)
|
|
}
|
|
|
|
// Calls luaopen_string
|
|
func (L *State) OpenString() {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.clua_openstring(L.s)
|
|
}
|
|
|
|
// Calls luaopen_table
|
|
func (L *State) OpenTable() {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.clua_opentable(L.s)
|
|
}
|
|
|
|
// Calls luaopen_os
|
|
func (L *State) OpenOS() {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.clua_openos(L.s)
|
|
}
|
|
|
|
// Sets the maximum number of operations to execute at instrNumber, after this the execution ends
|
|
func (L *State) SetExecutionLimit(instrNumber int) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.clua_setexecutionlimit(L.s, C.int(instrNumber))
|
|
}
|
|
|
|
// Returns the current stack trace
|
|
func (L *State) StackTrace() []LuaStackEntry {
|
|
r := []LuaStackEntry{}
|
|
var d C.lua_Debug
|
|
Sln := C.CString("Sln")
|
|
defer C.free(unsafe.Pointer(Sln))
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
|
|
for depth := 0; C.lua_getstack(L.s, C.int(depth), &d) > 0; depth++ {
|
|
C.lua_getinfo(L.s, Sln, &d)
|
|
ssb := make([]byte, C.LUA_IDSIZE)
|
|
for i := 0; i < C.LUA_IDSIZE; i++ {
|
|
ssb[i] = byte(d.short_src[i])
|
|
if ssb[i] == 0 {
|
|
ssb = ssb[:i]
|
|
break
|
|
}
|
|
}
|
|
ss := string(ssb)
|
|
|
|
r = append(r, LuaStackEntry{C.GoString(d.name), C.GoString(d.source), ss, int(d.currentline)})
|
|
}
|
|
|
|
return r
|
|
}
|
|
|
|
func (L *State) RaiseError(msg string) {
|
|
st := L.StackTrace()
|
|
prefix := ""
|
|
if len(st) >= 1 {
|
|
prefix = fmt.Sprintf("%s:%d: ", st[1].ShortSource, st[1].CurrentLine)
|
|
}
|
|
panic(&LuaError{0, prefix + msg, st})
|
|
}
|
|
|
|
func (L *State) NewError(msg string) *LuaError {
|
|
return &LuaError{0, msg, L.StackTrace()}
|
|
}
|
|
|
|
// LuaJIT only: return ctype of the cdata at the top of the stack.
|
|
func (L *State) LuaJITctypeID(idx int) uint32 {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
//fmt.Printf("top of LuaJITctypeID, idx=%v, L is '%#v', top=%v\n", idx, L, L.GetTop())
|
|
res := C.clua_luajit_ctypeid(L.s, C.int(idx))
|
|
return uint32(res)
|
|
}
|
|
|
|
func (L *State) PushInt64(n int64) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.clua_luajit_push_cdata_int64(L.s, C.int64_t(n))
|
|
}
|
|
|
|
func (L *State) PushUint64(u uint64) {
|
|
defer L.r.Unlock()
|
|
L.r.Lock()
|
|
C.clua_luajit_push_cdata_uint64(L.s, C.uint64_t(u))
|
|
}
|
|
|
|
// jea
|
|
func DumpLuaStack(L *State) {
|
|
fmt.Printf("\n%s\n", DumpLuaStackAsString(L))
|
|
}
|
|
|
|
func DumpLuaStackAsString(L *State) (s string) {
|
|
var top int
|
|
|
|
top = L.GetTop()
|
|
isMain := L.PushThread()
|
|
thr := L.ToThread(-1)
|
|
L.SetTop(top)
|
|
s += fmt.Sprintf("========== begin DumpLuaStack (of coro %p/lua.State=%p; isMain=%v): top = %v\n", thr, thr.s, isMain, top)
|
|
for i := top; i >= 1; i-- {
|
|
|
|
t := L.Type(i)
|
|
s += fmt.Sprintf("DumpLuaStack: i=%v, t= %v\n", i, t)
|
|
s += LuaStackPosToString(L, i)
|
|
}
|
|
s += fmt.Sprintf("========= end of DumpLuaStack\n")
|
|
return
|
|
}
|
|
|
|
func LuaStackPosToString(L *State, i int) string {
|
|
t := L.Type(i)
|
|
|
|
switch t {
|
|
case LUA_TNONE: // -1
|
|
return fmt.Sprintf("LUA_TNONE; i=%v was invalid index\n", i)
|
|
case LUA_TNIL:
|
|
return fmt.Sprintf("LUA_TNIL: nil\n")
|
|
case LUA_TSTRING:
|
|
return fmt.Sprintf(" String : \t%v\n", L.ToString(i))
|
|
case LUA_TBOOLEAN:
|
|
return fmt.Sprintf(" Bool : \t\t%v\n", L.ToBoolean(i))
|
|
case LUA_TNUMBER:
|
|
return fmt.Sprintf(" Number : \t%v\n", L.ToNumber(i))
|
|
case LUA_TTABLE:
|
|
return fmt.Sprintf(" Table : \n%s\n", dumpTableString(L, i))
|
|
|
|
case 10: // LUA_TCDATA aka cdata
|
|
ctype := L.LuaJITctypeID(i)
|
|
switch ctype {
|
|
case 5: // int8
|
|
case 6: // uint8
|
|
case 7: // int16
|
|
case 8: // uint16
|
|
case 9: // int32
|
|
case 10: // uint32
|
|
case 11: // int64
|
|
val := L.CdataToInt64(i)
|
|
return fmt.Sprintf(" int64: '%v'\n", val)
|
|
case 12: // uint64
|
|
val := L.CdataToUint64(i)
|
|
return fmt.Sprintf(" uint64: '%v'\n", val)
|
|
case 13: // float32
|
|
case 14: // float64
|
|
|
|
case 0: // means it wasn't a ctype
|
|
}
|
|
|
|
case LUA_TUSERDATA:
|
|
return fmt.Sprintf(" Type(code %v/ LUA_TUSERDATA) : 0x%x with pointer %p\n", t, L.ToPointer(i), L.ToUserdata(i))
|
|
case LUA_TFUNCTION:
|
|
return fmt.Sprintf(" Type(code %v/ LUA_TFUNCTION) : 0x%x\n", t, L.ToPointer(i))
|
|
case LUA_TTHREAD:
|
|
return fmt.Sprintf(" Type(code %v/ LUA_TTHREAD) : 0x%x\n", t, L.ToPointer(i))
|
|
case LUA_TLIGHTUSERDATA:
|
|
return fmt.Sprintf(" Type(code %v/ LUA_TLIGHTUSERDATA) : 0x%x with pointer %p\n", t, L.ToPointer(i), L.ToUserdata(i))
|
|
default:
|
|
}
|
|
return fmt.Sprintf(" Type(code %v) : 0x%x, no auto-print available.\n", t, L.ToPointer(i))
|
|
}
|
|
|
|
func dumpTableString(L *State, index int) (s string) {
|
|
|
|
// Push another reference to the table on top of the stack (so we know
|
|
// where it is, and this function can work for negative, positive and
|
|
// pseudo indices
|
|
L.PushValue(index)
|
|
// stack now contains: -1 => table
|
|
L.PushNil()
|
|
// stack now contains: -1 => nil; -2 => table
|
|
for L.Next(-2) != 0 {
|
|
|
|
// stack now contains: -1 => value; -2 => key; -3 => table
|
|
// copy the key so that lua_tostring does not modify the original
|
|
L.PushValue(-2)
|
|
// stack now contains: -1 => key; -2 => value; -3 => key; -4 => table
|
|
key := L.ToString(-1)
|
|
value := L.ToString(-2)
|
|
s += fmt.Sprintf("'%s' => '%s'\n", key, value)
|
|
// pop value + copy of key, leaving original key
|
|
L.Pop(2)
|
|
// stack now contains: -1 => key; -2 => table
|
|
}
|
|
// stack now contains: -1 => table (when lua_next returns 0 it pops the key
|
|
// but does not push anything.)
|
|
// Pop table
|
|
L.Pop(1)
|
|
// Stack is now the same as it was on entry to this function
|
|
return
|
|
}
|