scream/vendor/src/github.com/layeh/gopher-luar/func.go

97 lines
2.5 KiB
Go

package luar
import (
"reflect"
"github.com/yuin/gopher-lua"
)
// LState is an wrapper for gopher-lua's LState. It should be used when you
// wish to have a function/method with the standard "func(*lua.LState) int"
// signature.
type LState struct {
*lua.LState
}
var (
refTypeLStatePtr reflect.Type
refTypeLuaLValueSlice reflect.Type
refTypeLuaLValue reflect.Type
refTypeInt reflect.Type
)
func init() {
refTypeLStatePtr = reflect.TypeOf(&LState{})
refTypeLuaLValueSlice = reflect.TypeOf([]lua.LValue{})
refTypeLuaLValue = reflect.TypeOf((*lua.LValue)(nil)).Elem()
refTypeInt = reflect.TypeOf(int(0))
}
func funcIsBypass(t reflect.Type) bool {
if t.NumIn() == 1 && t.NumOut() == 1 && t.In(0) == refTypeLStatePtr && t.Out(0) == refTypeInt {
return true
}
if t.NumIn() == 2 && t.NumOut() == 1 && t.In(1) == refTypeLStatePtr && t.Out(0) == refTypeInt {
return true
}
return false
}
func funcEvaluate(L *lua.LState, fn reflect.Value) int {
fnType := fn.Type()
if funcIsBypass(fnType) {
luarState := LState{L}
args := make([]reflect.Value, 0, 2)
if fnType.NumIn() == 2 {
receiverHint := fnType.In(0)
receiver := lValueToReflect(L.Get(1), receiverHint)
if receiver.Type() != receiverHint {
L.RaiseError("incorrect receiver type")
}
args = append(args, receiver)
L.Remove(1)
}
args = append(args, reflect.ValueOf(&luarState))
return fn.Call(args)[0].Interface().(int)
}
top := L.GetTop()
expected := fnType.NumIn()
variadic := fnType.IsVariadic()
if !variadic && top != expected {
L.RaiseError("invalid number of function arguments (%d expected, got %d)", expected, top)
}
if variadic && top < expected-1 {
L.RaiseError("invalid number of function arguments (%d or more expected, got %d)", expected-1, top)
}
args := make([]reflect.Value, top)
for i := 0; i < L.GetTop(); i++ {
var hint reflect.Type
if variadic && i >= expected-1 {
hint = fnType.In(expected - 1).Elem()
} else {
hint = fnType.In(i)
}
args[i] = lValueToReflect(L.Get(i+1), hint)
}
ret := fn.Call(args)
if len(ret) == 1 && ret[0].Type() == refTypeLuaLValueSlice {
values := ret[0].Interface().([]lua.LValue)
for _, value := range values {
L.Push(value)
}
return len(values)
}
for _, val := range ret {
L.Push(New(L, val.Interface()))
}
return len(ret)
}
func funcWrapper(L *lua.LState, fn reflect.Value) *lua.LFunction {
wrapper := func(L *lua.LState) int {
return funcEvaluate(L, fn)
}
return L.NewFunction(wrapper)
}