202 lines
5.1 KiB
Go
202 lines
5.1 KiB
Go
package luar
|
|
|
|
import (
|
|
"container/list"
|
|
"reflect"
|
|
|
|
"github.com/yuin/gopher-lua"
|
|
)
|
|
|
|
func addMethods(L *lua.LState, c *Config, vtype reflect.Type, tbl *lua.LTable, ptrReceiver bool) {
|
|
for i := 0; i < vtype.NumMethod(); i++ {
|
|
method := vtype.Method(i)
|
|
if method.PkgPath != "" {
|
|
continue
|
|
}
|
|
namesFn := c.MethodNames
|
|
if namesFn == nil {
|
|
namesFn = defaultMethodNames
|
|
}
|
|
fn := funcWrapper(L, method.Func, ptrReceiver)
|
|
for _, name := range namesFn(vtype, method) {
|
|
tbl.RawSetString(name, fn)
|
|
}
|
|
}
|
|
}
|
|
|
|
func addFields(L *lua.LState, c *Config, vtype reflect.Type, tbl *lua.LTable) {
|
|
type element struct {
|
|
Type reflect.Type
|
|
Index []int
|
|
}
|
|
|
|
queue := list.New()
|
|
queue.PushFront(element{
|
|
Type: vtype,
|
|
})
|
|
|
|
namesFn := c.FieldNames
|
|
if namesFn == nil {
|
|
namesFn = defaultFieldNames
|
|
}
|
|
|
|
for queue.Len() > 0 {
|
|
e := queue.Back()
|
|
elem := e.Value.(element)
|
|
vtype := elem.Type
|
|
fields:
|
|
for i := 0; i < vtype.NumField(); i++ {
|
|
field := vtype.Field(i)
|
|
if field.PkgPath != "" && !field.Anonymous {
|
|
continue
|
|
}
|
|
names := namesFn(vtype, field)
|
|
for _, key := range names {
|
|
if tbl.RawGetString(key) != lua.LNil {
|
|
continue fields
|
|
}
|
|
}
|
|
|
|
ud := L.NewUserData()
|
|
ud.Value = append(elem.Index[:len(elem.Index):len(elem.Index)], i)
|
|
for _, key := range names {
|
|
tbl.RawSetString(key, ud)
|
|
}
|
|
if field.Anonymous {
|
|
t := field.Type
|
|
if field.Type.Kind() != reflect.Struct {
|
|
if field.Type.Kind() != reflect.Ptr || field.Type.Elem().Kind() != reflect.Struct {
|
|
continue
|
|
}
|
|
t = field.Type.Elem()
|
|
}
|
|
queue.PushFront(element{
|
|
Type: t,
|
|
Index: append(elem.Index[:len(elem.Index):len(elem.Index)], i),
|
|
})
|
|
}
|
|
}
|
|
|
|
queue.Remove(e)
|
|
}
|
|
}
|
|
|
|
func getMetatable(L *lua.LState, vtype reflect.Type) *lua.LTable {
|
|
config := GetConfig(L)
|
|
|
|
if v := config.regular[vtype]; v != nil {
|
|
return v
|
|
}
|
|
|
|
var (
|
|
mt *lua.LTable
|
|
methods = L.CreateTable(0, vtype.NumMethod())
|
|
)
|
|
|
|
switch vtype.Kind() {
|
|
case reflect.Array:
|
|
mt = L.CreateTable(0, 7)
|
|
|
|
mt.RawSetString("__index", L.NewFunction(arrayIndex))
|
|
mt.RawSetString("__len", L.NewFunction(arrayLen))
|
|
mt.RawSetString("__call", L.NewFunction(arrayCall))
|
|
mt.RawSetString("__eq", L.NewFunction(arrayEq))
|
|
|
|
addMethods(L, config, vtype, methods, false)
|
|
case reflect.Chan:
|
|
mt = L.CreateTable(0, 8)
|
|
|
|
mt.RawSetString("__index", L.NewFunction(chanIndex))
|
|
mt.RawSetString("__len", L.NewFunction(chanLen))
|
|
mt.RawSetString("__eq", L.NewFunction(chanEq))
|
|
mt.RawSetString("__call", L.NewFunction(chanCall))
|
|
mt.RawSetString("__unm", L.NewFunction(chanUnm))
|
|
|
|
addMethods(L, config, vtype, methods, false)
|
|
case reflect.Map:
|
|
mt = L.CreateTable(0, 7)
|
|
|
|
mt.RawSetString("__index", L.NewFunction(mapIndex))
|
|
mt.RawSetString("__newindex", L.NewFunction(mapNewIndex))
|
|
mt.RawSetString("__len", L.NewFunction(mapLen))
|
|
mt.RawSetString("__call", L.NewFunction(mapCall))
|
|
|
|
addMethods(L, config, vtype, methods, false)
|
|
case reflect.Slice:
|
|
mt = L.CreateTable(0, 8)
|
|
|
|
mt.RawSetString("__index", L.NewFunction(sliceIndex))
|
|
mt.RawSetString("__newindex", L.NewFunction(sliceNewIndex))
|
|
mt.RawSetString("__len", L.NewFunction(sliceLen))
|
|
mt.RawSetString("__call", L.NewFunction(sliceCall))
|
|
mt.RawSetString("__add", L.NewFunction(sliceAdd))
|
|
|
|
addMethods(L, config, vtype, methods, false)
|
|
case reflect.Struct:
|
|
mt = L.CreateTable(0, 6)
|
|
|
|
fields := L.CreateTable(0, vtype.NumField())
|
|
addFields(L, config, vtype, fields)
|
|
mt.RawSetString("fields", fields)
|
|
|
|
mt.RawSetString("__index", L.NewFunction(structIndex))
|
|
mt.RawSetString("__eq", L.NewFunction(structEq))
|
|
|
|
addMethods(L, config, vtype, methods, false)
|
|
case reflect.Ptr:
|
|
switch vtype.Elem().Kind() {
|
|
case reflect.Array:
|
|
mt = L.CreateTable(0, 10)
|
|
|
|
mt.RawSetString("__index", L.NewFunction(arrayPtrIndex))
|
|
mt.RawSetString("__newindex", L.NewFunction(arrayPtrNewIndex))
|
|
mt.RawSetString("__call", L.NewFunction(arrayCall)) // same as non-pointer
|
|
mt.RawSetString("__len", L.NewFunction(arrayLen)) // same as non-pointer
|
|
case reflect.Struct:
|
|
mt = L.CreateTable(0, 8)
|
|
|
|
mt.RawSetString("__index", L.NewFunction(structPtrIndex))
|
|
mt.RawSetString("__newindex", L.NewFunction(structPtrNewIndex))
|
|
default:
|
|
mt = L.CreateTable(0, 7)
|
|
|
|
mt.RawSetString("__index", L.NewFunction(ptrIndex))
|
|
}
|
|
|
|
mt.RawSetString("__eq", L.NewFunction(ptrEq))
|
|
mt.RawSetString("__pow", L.NewFunction(ptrPow))
|
|
mt.RawSetString("__unm", L.NewFunction(ptrUnm))
|
|
|
|
addMethods(L, config, vtype, methods, true)
|
|
default:
|
|
panic("unexpected kind " + vtype.Kind().String())
|
|
}
|
|
|
|
mt.RawSetString("__tostring", L.NewFunction(tostring))
|
|
mt.RawSetString("__metatable", lua.LString("gopher-luar"))
|
|
mt.RawSetString("methods", methods)
|
|
|
|
config.regular[vtype] = mt
|
|
return mt
|
|
}
|
|
|
|
func getMetatableFromValue(L *lua.LState, value reflect.Value) *lua.LTable {
|
|
vtype := value.Type()
|
|
return getMetatable(L, vtype)
|
|
}
|
|
|
|
func getTypeMetatable(L *lua.LState, t reflect.Type) *lua.LTable {
|
|
config := GetConfig(L)
|
|
|
|
if v := config.types; v != nil {
|
|
return v
|
|
}
|
|
|
|
mt := L.CreateTable(0, 2)
|
|
mt.RawSetString("__call", L.NewFunction(typeCall))
|
|
mt.RawSetString("__eq", L.NewFunction(typeEq))
|
|
|
|
config.types = mt
|
|
return mt
|
|
}
|