mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-23 05:49:01 +00:00
reflect: implement MakeChan
TinyGo had MakeMap/MakeSlice but not MakeChan. Implement it via the runtime chanMake primitive (mirroring MakeMap), so packages that call reflect.MakeChan (e.g. github.com/ugorji/go/codec used by gin) compile and work.
This commit is contained in:
committed by
Damian Gryski
parent
af429a597b
commit
1579b235e8
@@ -2208,6 +2208,9 @@ func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe
|
|||||||
//go:linkname hashmapMakeReflect runtime.hashmapMakeReflect
|
//go:linkname hashmapMakeReflect runtime.hashmapMakeReflect
|
||||||
func hashmapMakeReflect(keySize, valueSize, sizeHint uintptr, keyType unsafe.Pointer) unsafe.Pointer
|
func hashmapMakeReflect(keySize, valueSize, sizeHint uintptr, keyType unsafe.Pointer) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname chanMake runtime.chanMake
|
||||||
|
func chanMake(elementSize uintptr, bufSize uintptr) unsafe.Pointer
|
||||||
|
|
||||||
// MakeMapWithSize creates a new map with the specified type and initial space
|
// MakeMapWithSize creates a new map with the specified type and initial space
|
||||||
// for approximately n elements.
|
// for approximately n elements.
|
||||||
func MakeMapWithSize(typ Type, n int) Value {
|
func MakeMapWithSize(typ Type, n int) Value {
|
||||||
@@ -2254,6 +2257,26 @@ func MakeMap(typ Type) Value {
|
|||||||
return MakeMapWithSize(typ, 8)
|
return MakeMapWithSize(typ, 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakeChan creates a new channel with the specified type and buffer size.
|
||||||
|
func MakeChan(typ Type, size int) Value {
|
||||||
|
if typ.Kind() != Chan {
|
||||||
|
panic(&ValueError{Method: "MakeChan", Kind: typ.Kind()})
|
||||||
|
}
|
||||||
|
if size < 0 {
|
||||||
|
panic("reflect.MakeChan: negative buffer size")
|
||||||
|
}
|
||||||
|
if typ.(*RawType).ChanDir() != BothDir {
|
||||||
|
panic("reflect.MakeChan: unidirectional channel type")
|
||||||
|
}
|
||||||
|
elem := typ.Elem().(*RawType)
|
||||||
|
ch := chanMake(elem.Size(), uintptr(size))
|
||||||
|
return Value{
|
||||||
|
typecode: typ.(*RawType),
|
||||||
|
value: ch,
|
||||||
|
flags: valueFlagExported,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (v Value) Call(in []Value) []Value {
|
func (v Value) Call(in []Value) []Value {
|
||||||
panic("unimplemented: (reflect.Value).Call()")
|
panic("unimplemented: (reflect.Value).Call()")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -201,6 +201,11 @@ func MakeMapWithSize(typ Type, n int) Value {
|
|||||||
return Value{reflectlite.MakeMapWithSize(toRawType(typ), n)}
|
return Value{reflectlite.MakeMapWithSize(toRawType(typ), n)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakeChan creates a new channel with the specified type and buffer size.
|
||||||
|
func MakeChan(typ Type, buffer int) Value {
|
||||||
|
return Value{reflectlite.MakeChan(toRawType(typ), buffer)}
|
||||||
|
}
|
||||||
|
|
||||||
func (v Value) Call(in []Value) []Value {
|
func (v Value) Call(in []Value) []Value {
|
||||||
panic("unimplemented: (reflect.Value).Call()")
|
panic("unimplemented: (reflect.Value).Call()")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -994,6 +994,83 @@ func TestTypeAssertPanic(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTinyMakeChan(t *testing.T) {
|
||||||
|
// Value.Send and Value.Recv are not implemented yet, so the channel is
|
||||||
|
// exercised through Interface(): that proves MakeChan returns a working
|
||||||
|
// channel rather than merely a value of the right kind.
|
||||||
|
t.Run("buffered", func(t *testing.T) {
|
||||||
|
v := MakeChan(TypeOf(make(chan int)), 2)
|
||||||
|
if got, want := v.Kind(), Chan; got != want {
|
||||||
|
t.Errorf("Kind()=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := v.Cap(), 2; got != want {
|
||||||
|
t.Errorf("Cap()=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := v.Len(), 0; got != want {
|
||||||
|
t.Errorf("Len()=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
ch, ok := v.Interface().(chan int)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Interface() is %T, want chan int", v.Interface())
|
||||||
|
}
|
||||||
|
ch <- 1
|
||||||
|
ch <- 2
|
||||||
|
if got, want := v.Len(), 2; got != want {
|
||||||
|
t.Errorf("Len()=%v after two sends, want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := <-ch, 1; got != want {
|
||||||
|
t.Errorf("<-ch=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := <-ch, 2; got != want {
|
||||||
|
t.Errorf("<-ch=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("unbuffered", func(t *testing.T) {
|
||||||
|
v := MakeChan(TypeOf(make(chan string)), 0)
|
||||||
|
if got, want := v.Cap(), 0; got != want {
|
||||||
|
t.Errorf("Cap()=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
ch, ok := v.Interface().(chan string)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Interface() is %T, want chan string", v.Interface())
|
||||||
|
}
|
||||||
|
go func() { ch <- "hello" }()
|
||||||
|
if got, want := <-ch, "hello"; got != want {
|
||||||
|
t.Errorf("<-ch=%q, want %q", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// The three cases below rely on recovering from a panic, which wasm
|
||||||
|
// cannot do yet without exceptions. Log and return rather than Skip:
|
||||||
|
// t.Skip needs the same machinery it is standing in for.
|
||||||
|
// TODO: drop this once tinygo-org/tinygo#5550 lands.
|
||||||
|
if runtime.GOOS == "wasip1" {
|
||||||
|
t.Log("not running the panic cases: panic/recover on wasm needs #5550")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("not a channel", func(t *testing.T) {
|
||||||
|
defer func() { recover() }()
|
||||||
|
MakeChan(TypeOf(0), 0)
|
||||||
|
t.Fatalf("MakeChan did not panic on a non-channel type")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("negative buffer", func(t *testing.T) {
|
||||||
|
defer func() { recover() }()
|
||||||
|
MakeChan(TypeOf(make(chan int)), -1)
|
||||||
|
t.Fatalf("MakeChan did not panic on a negative buffer size")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("directional", func(t *testing.T) {
|
||||||
|
defer func() { recover() }()
|
||||||
|
var recvOnly <-chan int
|
||||||
|
MakeChan(TypeOf(recvOnly), 0)
|
||||||
|
t.Fatalf("MakeChan did not panic on a unidirectional channel type")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Functions needed by all_test.go
|
// Functions needed by all_test.go
|
||||||
|
|
||||||
func IsRO(v Value) bool {
|
func IsRO(v Value) bool {
|
||||||
|
|||||||
Reference in New Issue
Block a user