Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/internal/reflectlite/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -2208,6 +2208,9 @@ func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe
//go:linkname hashmapMakeReflect runtime.hashmapMakeReflect
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
// for approximately n elements.
func MakeMapWithSize(typ Type, n int) Value {
Expand Down Expand Up @@ -2254,6 +2257,26 @@ func MakeMap(typ Type) Value {
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 {
panic("unimplemented: (reflect.Value).Call()")
}
Expand Down
5 changes: 5 additions & 0 deletions src/reflect/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@ func MakeMapWithSize(typ Type, n int) Value {
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 {
panic("unimplemented: (reflect.Value).Call()")
}
Expand Down
75 changes: 75 additions & 0 deletions src/reflect/value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,81 @@ 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.
// TODO: drop this skip once tinygo-org/tinygo#5550 lands.
if runtime.GOOS == "wasip1" {
t.Skip("skipping panic cases: panic/recover on wasm needs #5550")
}

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

func IsRO(v Value) bool {
Expand Down