123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- package lua
- import (
- "reflect"
- )
- func checkChannel(L *LState, idx int) reflect.Value {
- ch := L.CheckChannel(idx)
- return reflect.ValueOf(ch)
- }
- func checkGoroutineSafe(L *LState, idx int) LValue {
- v := L.CheckAny(2)
- if !isGoroutineSafe(v) {
- L.ArgError(2, "can not send a function, userdata, thread or table that has a metatable")
- }
- return v
- }
- func OpenChannel(L *LState) int {
- var mod LValue
- //_, ok := L.G.builtinMts[int(LTChannel)]
- // if !ok {
- mod = L.RegisterModule(ChannelLibName, channelFuncs)
- mt := L.SetFuncs(L.NewTable(), channelMethods)
- mt.RawSetString("__index", mt)
- L.G.builtinMts[int(LTChannel)] = mt
- // }
- L.Push(mod)
- return 1
- }
- var channelFuncs = map[string]LGFunction{
- "make": channelMake,
- "select": channelSelect,
- }
- func channelMake(L *LState) int {
- buffer := L.OptInt(1, 0)
- L.Push(LChannel(make(chan LValue, buffer)))
- return 1
- }
- func channelSelect(L *LState) int {
- //TODO check case table size
- cases := make([]reflect.SelectCase, L.GetTop())
- top := L.GetTop()
- for i := 0; i < top; i++ {
- cas := reflect.SelectCase{reflect.SelectSend, reflect.ValueOf(nil), reflect.ValueOf(nil)}
- tbl := L.CheckTable(i + 1)
- dir, ok1 := tbl.RawGetInt(1).(LString)
- if !ok1 {
- L.ArgError(i+1, "invalid select case")
- }
- switch string(dir) {
- case "<-|":
- ch, ok := tbl.RawGetInt(2).(LChannel)
- if !ok {
- L.ArgError(i+1, "invalid select case")
- }
- cas.Chan = reflect.ValueOf((chan LValue)(ch))
- v := tbl.RawGetInt(3)
- if !isGoroutineSafe(v) {
- L.ArgError(i+1, "can not send a function, userdata, thread or table that has a metatable")
- }
- cas.Send = reflect.ValueOf(v)
- case "|<-":
- ch, ok := tbl.RawGetInt(2).(LChannel)
- if !ok {
- L.ArgError(i+1, "invalid select case")
- }
- cas.Chan = reflect.ValueOf((chan LValue)(ch))
- cas.Dir = reflect.SelectRecv
- case "default":
- cas.Dir = reflect.SelectDefault
- default:
- L.ArgError(i+1, "invalid channel direction:"+string(dir))
- }
- cases[i] = cas
- }
- pos, recv, rok := reflect.Select(cases)
- lv := LNil
- if recv.Kind() != 0 {
- lv, _ = recv.Interface().(LValue)
- if lv == nil {
- lv = LNil
- }
- }
- tbl := L.Get(pos + 1).(*LTable)
- last := tbl.RawGetInt(tbl.Len())
- if last.Type() == LTFunction {
- L.Push(last)
- switch cases[pos].Dir {
- case reflect.SelectRecv:
- if rok {
- L.Push(LTrue)
- } else {
- L.Push(LFalse)
- }
- L.Push(lv)
- L.Call(2, 0)
- case reflect.SelectSend:
- L.Push(tbl.RawGetInt(3))
- L.Call(1, 0)
- case reflect.SelectDefault:
- L.Call(0, 0)
- }
- }
- L.Push(LNumber(pos + 1))
- L.Push(lv)
- if rok {
- L.Push(LTrue)
- } else {
- L.Push(LFalse)
- }
- return 3
- }
- var channelMethods = map[string]LGFunction{
- "receive": channelReceive,
- "send": channelSend,
- "close": channelClose,
- }
- func channelReceive(L *LState) int {
- rch := checkChannel(L, 1)
- v, ok := rch.Recv()
- if ok {
- L.Push(LTrue)
- L.Push(v.Interface().(LValue))
- } else {
- L.Push(LFalse)
- L.Push(LNil)
- }
- return 2
- }
- func channelSend(L *LState) int {
- rch := checkChannel(L, 1)
- v := checkGoroutineSafe(L, 2)
- rch.Send(reflect.ValueOf(v))
- return 0
- }
- func channelClose(L *LState) int {
- rch := checkChannel(L, 1)
- rch.Close()
- return 0
- }
- //
|