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 refTypeLuaLValue reflect.Type refTypeInt reflect.Type refTypeEmptyIface reflect.Type ) func init() { refTypeLStatePtr = reflect.TypeOf(&LState{}) refTypeLuaLValue = reflect.TypeOf((*lua.LValue)(nil)).Elem() refTypeInt = reflect.TypeOf(int(0)) refTypeEmptyIface = reflect.TypeOf((*interface{})(nil)).Elem() } func getFunc(L *lua.LState) (ref reflect.Value, refType reflect.Type) { ref = L.Get(lua.UpvalueIndex(1)).(*lua.LUserData).Value.(reflect.Value) refType = ref.Type() return } func isPtrReceiverMethod(L *lua.LState) bool { return bool(L.Get(lua.UpvalueIndex(2)).(lua.LBool)) } 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 funcBypass(L *lua.LState) int { ref, refType := getFunc(L) convertedPtr := false var receiver reflect.Value var ud lua.LValue luarState := LState{L} args := make([]reflect.Value, 0, 2) if refType.NumIn() == 2 { receiverHint := refType.In(0) ud = L.Get(1) var err error if isPtrReceiverMethod(L) { receiver, err = lValueToReflect(L, ud, receiverHint, &convertedPtr) } else { receiver, err = lValueToReflect(L, ud, receiverHint, nil) } if err != nil { L.ArgError(1, err.Error()) } args = append(args, receiver) L.Remove(1) } args = append(args, reflect.ValueOf(&luarState)) ret := ref.Call(args)[0].Interface().(int) if convertedPtr { ud.(*lua.LUserData).Value = receiver.Elem().Interface() } return ret } func funcRegular(L *lua.LState) int { ref, refType := getFunc(L) top := L.GetTop() expected := refType.NumIn() variadic := refType.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) } convertedPtr := false var receiver reflect.Value var ud lua.LValue args := make([]reflect.Value, top) for i := 0; i < L.GetTop(); i++ { var hint reflect.Type if variadic && i >= expected-1 { hint = refType.In(expected - 1).Elem() } else { hint = refType.In(i) } var arg reflect.Value var err error if i == 0 && isPtrReceiverMethod(L) { ud = L.Get(1) v := ud arg, err = lValueToReflect(L, v, hint, &convertedPtr) if err != nil { L.ArgError(1, err.Error()) } receiver = arg } else { v := L.Get(i + 1) arg, err = lValueToReflect(L, v, hint, nil) if err != nil { L.ArgError(i+1, err.Error()) } } args[i] = arg } ret := ref.Call(args) if convertedPtr { ud.(*lua.LUserData).Value = receiver.Elem().Interface() } for _, val := range ret { L.Push(New(L, val.Interface())) } return len(ret) } func funcWrapper(L *lua.LState, fn reflect.Value, isPtrReceiverMethod bool) *lua.LFunction { up := L.NewUserData() up.Value = fn if funcIsBypass(fn.Type()) { return L.NewClosure(funcBypass, up, lua.LBool(isPtrReceiverMethod)) } return L.NewClosure(funcRegular, up, lua.LBool(isPtrReceiverMethod)) }