From f8bfa62e431a782f47e6ec22d3fe34d00d5a0024 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 15 Feb 2023 12:11:13 -0800 Subject: [PATCH 01/47] compiler, reflect: add map key and element type info --- compiler/interface.go | 4 ++++ src/reflect/type.go | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/compiler/interface.go b/compiler/interface.go index a359f33a4b..097eadcf4f 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -131,6 +131,8 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value { case *types.Map: typeFieldTypes = append(typeFieldTypes, types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]), + types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]), + types.NewVar(token.NoPos, nil, "keyType", types.Typ[types.UnsafePointer]), ) case *types.Struct: typeFieldTypes = append(typeFieldTypes, @@ -193,6 +195,8 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value { case *types.Map: typeFields = []llvm.Value{ c.getTypeCode(types.NewPointer(typ)), // ptrTo + c.getTypeCode(typ.Elem()), // elem + c.getTypeCode(typ.Key()), // key } case *types.Struct: typeFields = []llvm.Value{ diff --git a/src/reflect/type.go b/src/reflect/type.go index cc106a70d3..3998adc026 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -33,6 +33,8 @@ // - map types (this is still missing the key and element types) // meta uint8 // ptrTo *typeStruct +// elem *typeStruct +// key *typeStruct // - struct types (see structType): // meta uint8 // numField uint16 @@ -408,6 +410,13 @@ type arrayType struct { arrayLen uintptr } +type mapType struct { + rawType + ptrTo *rawType + elem *rawType + key *rawType +} + // Type for struct types. The numField value is intentionally put before ptrTo // for better struct packing on 32-bit and 64-bit architectures. On these // architectures, the ptrTo field still has the same offset as in all the other From cd93cd53a1ddee20bedc1f459ead06f1b7b4d3aa Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 15 Feb 2023 12:12:51 -0800 Subject: [PATCH 02/47] reflect: add MapIndex --- src/reflect/type.go | 12 ++++++++++-- src/reflect/value.go | 23 ++++++++++++++++++++++- src/runtime/hashmap.go | 5 +++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/reflect/type.go b/src/reflect/type.go index 3998adc026..18e8499ab3 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -481,13 +481,21 @@ func (t *rawType) elem() *rawType { switch underlying.Kind() { case Pointer: return (*ptrType)(unsafe.Pointer(underlying)).elem - case Chan, Slice, Array: + case Chan, Slice, Array, Map: return (*elemType)(unsafe.Pointer(underlying)).elem - default: // not implemented: Map + default: panic("unimplemented: (reflect.Type).Elem()") } } +func (t *rawType) key() *rawType { + underlying := t.underlying() + if underlying.Kind() != Map { + panic("key called on non-map type") + } + return (*mapType)(unsafe.Pointer(underlying)).key +} + // Field returns the type of the i'th field of this struct type. It panics if t // is not a struct type. func (t *rawType) Field(i int) StructField { diff --git a/src/reflect/value.go b/src/reflect/value.go index 32898f0d22..ac578ad524 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -645,7 +645,25 @@ func (v Value) MapKeys() []Value { } func (v Value) MapIndex(key Value) Value { - panic("unimplemented: (reflect.Value).MapIndex()") + if v.Kind() != Map { + panic(&ValueError{Method: "MapIndex", Kind: v.Kind()}) + } + + // compare key type with actual key type of map + if key.typecode != v.typecode.key() { + panic(&ValueError{Method: "MapIndex"}) + } + + elemType := v.Type().Elem() + elem := New(elemType) + + switch key.Kind() { + case String: + hashmapStringGet(v.value, *(*string)(key.value), elem.value, elemType.Size()) + return elem.Elem() + default: + panic("unimplemented: (reflect.Value).MapIndex()") + } } func (v Value) MapRange() *MapIter { @@ -863,6 +881,9 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer //go:linkname sliceAppend runtime.sliceAppend func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen uintptr, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) +//go:linkname hashmapStringGet runtime.hashmapStringGetUnsafePointer +func hashmapStringGet(m unsafe.Pointer, key string, value unsafe.Pointer, valueSize uintptr) bool + // Copy copies the contents of src into dst until either // dst has been filled or src has been exhausted. func Copy(dst, src Value) int { diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go index e00b223d4d..b3479bcafe 100644 --- a/src/runtime/hashmap.go +++ b/src/runtime/hashmap.go @@ -468,6 +468,11 @@ func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize ui return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash) } +func hashmapStringGetUnsafePointer(p unsafe.Pointer, key string, value unsafe.Pointer, valueSize uintptr) bool { + m := (*hashmap)(p) + return hashmapStringGet(m, key, value, valueSize) +} + func hashmapStringDelete(m *hashmap, key string) { if m == nil { return From bba9fdeea1f1d40b76dcff3d727707d1cb3520b3 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 15 Feb 2023 12:33:00 -0800 Subject: [PATCH 03/47] reflect: add SetMapIndex --- src/reflect/value.go | 23 ++++++++++++++++++++++- src/runtime/hashmap.go | 5 +++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index ac578ad524..f619c59b5c 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -884,6 +884,9 @@ func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen uintp //go:linkname hashmapStringGet runtime.hashmapStringGetUnsafePointer func hashmapStringGet(m unsafe.Pointer, key string, value unsafe.Pointer, valueSize uintptr) bool +//go:linkname hashmapStringSet runtime.hashmapStringSetUnsafePointer +func hashmapStringSet(m unsafe.Pointer, key string, value unsafe.Pointer) + // Copy copies the contents of src into dst until either // dst has been filled or src has been exhausted. func Copy(dst, src Value) int { @@ -925,7 +928,25 @@ func AppendSlice(s, t Value) Value { } func (v Value) SetMapIndex(key, elem Value) { - panic("unimplemented: (reflect.Value).SetMapIndex()") + if v.Kind() != Map { + panic(&ValueError{Method: "MapIndex", Kind: v.Kind()}) + } + + // compare key type with actual key type of map + if key.typecode != v.typecode.key() { + panic(&ValueError{Method: "MapIndex"}) + } + + if elem.typecode != v.typecode.elem() { + panic(&ValueError{Method: "MapIndex"}) + } + + switch key.Kind() { + case String: + hashmapStringSet(v.value, *(*string)(key.value), elem.value) + default: + panic("unimplemented: (reflect.Value).MapIndex()") + } } // FieldByIndex returns the nested field corresponding to index. diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go index b3479bcafe..ba553ea078 100644 --- a/src/runtime/hashmap.go +++ b/src/runtime/hashmap.go @@ -459,6 +459,11 @@ func hashmapStringSet(m *hashmap, key string, value unsafe.Pointer) { hashmapSet(m, unsafe.Pointer(&key), value, hash) } +func hashmapStringSetUnsafePointer(p unsafe.Pointer, key string, value unsafe.Pointer) { + m := (*hashmap)(p) + hashmapStringSet(m, key, value) +} + func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize uintptr) bool { if m == nil { memzero(value, uintptr(valueSize)) From e44bd0d7ee503bfc190c9ce6e7c533e1dcbc9b3d Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 15 Feb 2023 12:50:10 -0800 Subject: [PATCH 04/47] reflect: handle indirect values in SetMapIndex --- src/reflect/value.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index f619c59b5c..30784fd9b6 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -943,7 +943,12 @@ func (v Value) SetMapIndex(key, elem Value) { switch key.Kind() { case String: - hashmapStringSet(v.value, *(*string)(key.value), elem.value) + if elem.isIndirect() { + hashmapStringSet(v.value, *(*string)(key.value), elem.value) + } else { + hashmapStringSet(v.value, *(*string)(key.value), (unsafe.Pointer)(&elem.value)) + } + default: panic("unimplemented: (reflect.Value).MapIndex()") } From da935f8e7c62ddc5ce3c4d0f3891da2e9130cb58 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 15 Feb 2023 13:03:00 -0800 Subject: [PATCH 05/47] reflect: handle Type.Key() --- src/reflect/type.go | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/reflect/type.go b/src/reflect/type.go index 18e8499ab3..d4b81b74c1 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -445,6 +445,10 @@ func (t *rawType) underlying() *rawType { return t } +func (t *rawType) isNamed() bool { + return t.meta&flagNamed != 0 +} + func TypeOf(i interface{}) Type { return ValueOf(i).typecode } @@ -463,7 +467,12 @@ func PointerTo(t Type) Type { } func (t *rawType) String() string { - return "T" + if t.isNamed() { + // TODO(dgryski): lookup named type here + return "T" + } + // TODO(dgryski): doesn't yet handle complex types + return t.Kind().String() } func (t *rawType) Kind() Kind { @@ -810,11 +819,15 @@ func (t *rawType) NumMethod() int { } func (t *rawType) Name() string { - panic("unimplemented: (reflect.Type).Name()") + return "unimplemented: (reflect.Type).Name()" } func (t *rawType) Key() Type { - panic("unimplemented: (reflect.Type).Key()") + if t.Kind() != Map { + panic(TypeError{"Key"}) + } + + return t.key() } func (t rawType) In(i int) Type { From 39fb3a5adb9c70c84ca32398a513371a7622c164 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 15 Feb 2023 13:03:53 -0800 Subject: [PATCH 06/47] reflect: use t.isNamed() --- src/reflect/type.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reflect/type.go b/src/reflect/type.go index d4b81b74c1..aa76c9ea80 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -439,7 +439,7 @@ type structField struct { // Equivalent to (go/types.Type).Underlying(): if this is a named type return // the underlying type, else just return the type itself. func (t *rawType) underlying() *rawType { - if t.meta&flagNamed != 0 { + if t.isNamed() { return (*elemType)(unsafe.Pointer(t)).elem } return t From d8fa66f96e8b11fee8196683dc38cba628d1e0e9 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 15 Feb 2023 13:41:51 -0800 Subject: [PATCH 07/47] reflect: add MapKeys() --- src/reflect/value.go | 28 +++++++++++++++++++++++++++- src/runtime/hashmap.go | 8 ++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 30784fd9b6..6b40cf8832 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -641,7 +641,27 @@ func (v Value) OverflowFloat(x float64) bool { } func (v Value) MapKeys() []Value { - panic("unimplemented: (reflect.Value).MapKeys()") + if v.Kind() != Map { + panic(&ValueError{Method: "MapKeys", Kind: v.Kind()}) + } + + // empty map + if v.Len() == 0 { + return nil + } + + var keys []Value + + it := hashmapNewIterator() + k := New(v.typecode.Key()) + e := New(v.typecode.Elem()) + + for hashmapNext(v.value, it, k.value, e.value) { + keys = append(keys, k.Elem()) + k = New(v.typecode.Key()) + } + + return keys } func (v Value) MapIndex(key Value) Value { @@ -887,6 +907,12 @@ func hashmapStringGet(m unsafe.Pointer, key string, value unsafe.Pointer, valueS //go:linkname hashmapStringSet runtime.hashmapStringSetUnsafePointer func hashmapStringSet(m unsafe.Pointer, key string, value unsafe.Pointer) +//go:linkname hashmapNewIterator runtime.hashmapNewIterator +func hashmapNewIterator() unsafe.Pointer + +//go:linkname hashmapNext runtime.hashmapNext +func hashmapNext(m unsafe.Pointer, it unsafe.Pointer, key, value unsafe.Pointer) bool + // Copy copies the contents of src into dst until either // dst has been filled or src has been exhausted. func Copy(dst, src Value) int { diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go index ba553ea078..b67118d383 100644 --- a/src/runtime/hashmap.go +++ b/src/runtime/hashmap.go @@ -49,6 +49,11 @@ type hashmapIterator struct { bucketIndex uint8 // current index into bucket } +// for reflect +func hashmapNewIterator() unsafe.Pointer { + return unsafe.Pointer(new(hashmapIterator)) +} + // Get the topmost 8 bits of the hash, without using a special value (like 0). func hashmapTopHash(hash uint32) uint8 { tophash := uint8(hash >> 24) @@ -409,6 +414,9 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo } } +// for reflect +func hashmapNextUnsafePointer(m unsafe.Pointer, it unsafe.Pointer, key, value unsafe.Pointer) bool + // Hashmap with plain binary data keys (not containing strings etc.). func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) { if m == nil { From 635b313965b3bca324945dbd14fae364a652a095 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 15 Feb 2023 14:02:30 -0800 Subject: [PATCH 08/47] reflect: add MapRange() --- src/reflect/value.go | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 6b40cf8832..8d714b2428 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -687,22 +687,56 @@ func (v Value) MapIndex(key Value) Value { } func (v Value) MapRange() *MapIter { - panic("unimplemented: (reflect.Value).MapRange()") + return &MapIter{ + m: v, + it: hashmapNewIterator(), + key: New(v.typecode.Key()), + val: New(v.typecode.Elem()), + } } type MapIter struct { + m Value + it unsafe.Pointer + key Value + val Value + + valid bool + done bool } func (it *MapIter) Key() Value { - panic("unimplemented: (*reflect.MapIter).Key()") + if !it.valid { + panic("MapIter: Key() called before Next()") + } + + if it.done { + panic("MapIter.Key called on exhausted iterator") + } + + return it.key.Elem() } func (it *MapIter) Value() Value { - panic("unimplemented: (*reflect.MapIter).Value()") + if !it.valid { + panic("MapIter: Value() called before Next()") + } + + if it.done { + panic("MapIter.Value called on exhausted iterator") + } + + return it.val.Elem() } func (it *MapIter) Next() bool { - panic("unimplemented: (*reflect.MapIter).Next()") + it.valid = true + ok := hashmapNext(it.m.value, it.it, it.key.value, it.val.value) + if !ok { + it.done = true + } + + return ok } func (v Value) Set(x Value) { From 08487d8d4f3f7bc31cca88422e9efa45323f2b7f Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 15 Feb 2023 21:46:11 -0800 Subject: [PATCH 09/47] reflect,runtime: remove UnsafePointer wrappers --- src/reflect/value.go | 10 +++++----- src/runtime/chan.go | 12 ------------ src/runtime/hashmap.go | 19 ------------------- 3 files changed, 5 insertions(+), 36 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 8d714b2428..5cb7c19c91 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -389,10 +389,10 @@ func (v Value) Slice3(i, j, k int) Value { panic("unimplemented: (reflect.Value).Slice3()") } -//go:linkname maplen runtime.hashmapLenUnsafePointer +//go:linkname maplen runtime.hashmapLen func maplen(p unsafe.Pointer) int -//go:linkname chanlen runtime.chanLenUnsafePointer +//go:linkname chanlen runtime.chanLen func chanlen(p unsafe.Pointer) int // Len returns the length of this value for slices, strings, arrays, channels, @@ -414,7 +414,7 @@ func (v Value) Len() int { } } -//go:linkname chancap runtime.chanCapUnsafePointer +//go:linkname chancap runtime.chanCap func chancap(p unsafe.Pointer) int // Cap returns the capacity of this value for arrays, channels and slices. @@ -935,10 +935,10 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer //go:linkname sliceAppend runtime.sliceAppend func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen uintptr, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) -//go:linkname hashmapStringGet runtime.hashmapStringGetUnsafePointer +//go:linkname hashmapStringGet runtime.hashmapStringGet func hashmapStringGet(m unsafe.Pointer, key string, value unsafe.Pointer, valueSize uintptr) bool -//go:linkname hashmapStringSet runtime.hashmapStringSetUnsafePointer +//go:linkname hashmapStringSet runtime.hashmapStringSet func hashmapStringSet(m unsafe.Pointer, key string, value unsafe.Pointer) //go:linkname hashmapNewIterator runtime.hashmapNewIterator diff --git a/src/runtime/chan.go b/src/runtime/chan.go index 5bc05929dc..f8072486d4 100644 --- a/src/runtime/chan.go +++ b/src/runtime/chan.go @@ -148,12 +148,6 @@ func chanLen(c *channel) int { return int(c.bufUsed) } -// wrapper for use in reflect -func chanLenUnsafePointer(p unsafe.Pointer) int { - c := (*channel)(p) - return chanLen(c) -} - // Return the capacity of this chan, called from the cap builtin. // A nil chan is defined as having capacity 0. // @@ -165,12 +159,6 @@ func chanCap(c *channel) int { return int(c.bufSize) } -// wrapper for use in reflect -func chanCapUnsafePointer(p unsafe.Pointer) int { - c := (*channel)(p) - return chanCap(c) -} - // resumeRX resumes the next receiver and returns the destination pointer. // If the ok value is true, then the caller is expected to store a value into this pointer. func (ch *channel) resumeRX(ok bool) unsafe.Pointer { diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go index b67118d383..9165449ee2 100644 --- a/src/runtime/hashmap.go +++ b/src/runtime/hashmap.go @@ -147,12 +147,6 @@ func hashmapLen(m *hashmap) int { return int(m.count) } -// wrapper for use in reflect -func hashmapLenUnsafePointer(p unsafe.Pointer) int { - m := (*hashmap)(p) - return hashmapLen(m) -} - // Set a specified key to a given value. Grow the map if necessary. // //go:nobounds @@ -414,9 +408,6 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo } } -// for reflect -func hashmapNextUnsafePointer(m unsafe.Pointer, it unsafe.Pointer, key, value unsafe.Pointer) bool - // Hashmap with plain binary data keys (not containing strings etc.). func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) { if m == nil { @@ -467,11 +458,6 @@ func hashmapStringSet(m *hashmap, key string, value unsafe.Pointer) { hashmapSet(m, unsafe.Pointer(&key), value, hash) } -func hashmapStringSetUnsafePointer(p unsafe.Pointer, key string, value unsafe.Pointer) { - m := (*hashmap)(p) - hashmapStringSet(m, key, value) -} - func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize uintptr) bool { if m == nil { memzero(value, uintptr(valueSize)) @@ -481,11 +467,6 @@ func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize ui return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash) } -func hashmapStringGetUnsafePointer(p unsafe.Pointer, key string, value unsafe.Pointer, valueSize uintptr) bool { - m := (*hashmap)(p) - return hashmapStringGet(m, key, value, valueSize) -} - func hashmapStringDelete(m *hashmap, key string) { if m == nil { return From 7d6a4fc9a28f0dbac9e519974199717ac112d326 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Thu, 16 Feb 2023 12:32:55 -0800 Subject: [PATCH 10/47] reflect: add some slice reflect methods --- src/reflect/value.go | 76 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 5cb7c19c91..5c1bb3996e 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -217,7 +217,15 @@ func (v Value) CanAddr() bool { } func (v Value) Addr() Value { - panic("unimplemented: (reflect.Value).Addr()") + if !v.CanAddr() { + panic("taking addr of un-addr-able value") + } + + return Value{ + typecode: PtrTo(v.Type()).(*rawType), + value: unsafe.Pointer(&v.value), + flags: v.flags, + } } func (v Value) CanSet() bool { @@ -844,7 +852,15 @@ func (v Value) SetCap(n int) { } func (v Value) SetLen(n int) { - panic("unimplemented: (reflect.Value).SetLen()") + if v.typecode.Kind() != Slice { + panic("setlen: not slice") + } + + hdr := (*sliceHeader)(v.value) + if uintptr(n) > hdr.cap { + panic("setlen: cap too big") + } + hdr.len = uintptr(n) } func (v Value) checkAddressable() { @@ -853,8 +869,16 @@ func (v Value) checkAddressable() { } } +// OverflowInt reports whether the int64 x cannot be represented by v's type. +// It panics if v's Kind is not Int, Int8, Int16, Int32, or Int64. func (v Value) OverflowInt(x int64) bool { - panic("unimplemented: reflect.OverflowInt()") + switch v.Kind() { + case Int, Int8, Int16, Int32, Int64: + bitSize := v.typecode.Size() * 8 + trunc := (x << (64 - bitSize)) >> (64 - bitSize) + return x != trunc + } + panic(&ValueError{"reflect.Value.OverflowInt", v.Kind()}) } func (v Value) OverflowUint(x uint64) bool { @@ -866,7 +890,30 @@ func (v Value) Convert(t Type) Value { } func MakeSlice(typ Type, len, cap int) Value { - panic("unimplemented: reflect.MakeSlice()") + if typ.Kind() != Slice { + panic("reflect.MakeSlice of non-slice type") + } + if len < 0 { + panic("reflect.MakeSlice: negative len") + } + if cap < 0 { + panic("reflect.MakeSlice: negative cap") + } + if len > cap { + panic("reflect.MakeSlice: len > cap") + } + + var slice sliceHeader + slice.cap = uintptr(cap) + slice.len = uintptr(len) + slice.data = unsafe.Pointer(alloc(typ.Size()*uintptr(cap), nil)) + + return Value{ + typecode: typ.(*rawType), + value: unsafe.Pointer(&slice), + // flags: valueFlagIndirect, + } + } func Zero(typ Type) Value { @@ -947,10 +994,29 @@ func hashmapNewIterator() unsafe.Pointer //go:linkname hashmapNext runtime.hashmapNext func hashmapNext(m unsafe.Pointer, it unsafe.Pointer, key, value unsafe.Pointer) bool +//go:linkname sliceCopy runtime.sliceCopy +func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr) int + // Copy copies the contents of src into dst until either // dst has been filled or src has been exhausted. func Copy(dst, src Value) int { - panic("unimplemented: reflect.Copy()") + + if dst.typecode.Kind() != Slice { + panic("dst not slice " + dst.typecode.Kind().String()) + } + + if src.typecode.Kind() != Slice { + panic("src not slice") + } + + if dst.typecode != src.typecode { + panic("type mismatch") + } + + dhdr := (*sliceHeader)(dst.value) + shdr := (*sliceHeader)(src.value) + + return sliceCopy(dhdr.data, shdr.data, dhdr.len, shdr.len, dst.typecode.Elem().Size()) } // Append appends the values x to a slice s and returns the resulting slice. From 607316f4de6e26c60ecba1ea30c346cce0c17929 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Thu, 16 Feb 2023 13:08:47 -0800 Subject: [PATCH 11/47] reflect: flesh out MakeMap for string keys --- src/reflect/value.go | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 5c1bb3996e..4e06bd8bed 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -1094,9 +1094,41 @@ func (v Value) FieldByName(name string) Value { panic("unimplemented: (reflect.Value).FieldByName()") } +//go:linkname hashmapMake runtime.hashmapMake +func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer + // MakeMap creates a new map with the specified type. func MakeMap(typ Type) Value { - panic("unimplemented: reflect.MakeMap()") + + const ( + hashmapAlgorithmBinary uint8 = iota + hashmapAlgorithmString + hashmapAlgorithmInterface + ) + + if typ.Kind() != Map { + panic("makemap: not map") + } + + key := typ.Key() + val := typ.Elem() + + var alg uint8 + + switch key.Kind() { + case String: + alg = hashmapAlgorithmString + default: + panic("makemap: unknown key type") + } + + m := hashmapMake(key.Size(), val.Size(), 0, alg) + + return Value{ + typecode: typ.(*rawType), + value: unsafe.Pointer(&m), + flags: 0, // what should the flags be + } } func (v Value) Call(in []Value) []Value { From bc0238ed2f58c1b646df14354338bda1836cb934 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 14:16:18 -0800 Subject: [PATCH 12/47] reflect: fix extra pointer in MakeMap --- src/reflect/value.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 4e06bd8bed..82ae6b10f1 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -1126,7 +1126,7 @@ func MakeMap(typ Type) Value { return Value{ typecode: typ.(*rawType), - value: unsafe.Pointer(&m), + value: unsafe.Pointer(m), flags: 0, // what should the flags be } } From 010e9ac9d5e4d917695567ade86694d2159990a6 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 14:16:58 -0800 Subject: [PATCH 13/47] reflect: add pointerto() that works with rawTypes --- src/reflect/type.go | 9 +++++++-- src/reflect/value.go | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/reflect/type.go b/src/reflect/type.go index aa76c9ea80..2be7ed2920 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -456,14 +456,19 @@ func TypeOf(i interface{}) Type { func PtrTo(t Type) Type { return PointerTo(t) } func PointerTo(t Type) Type { + return pointerTo(t.(*rawType)) +} + +func pointerTo(t *rawType) *rawType { switch t.Kind() { case Pointer: panic("reflect: cannot make **T type") case Struct: - return (*structType)(unsafe.Pointer(t.(*rawType))).ptrTo + return (*structType)(unsafe.Pointer(t)).ptrTo default: - return (*elemType)(unsafe.Pointer(t.(*rawType))).ptrTo + return (*elemType)(unsafe.Pointer(t)).ptrTo } + } func (t *rawType) String() string { diff --git a/src/reflect/value.go b/src/reflect/value.go index 82ae6b10f1..d34dc5d7fb 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -222,7 +222,7 @@ func (v Value) Addr() Value { } return Value{ - typecode: PtrTo(v.Type()).(*rawType), + typecode: pointerTo(v.typecode), value: unsafe.Pointer(&v.value), flags: v.flags, } @@ -924,7 +924,7 @@ func Zero(typ Type) Value { // new value of the given type. func New(typ Type) Value { return Value{ - typecode: PtrTo(typ).(*rawType), + typecode: pointerTo(typ.(*rawType)), value: alloc(typ.Size(), nil), flags: valueFlagExported, } From bb33d513da9aac00c67992efe90a40745069fdbf Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 14:59:08 -0800 Subject: [PATCH 14/47] reflect: preallocate keys slice in MapKeys() --- src/reflect/value.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index d34dc5d7fb..5cad6c71d8 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -658,7 +658,7 @@ func (v Value) MapKeys() []Value { return nil } - var keys []Value + keys := make([]Value, 0, v.Len()) it := hashmapNewIterator() k := New(v.typecode.Key()) From 89f8bb775e271a1e9f30ce6027ad40dd982d99b8 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 14:59:48 -0800 Subject: [PATCH 15/47] reflect: use pointer() for accessing map pointer --- src/reflect/value.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 5cad6c71d8..1129d2e9a3 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -664,7 +664,7 @@ func (v Value) MapKeys() []Value { k := New(v.typecode.Key()) e := New(v.typecode.Elem()) - for hashmapNext(v.value, it, k.value, e.value) { + for hashmapNext(v.pointer(), it, k.value, e.value) { keys = append(keys, k.Elem()) k = New(v.typecode.Key()) } @@ -687,7 +687,7 @@ func (v Value) MapIndex(key Value) Value { switch key.Kind() { case String: - hashmapStringGet(v.value, *(*string)(key.value), elem.value, elemType.Size()) + hashmapStringGet(v.pointer(), *(*string)(key.value), elem.value, elemType.Size()) return elem.Elem() default: panic("unimplemented: (reflect.Value).MapIndex()") @@ -739,7 +739,7 @@ func (it *MapIter) Value() Value { func (it *MapIter) Next() bool { it.valid = true - ok := hashmapNext(it.m.value, it.it, it.key.value, it.val.value) + ok := hashmapNext(it.m.pointer(), it.it, it.key.value, it.val.value) if !ok { it.done = true } From 09ee68666baa2af61ef547a2bbe2a52339131d71 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 15:00:06 -0800 Subject: [PATCH 16/47] reflect: set valueFlagIndirect for slices --- src/reflect/value.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 1129d2e9a3..a3fa1f3e64 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -911,7 +911,7 @@ func MakeSlice(typ Type, len, cap int) Value { return Value{ typecode: typ.(*rawType), value: unsafe.Pointer(&slice), - // flags: valueFlagIndirect, + flags: valueFlagIndirect, } } @@ -1127,7 +1127,7 @@ func MakeMap(typ Type) Value { return Value{ typecode: typ.(*rawType), value: unsafe.Pointer(m), - flags: 0, // what should the flags be + flags: 0, } } From 6ab842d68ec0e431a9e17e97a87fb0a998ea30a5 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 15:15:32 -0800 Subject: [PATCH 17/47] reflect: simplify MakeSlice() panic logic --- src/reflect/value.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index a3fa1f3e64..1a0362fb94 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -889,18 +889,16 @@ func (v Value) Convert(t Type) Value { panic("unimplemented: (reflect.Value).Convert()") } +//go:linkname slicePanic runtime.slicePanic +func slicePanic() + func MakeSlice(typ Type, len, cap int) Value { if typ.Kind() != Slice { panic("reflect.MakeSlice of non-slice type") } - if len < 0 { - panic("reflect.MakeSlice: negative len") - } - if cap < 0 { - panic("reflect.MakeSlice: negative cap") - } - if len > cap { - panic("reflect.MakeSlice: len > cap") + + if len < 0 || cap < 0 || len > cap || int(uintptr(len)) != len || int(uintptr(cap)) != cap { + slicePanic() } var slice sliceHeader From 72fcd2f5911b86bccf20bab8bf3de65155443570 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 15:35:21 -0800 Subject: [PATCH 18/47] reflect: add stubs for channel operations --- src/reflect/value.go | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 1a0362fb94..1b42158e52 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -1137,10 +1137,33 @@ func (v Value) MethodByName(name string) Value { panic("unimplemented: (reflect.Value).MethodByName()") } +func NewAt(typ Type, p unsafe.Pointer) Value { + panic("unimplemented: reflect.New()") +} + +type SelectDir int + +const ( + _ SelectDir = iota + SelectSend // case Chan <- Send + SelectRecv // case <-Chan: + SelectDefault // default +) + +type SelectCase struct { + Dir SelectDir // direction of case + Chan Value // channel to use (for send or receive) + Send Value // value to send (for send) +} + +func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) { + panic("unimplemented: reflect.Select") +} + func (v Value) Recv() (x Value, ok bool) { panic("unimplemented: (reflect.Value).Recv()") } -func NewAt(typ Type, p unsafe.Pointer) Value { - panic("unimplemented: reflect.New()") +func (v Value) Send(x Value) { + panic("unimplemented: reflect.Value.Send()") } From 368d9ba0b4391a55c51c5c711fe61839e2f8fe5b Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 16:41:08 -0800 Subject: [PATCH 19/47] reflect: use ValueError() in SetLen() --- src/reflect/value.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 1b42158e52..7da332bda5 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -853,7 +853,7 @@ func (v Value) SetCap(n int) { func (v Value) SetLen(n int) { if v.typecode.Kind() != Slice { - panic("setlen: not slice") + panic(&ValueError{"reflect.Value.SetLen", v.Kind()}) } hdr := (*sliceHeader)(v.value) From bc01c04b132b0dd950cfec3f3bd6563ff24145d0 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 17:06:11 -0800 Subject: [PATCH 20/47] reflect: fix panic message --- src/reflect/value.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 7da332bda5..4d6bc147c4 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -858,7 +858,7 @@ func (v Value) SetLen(n int) { hdr := (*sliceHeader)(v.value) if uintptr(n) > hdr.cap { - panic("setlen: cap too big") + panic("setlen: len too big") } hdr.len = uintptr(n) } From 3701efb245075bcc68b91045fe968d03bdbc8718 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 17:07:23 -0800 Subject: [PATCH 21/47] reflect: add OverflowFloat and OverflowUint --- src/reflect/value.go | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 4d6bc147c4..661c41abb6 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -644,8 +644,24 @@ func (v Value) NumMethod() int { return v.typecode.NumMethod() } +// OverflowFloat reports whether the float64 x cannot be represented by v's type. +// It panics if v's Kind is not Float32 or Float64. func (v Value) OverflowFloat(x float64) bool { - panic("unimplemented: (reflect.Value).OverflowFloat()") + k := v.Kind() + switch k { + case Float32: + return overflowFloat32(x) + case Float64: + return false + } + panic(&ValueError{"reflect.Value.OverflowFloat", v.Kind()}) +} + +func overflowFloat32(x float64) bool { + if x < 0 { + x = -x + } + return math.MaxFloat32 < x && x <= math.MaxFloat64 } func (v Value) MapKeys() []Value { @@ -881,8 +897,17 @@ func (v Value) OverflowInt(x int64) bool { panic(&ValueError{"reflect.Value.OverflowInt", v.Kind()}) } +// OverflowUint reports whether the uint64 x cannot be represented by v's type. +// It panics if v's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64. func (v Value) OverflowUint(x uint64) bool { - panic("unimplemented: reflect.OverflowUint()") + k := v.Kind() + switch k { + case Uint, Uintptr, Uint8, Uint16, Uint32, Uint64: + bitSize := v.typecode.Size() * 8 + trunc := (x << (64 - bitSize)) >> (64 - bitSize) + return x != trunc + } + panic(&ValueError{"reflect.Value.OverflowUint", v.Kind()}) } func (v Value) Convert(t Type) Value { From e125fb616fd779b388cb1ccf593b21e0e915e007 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 09:32:42 -0800 Subject: [PATCH 22/47] reflect: better setlen panic message --- src/reflect/value.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 661c41abb6..de35551d2d 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -874,7 +874,7 @@ func (v Value) SetLen(n int) { hdr := (*sliceHeader)(v.value) if uintptr(n) > hdr.cap { - panic("setlen: len too big") + panic("reflect.Value.SetLen: slice length out of range") } hdr.len = uintptr(n) } From 5392f1dff69ea0e669a85b578eeea599abf2cf5e Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 09:37:48 -0800 Subject: [PATCH 23/47] reflect: add binary map keys --- src/reflect/type.go | 23 +++++++++++++++++ src/reflect/value.go | 59 +++++++++++++++++++++++++++++++++----------- 2 files changed, 67 insertions(+), 15 deletions(-) diff --git a/src/reflect/type.go b/src/reflect/type.go index 2be7ed2920..d634536f95 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -799,6 +799,29 @@ func (t *rawType) Comparable() bool { } } +// isbinary() returns if the hashmapAlgorithmBinary functions can be used on this type +func (t *rawType) isBinary() bool { + switch t.Kind() { + case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: + return true + case Float32, Float64, Complex64, Complex128: + return true + case Pointer: + return true + case Array: + return t.elem().isBinary() + case Struct: + numField := t.NumField() + for i := 0; i < numField; i++ { + if !t.rawField(i).Type.isBinary() { + return false + } + } + return true + } + return false +} + func (t rawType) ChanDir() ChanDir { panic("unimplemented: (reflect.Type).ChanDir()") } diff --git a/src/reflect/value.go b/src/reflect/value.go index de35551d2d..20be2787f4 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -701,13 +701,21 @@ func (v Value) MapIndex(key Value) Value { elemType := v.Type().Elem() elem := New(elemType) - switch key.Kind() { - case String: + if key.Kind() == String { hashmapStringGet(v.pointer(), *(*string)(key.value), elem.value, elemType.Size()) return elem.Elem() - default: - panic("unimplemented: (reflect.Value).MapIndex()") + } else if key.typecode.isBinary() { + var keyptr unsafe.Pointer + if key.isIndirect() { + keyptr = key.value + } else { + keyptr = unsafe.Pointer(&key.value) + } + hashmapBinaryGet(v.pointer(), keyptr, elem.value, elemType.Size()) + return elem.Elem() } + + panic("unimplemented: (reflect.Value).MapIndex()") } func (v Value) MapRange() *MapIter { @@ -1011,6 +1019,12 @@ func hashmapStringGet(m unsafe.Pointer, key string, value unsafe.Pointer, valueS //go:linkname hashmapStringSet runtime.hashmapStringSet func hashmapStringSet(m unsafe.Pointer, key string, value unsafe.Pointer) +//go:linkname hashmapBinaryGet runtime.hashmapBinaryGet +func hashmapBinaryGet(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool + +//go:linkname hashmapBinarySet runtime.hashmapBinarySet +func hashmapBinarySet(m unsafe.Pointer, key, value unsafe.Pointer) + //go:linkname hashmapNewIterator runtime.hashmapNewIterator func hashmapNewIterator() unsafe.Pointer @@ -1090,15 +1104,30 @@ func (v Value) SetMapIndex(key, elem Value) { panic(&ValueError{Method: "MapIndex"}) } - switch key.Kind() { - case String: + if key.Kind() == String { if elem.isIndirect() { hashmapStringSet(v.value, *(*string)(key.value), elem.value) } else { hashmapStringSet(v.value, *(*string)(key.value), (unsafe.Pointer)(&elem.value)) } - default: + } else if key.typecode.isBinary() { + var keyptr unsafe.Pointer + if key.isIndirect() { + keyptr = key.value + } else { + keyptr = unsafe.Pointer(&key.value) + } + + var elemptr unsafe.Pointer + if elem.isIndirect() { + elemptr = elem.value + } else { + elemptr = unsafe.Pointer(&elem.value) + } + + hashmapBinarySet(v.pointer(), keyptr, elemptr) + } else { panic("unimplemented: (reflect.Value).MapIndex()") } } @@ -1122,7 +1151,6 @@ func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe // MakeMap creates a new map with the specified type. func MakeMap(typ Type) Value { - const ( hashmapAlgorithmBinary uint8 = iota hashmapAlgorithmString @@ -1130,19 +1158,20 @@ func MakeMap(typ Type) Value { ) if typ.Kind() != Map { - panic("makemap: not map") + panic(&ValueError{"MakeMap", typ.Kind()}) } - key := typ.Key() - val := typ.Elem() + key := typ.Key().(*rawType) + val := typ.Elem().(*rawType) var alg uint8 - switch key.Kind() { - case String: + if key.Kind() == String { alg = hashmapAlgorithmString - default: - panic("makemap: unknown key type") + } else if key.isBinary() { + alg = hashmapAlgorithmBinary + } else { + panic("reflect.MakeMap: invalid key type") } m := hashmapMake(key.Size(), val.Size(), 0, alg) From 1be78f51d31effd7859ad9433810bdc172743e9b Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 11:41:01 -0800 Subject: [PATCH 24/47] compiler,reflect: add Name() support --- compiler/interface.go | 15 +++++++++++++-- src/reflect/type.go | 19 ++++++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/compiler/interface.go b/compiler/interface.go index 097eadcf4f..eb8003bf93 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -112,6 +112,8 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value { typeFieldTypes = append(typeFieldTypes, types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]), types.NewVar(token.NoPos, nil, "underlying", types.Typ[types.UnsafePointer]), + types.NewVar(token.NoPos, nil, "len", types.Typ[types.Uintptr]), + types.NewVar(token.NoPos, nil, "name", types.NewArray(types.Typ[types.Int8], int64(len(typ.Obj().Name())))), ) case *types.Chan, *types.Slice: typeFieldTypes = append(typeFieldTypes, @@ -169,9 +171,18 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value { case *types.Basic: typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))} case *types.Named: + name := typ.Obj().Name() + var buf []llvm.Value + + for _, b := range []byte(name) { + buf = append(buf, llvm.ConstInt(c.ctx.Int8Type(), uint64(b), false)) + } + typeFields = []llvm.Value{ - c.getTypeCode(types.NewPointer(typ)), // ptrTo - c.getTypeCode(typ.Underlying()), // underlying + c.getTypeCode(types.NewPointer(typ)), // ptrTo + c.getTypeCode(typ.Underlying()), // underlying + llvm.ConstInt(c.uintptrType, uint64(len(name)), false), // length + llvm.ConstArray(c.ctx.Int8Type(), buf), } metabyte |= 1 << 5 // "named" flag case *types.Chan: diff --git a/src/reflect/type.go b/src/reflect/type.go index d634536f95..256a6e1247 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -417,6 +417,14 @@ type mapType struct { key *rawType } +type namedType struct { + rawType + ptrTo *rawType + underlying *rawType + nlen uintptr + name [1]byte +} + // Type for struct types. The numField value is intentionally put before ptrTo // for better struct packing on 32-bit and 64-bit architectures. On these // architectures, the ptrTo field still has the same offset as in all the other @@ -847,7 +855,16 @@ func (t *rawType) NumMethod() int { } func (t *rawType) Name() string { - return "unimplemented: (reflect.Type).Name()" + if t.isNamed() { + ntype := (*namedType)(unsafe.Pointer(t)) + name := stringHeader{ + data: unsafe.Pointer(&ntype.name), + len: ntype.nlen, + } + return *(*string)(unsafe.Pointer(&name)) + } + + return t.Kind().String() } func (t *rawType) Key() Type { From 1c43d8ce1a468a2c20d6fa9c145fbbb9cfbe69a3 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 12:41:14 -0800 Subject: [PATCH 25/47] reflect: add FieldByName and FieldByIndex --- src/reflect/type.go | 48 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/reflect/type.go b/src/reflect/type.go index 256a6e1247..bbcd4c9c6b 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -891,12 +891,52 @@ func (t rawType) PkgPath() string { panic("unimplemented: (reflect.Type).PkgPath()") } -func (t rawType) FieldByName(name string) (StructField, bool) { - panic("unimplemented: (reflect.Type).FieldByName()") +func (t *rawType) FieldByName(name string) (StructField, bool) { + if t.Kind() != Struct { + panic(TypeError{"FieldByName"}) + } + + numField := t.NumField() + + // This is incredibly inefficient + for i := 0; i < numField; i++ { + field := t.rawField(i) + if field.Name == name { + return StructField{ + Name: field.Name, + PkgPath: field.PkgPath, + Type: field.Type, // note: converts rawType to Type + Tag: field.Tag, + Anonymous: field.Anonymous, + Offset: field.Offset, + }, true + } + } + + return StructField{}, false } -func (t rawType) FieldByIndex(index []int) StructField { - panic("unimplemented: (reflect.Type).FieldByIndex()") +func (t *rawType) FieldByIndex(index []int) StructField { + ftype := t + var field rawStructField + + for _, n := range index { + if ftype.Kind() != Struct { + panic(TypeError{"FieldByIndex"}) + } + + field = ftype.rawField(n) + ftype = field.Type + } + + return StructField{ + Name: field.Name, + PkgPath: field.PkgPath, + Type: field.Type, // note: converts rawType to Type + Tag: field.Tag, + Anonymous: field.Anonymous, + Offset: field.Offset, + } } // A StructField describes a single field in a struct. From 2a3d1439669614e7bba56e5b7480b5c002dced3d Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 13:34:54 -0800 Subject: [PATCH 26/47] reflect,runtime: add LLVM14 unsafe.Pointer compat wrappers --- src/reflect/value.go | 18 +++++++++--------- src/runtime/chan.go | 8 ++++++++ src/runtime/hashmap.go | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 20be2787f4..6204ef34da 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -397,10 +397,10 @@ func (v Value) Slice3(i, j, k int) Value { panic("unimplemented: (reflect.Value).Slice3()") } -//go:linkname maplen runtime.hashmapLen +//go:linkname maplen runtime.hashmapLenUnsafePtr func maplen(p unsafe.Pointer) int -//go:linkname chanlen runtime.chanLen +//go:linkname chanlen runtime.chanLenUnsafePtr func chanlen(p unsafe.Pointer) int // Len returns the length of this value for slices, strings, arrays, channels, @@ -422,7 +422,7 @@ func (v Value) Len() int { } } -//go:linkname chancap runtime.chanCap +//go:linkname chancap runtime.chanCapUnsafePtr func chancap(p unsafe.Pointer) int // Cap returns the capacity of this value for arrays, channels and slices. @@ -1013,22 +1013,22 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer //go:linkname sliceAppend runtime.sliceAppend func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen uintptr, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) -//go:linkname hashmapStringGet runtime.hashmapStringGet +//go:linkname hashmapStringGet runtime.hashmapStringGetUnsafePtr func hashmapStringGet(m unsafe.Pointer, key string, value unsafe.Pointer, valueSize uintptr) bool -//go:linkname hashmapStringSet runtime.hashmapStringSet +//go:linkname hashmapStringSet runtime.hashmapStringSetUnsafePtr func hashmapStringSet(m unsafe.Pointer, key string, value unsafe.Pointer) -//go:linkname hashmapBinaryGet runtime.hashmapBinaryGet +//go:linkname hashmapBinaryGet runtime.hashmapBinaryGetUnsafePtr func hashmapBinaryGet(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool -//go:linkname hashmapBinarySet runtime.hashmapBinarySet +//go:linkname hashmapBinarySet runtime.hashmapBinarySetUnsafePtr func hashmapBinarySet(m unsafe.Pointer, key, value unsafe.Pointer) //go:linkname hashmapNewIterator runtime.hashmapNewIterator func hashmapNewIterator() unsafe.Pointer -//go:linkname hashmapNext runtime.hashmapNext +//go:linkname hashmapNext runtime.hashmapNextUnsafePtr func hashmapNext(m unsafe.Pointer, it unsafe.Pointer, key, value unsafe.Pointer) bool //go:linkname sliceCopy runtime.sliceCopy @@ -1146,7 +1146,7 @@ func (v Value) FieldByName(name string) Value { panic("unimplemented: (reflect.Value).FieldByName()") } -//go:linkname hashmapMake runtime.hashmapMake +//go:linkname hashmapMake runtime.hashmapMakeUnsafePtr func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer // MakeMap creates a new map with the specified type. diff --git a/src/runtime/chan.go b/src/runtime/chan.go index f8072486d4..511f0ab80f 100644 --- a/src/runtime/chan.go +++ b/src/runtime/chan.go @@ -148,6 +148,10 @@ func chanLen(c *channel) int { return int(c.bufUsed) } +func chanLenUnsafePtr(c unsafe.Pointer) int { + return chanLen((*channel)(c)) +} + // Return the capacity of this chan, called from the cap builtin. // A nil chan is defined as having capacity 0. // @@ -159,6 +163,10 @@ func chanCap(c *channel) int { return int(c.bufSize) } +func chanCapUnsafePtr(c unsafe.Pointer) int { + return chanCap((*channel)(c)) +} + // resumeRX resumes the next receiver and returns the destination pointer. // If the ok value is true, then the caller is expected to store a value into this pointer. func (ch *channel) resumeRX(ok bool) unsafe.Pointer { diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go index 9165449ee2..ad13250104 100644 --- a/src/runtime/hashmap.go +++ b/src/runtime/hashmap.go @@ -89,6 +89,10 @@ func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) *hashm } } +func hashmapMakeUnsafePtr(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer { + return (unsafe.Pointer)(hashmapMake(keySize, valueSize, sizeHint, alg)) +} + func hashmapKeyEqualAlg(alg hashmapAlgorithm) func(x, y unsafe.Pointer, n uintptr) bool { switch alg { case hashmapAlgorithmBinary: @@ -147,6 +151,10 @@ func hashmapLen(m *hashmap) int { return int(m.count) } +func hashmapLenUnsafePtr(m unsafe.Pointer) int { + return hashmapLen((*hashmap)(m)) +} + // Set a specified key to a given value. Grow the map if necessary. // //go:nobounds @@ -207,6 +215,10 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3 *emptySlotTophash = tophash } +func hashmapSetUnsafePtr(m unsafe.Pointer, key unsafe.Pointer, value unsafe.Pointer, hash uint32) { + hashmapSet((*hashmap)(m), key, value, hash) +} + // hashmapInsertIntoNewBucket creates a new bucket, inserts the given key and // value into the bucket, and returns a pointer to this bucket. func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash uint8) *hashmapBucket { @@ -298,6 +310,10 @@ func hashmapGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr, hash u return false } +func hashmapGetUnsafePtr(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr, hash uint32) bool { + return hashmapGet((*hashmap)(m), key, value, valueSize, hash) +} + // Delete a given key from the map. No-op when the key does not exist in the // map. // @@ -408,6 +424,10 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo } } +func hashmapNextUnsafePtr(m unsafe.Pointer, it unsafe.Pointer, key, value unsafe.Pointer) bool { + return hashmapNext((*hashmap)(m), (*hashmapIterator)(it), key, value) +} + // Hashmap with plain binary data keys (not containing strings etc.). func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) { if m == nil { @@ -417,6 +437,10 @@ func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) { hashmapSet(m, key, value, hash) } +func hashmapBinarySetUnsafePtr(m unsafe.Pointer, key, value unsafe.Pointer) { + hashmapBinarySet((*hashmap)(m), key, value) +} + func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr) bool { if m == nil { memzero(value, uintptr(valueSize)) @@ -426,6 +450,10 @@ func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr) return hashmapGet(m, key, value, valueSize, hash) } +func hashmapBinaryGetUnsafePtr(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool { + return hashmapBinaryGet((*hashmap)(m), key, value, valueSize) +} + func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) { if m == nil { return @@ -458,6 +486,10 @@ func hashmapStringSet(m *hashmap, key string, value unsafe.Pointer) { hashmapSet(m, unsafe.Pointer(&key), value, hash) } +func hashmapStringSetUnsafePtr(m unsafe.Pointer, key string, value unsafe.Pointer) { + hashmapStringSet((*hashmap)(m), key, value) +} + func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize uintptr) bool { if m == nil { memzero(value, uintptr(valueSize)) @@ -467,6 +499,10 @@ func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize ui return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash) } +func hashmapStringGetUnsafePtr(m unsafe.Pointer, key string, value unsafe.Pointer, valueSize uintptr) bool { + return hashmapStringGet((*hashmap)(m), key, value, valueSize) +} + func hashmapStringDelete(m *hashmap, key string) { if m == nil { return From 2f401a7b199cff657de3eb9272f85be563ed28da Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 13:53:45 -0800 Subject: [PATCH 27/47] reflect, runtime: a zero reflect.Value in SetMapIndex means delete() --- src/reflect/value.go | 38 ++++++++++++++++++++++++++++---------- src/runtime/hashmap.go | 8 ++++++++ 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 6204ef34da..0be5ed4249 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -1090,6 +1090,12 @@ func AppendSlice(s, t Value) Value { } } +//go:linkname hashmapStringDelete runtime.hashmapStringDeleteUnsafePtr +func hashmapStringDelete(m unsafe.Pointer, key string) + +//go:linkname hashmapBinaryDelete runtime.hashmapBinaryDeleteUnsafePtr +func hashmapBinaryDelete(m unsafe.Pointer, key unsafe.Pointer) + func (v Value) SetMapIndex(key, elem Value) { if v.Kind() != Map { panic(&ValueError{Method: "MapIndex", Kind: v.Kind()}) @@ -1100,15 +1106,24 @@ func (v Value) SetMapIndex(key, elem Value) { panic(&ValueError{Method: "MapIndex"}) } - if elem.typecode != v.typecode.elem() { + // if elem is the zero Value, it means delete + del := elem == Value{} + + if !del && elem.typecode != v.typecode.elem() { panic(&ValueError{Method: "MapIndex"}) } if key.Kind() == String { - if elem.isIndirect() { - hashmapStringSet(v.value, *(*string)(key.value), elem.value) + if del { + hashmapStringDelete(v.pointer(), *(*string)(key.value)) } else { - hashmapStringSet(v.value, *(*string)(key.value), (unsafe.Pointer)(&elem.value)) + var elemptr unsafe.Pointer + if elem.isIndirect() { + elemptr = elem.value + } else { + elemptr = unsafe.Pointer(&elem.value) + } + hashmapStringSet(v.pointer(), *(*string)(key.value), elemptr) } } else if key.typecode.isBinary() { @@ -1119,14 +1134,17 @@ func (v Value) SetMapIndex(key, elem Value) { keyptr = unsafe.Pointer(&key.value) } - var elemptr unsafe.Pointer - if elem.isIndirect() { - elemptr = elem.value + if del { + hashmapBinaryDelete(v.pointer(), keyptr) } else { - elemptr = unsafe.Pointer(&elem.value) + var elemptr unsafe.Pointer + if elem.isIndirect() { + elemptr = elem.value + } else { + elemptr = unsafe.Pointer(&elem.value) + } + hashmapBinarySet(v.pointer(), keyptr, elemptr) } - - hashmapBinarySet(v.pointer(), keyptr, elemptr) } else { panic("unimplemented: (reflect.Value).MapIndex()") } diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go index ad13250104..9f41542fe8 100644 --- a/src/runtime/hashmap.go +++ b/src/runtime/hashmap.go @@ -462,6 +462,10 @@ func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) { hashmapDelete(m, key, hash) } +func hashmapBinaryDeleteUnsafePtr(m unsafe.Pointer, key unsafe.Pointer) { + hashmapBinaryDelete((*hashmap)(m), key) +} + // Hashmap with string keys (a common case). func hashmapStringEqual(x, y unsafe.Pointer, n uintptr) bool { @@ -511,6 +515,10 @@ func hashmapStringDelete(m *hashmap, key string) { hashmapDelete(m, unsafe.Pointer(&key), hash) } +func hashmapStringDeleteUnsafePtr(m unsafe.Pointer, key string) { + hashmapStringDelete((*hashmap)(m), key) +} + // Hashmap with interface keys (for everything else). // This is a method that is intentionally unexported in the reflect package. It From a3fd40effc56b89073b77ee5f5117f79e0aa73a8 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 17:40:27 -0800 Subject: [PATCH 28/47] reflect: MapIndex returns Value{} if no key found --- src/reflect/value.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 0be5ed4249..ab70df97e8 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -702,7 +702,9 @@ func (v Value) MapIndex(key Value) Value { elem := New(elemType) if key.Kind() == String { - hashmapStringGet(v.pointer(), *(*string)(key.value), elem.value, elemType.Size()) + if ok := hashmapStringGet(v.pointer(), *(*string)(key.value), elem.value, elemType.Size()); !ok { + return Value{} + } return elem.Elem() } else if key.typecode.isBinary() { var keyptr unsafe.Pointer @@ -711,7 +713,9 @@ func (v Value) MapIndex(key Value) Value { } else { keyptr = unsafe.Pointer(&key.value) } - hashmapBinaryGet(v.pointer(), keyptr, elem.value, elemType.Size()) + if ok := hashmapBinaryGet(v.pointer(), keyptr, elem.value, elemType.Size()); !ok { + return Value{} + } return elem.Elem() } From 909208d7b1582f3f53c274c66bda846bf7bdd31c Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 17:40:58 -0800 Subject: [PATCH 29/47] reflect: fix SetMapIndex panic messages --- src/reflect/value.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index ab70df97e8..19b5e68eae 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -1102,19 +1102,19 @@ func hashmapBinaryDelete(m unsafe.Pointer, key unsafe.Pointer) func (v Value) SetMapIndex(key, elem Value) { if v.Kind() != Map { - panic(&ValueError{Method: "MapIndex", Kind: v.Kind()}) + panic(&ValueError{Method: "SetMapIndex", Kind: v.Kind()}) } // compare key type with actual key type of map if key.typecode != v.typecode.key() { - panic(&ValueError{Method: "MapIndex"}) + panic("reflect.Value.SetMapIndex: incompatible types for key") } // if elem is the zero Value, it means delete del := elem == Value{} if !del && elem.typecode != v.typecode.elem() { - panic(&ValueError{Method: "MapIndex"}) + panic("reflect.Value.SetMapIndex: incompatible types for value") } if key.Kind() == String { From d8051549dc1866b4f5af068f51a783c5f06ab313 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 17:43:03 -0800 Subject: [PATCH 30/47] reflect: add unit test to cover all the new goodies --- src/reflect/value_test.go | 163 +++++++++++++++++++++++++++++++++++++- 1 file changed, 159 insertions(+), 4 deletions(-) diff --git a/src/reflect/value_test.go b/src/reflect/value_test.go index 5698ede557..78503aaa41 100644 --- a/src/reflect/value_test.go +++ b/src/reflect/value_test.go @@ -1,7 +1,8 @@ package reflect_test import ( - . "reflect" + "reflect" + "sort" "testing" ) @@ -11,7 +12,7 @@ func TestIndirectPointers(t *testing.T) { var a = &m - if ValueOf(a).Elem().Len() != 1 { + if reflect.ValueOf(a).Elem().Len() != 1 { t.Errorf("bad map length via reflect") } @@ -19,14 +20,168 @@ func TestIndirectPointers(t *testing.T) { Decoded *[3]byte } - v1 := New(TypeOf(b.Decoded).Elem()) + v1 := reflect.New(reflect.TypeOf(b.Decoded).Elem()) var bb [3]byte bb[0] = 0xaa - v1.Elem().Set(ValueOf(bb)) + v1.Elem().Set(reflect.ValueOf(bb)) if v1.Elem().Index(0).Uint() != 0xaa { t.Errorf("bad indirect array index via reflect") } } + +func TestReflectMap(t *testing.T) { + + m := make(map[string]int) + m["two"] = 2 + v := reflect.ValueOf(m) + k := reflect.ValueOf("two") + + if got, want := v.MapIndex(k).Interface().(int), m["two"]; got != want { + t.Errorf(`MapIndex("two")=%v, want %v`, got, want) + + } + + m["two"] = 3 + v.SetMapIndex(k, reflect.ValueOf(3)) + if got, want := v.MapIndex(k).Interface().(int), m["two"]; got != want { + t.Errorf(`MapIndex("two")=%v, want %v`, got, want) + } + + mapT := reflect.TypeOf(m) + + if kstr, estr := mapT.Key().String(), mapT.Elem().String(); kstr != "string" || estr != "int" { + t.Errorf("mapT.Key()=%v, mapT.Elem()=%v", kstr, estr) + } + + m["four"] = 4 + m["five"] = 5 + m["seven"] = 7 + + // delete m["two"] + v.SetMapIndex(reflect.ValueOf("two"), reflect.Value{}) + if got, want := v.MapIndex(k), (reflect.Value{}); got != want { + t.Errorf(`MapIndex("two")=%v, want %v`, got, want) + } + + keys := v.MapKeys() + var kstrs []string + for _, k := range keys { + kstrs = append(kstrs, k.Interface().(string)) + } + + sort.Strings(kstrs) + want := []string{"five", "four", "seven"} + if !equal(kstrs, want) { + t.Errorf("keys=%v, want=%v", kstrs, want) + } + + // integer keys / string values + intm := map[int]string{ + 10: "ten", + 20: "twenty", + } + + intv := reflect.ValueOf(intm) + if got, want := intv.MapIndex(reflect.ValueOf(10)).Interface().(string), intm[10]; got != want { + t.Errorf(`MapIndex(10)=%v, want %v`, got, want) + } + + intv.SetMapIndex(reflect.ValueOf(10), reflect.Value{}) + if got, want := intv.MapIndex(reflect.ValueOf(10)), (reflect.Value{}); got != want { + t.Errorf(`MapIndex(10)=%v, want %v`, got, want) + } + + intv.SetMapIndex(reflect.ValueOf(30), reflect.ValueOf("thirty")) + + keys = intv.MapKeys() + var kints []int + for _, k := range keys { + kints = append(kints, k.Interface().(int)) + } + + sort.Ints(kints) + wantints := []int{20, 30} + if !equal(kints, wantints) { + t.Errorf("keys=%v, want=%v", kints, wantints) + } + + intm2 := make(map[int]string) + it := intv.MapRange() + for it.Next() { + intm2[it.Key().Interface().(int)] = it.Value().Interface().(string) + } + + if !reflect.DeepEqual(intm2, intm) { + t.Errorf("intm2 != intm") + } + + maptype := reflect.TypeOf(m) + refmap := reflect.MakeMap(maptype) + refmap.SetMapIndex(reflect.ValueOf("six"), reflect.ValueOf(6)) + six := refmap.MapIndex(reflect.ValueOf("six")) + + if six.Interface().(int) != 6 { + t.Errorf("MakeMap map lookup 6 = %v", six) + } + +} + +func TestNamedTypes(t *testing.T) { + type namedString string + + named := namedString("foo") + if got, want := reflect.TypeOf(named).Name(), "namedString"; got != want { + t.Errorf("TypeOf.Name()=%v, want %v", got, want) + } + +} + +func TestStruct(t *testing.T) { + type barStruct struct { + QuxString string + BazInt int + } + + type foobar struct { + Foo string + Bar barStruct + } + + var fb foobar + fb.Bar.QuxString = "qux" + + reffb := reflect.TypeOf(fb) + + q := reffb.FieldByIndex([]int{1, 0}) + if want := "QuxString"; q.Name != want { + t.Errorf("FieldByIndex=%v, want %v", q.Name, want) + } + + var ok bool + q, ok = reffb.FieldByName("Foo") + if q.Name != "Foo" || !ok { + t.Errorf("FieldByName(Foo)=%v,%v, want Foo, true") + } + + q, ok = reffb.FieldByName("Snorble") + if q.Name != "" || ok { + t.Errorf("FieldByName(Snorble)=%v,%v, want ``, false") + } + +} + +func equal[T comparable](a, b []T) bool { + if len(a) != len(b) { + return false + } + + for i, aa := range a { + if b[i] != aa { + return false + } + } + return true +} From 462b3e2e3105a1e6003d095c4d3d134c012c775b Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 17:45:08 -0800 Subject: [PATCH 31/47] compiler: update unit tests golden output --- compiler/testdata/interface.ll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/testdata/interface.ll b/compiler/testdata/interface.ll index 55f189cdf2..7b44122ffe 100644 --- a/compiler/testdata/interface.ll +++ b/compiler/testdata/interface.ll @@ -9,7 +9,7 @@ target triple = "wasm32-unknown-wasi" @"reflect/types.type:basic:int" = linkonce_odr constant { i8, ptr } { i8 2, ptr @"reflect/types.type:pointer:basic:int" }, align 4 @"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:basic:int" }, align 4 @"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:named:error" }, align 4 -@"reflect/types.type:named:error" = linkonce_odr constant { i8, ptr, ptr } { i8 52, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4 +@"reflect/types.type:named:error" = linkonce_odr constant { i8, ptr, ptr, i32, [5 x i8] } { i8 52, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 5, [5 x i8] c"error" }, align 4 @"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 20, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }, align 4 @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4 @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}" }, align 4 From e56e57090cf41bd2f0cd6bdcca4fef09bd366f57 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 18:11:46 -0800 Subject: [PATCH 32/47] reflect: uncomment a bunch of deep equal tests that now pass --- src/reflect/all_test.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index a2dc268a30..f85dba27b3 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -71,7 +71,7 @@ var deepEqualTests = []DeepEqualTest{ {&[3]int{1, 2, 3}, &[3]int{1, 2, 3}, true}, {Basic{1, 0.5}, Basic{1, 0.5}, true}, {error(nil), error(nil), true}, - //{map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, true}, + {map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, true}, {fn1, fn2, true}, {[]byte{1, 2, 3}, []byte{1, 2, 3}, true}, {[]MyByte{1, 2, 3}, []MyByte{1, 2, 3}, true}, @@ -87,10 +87,10 @@ var deepEqualTests = []DeepEqualTest{ {&[3]int{1, 2, 3}, &[3]int{1, 2, 4}, false}, {Basic{1, 0.5}, Basic{1, 0.6}, false}, {Basic{1, 0}, Basic{2, 0}, false}, - //{map[int]string{1: "one", 3: "two"}, map[int]string{2: "two", 1: "one"}, false}, - //{map[int]string{1: "one", 2: "txo"}, map[int]string{2: "two", 1: "one"}, false}, - //{map[int]string{1: "one"}, map[int]string{2: "two", 1: "one"}, false}, - //{map[int]string{2: "two", 1: "one"}, map[int]string{1: "one"}, false}, + {map[int]string{1: "one", 3: "two"}, map[int]string{2: "two", 1: "one"}, false}, + {map[int]string{1: "one", 2: "txo"}, map[int]string{2: "two", 1: "one"}, false}, + {map[int]string{1: "one"}, map[int]string{2: "two", 1: "one"}, false}, + {map[int]string{2: "two", 1: "one"}, map[int]string{1: "one"}, false}, {nil, 1, false}, {1, nil, false}, {fn1, fn3, false}, @@ -104,16 +104,16 @@ var deepEqualTests = []DeepEqualTest{ {&[1]float64{math.NaN()}, self{}, true}, {[]float64{math.NaN()}, []float64{math.NaN()}, false}, {[]float64{math.NaN()}, self{}, true}, - //{map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false}, - //{map[float64]float64{math.NaN(): 1}, self{}, true}, + {map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false}, + {map[float64]float64{math.NaN(): 1}, self{}, true}, // Nil vs empty: not the same. {[]int{}, []int(nil), false}, {[]int{}, []int{}, true}, {[]int(nil), []int(nil), true}, - //{map[int]int{}, map[int]int(nil), false}, - //{map[int]int{}, map[int]int{}, true}, - //{map[int]int(nil), map[int]int(nil), true}, + {map[int]int{}, map[int]int(nil), false}, + {map[int]int{}, map[int]int{}, true}, + {map[int]int(nil), map[int]int(nil), true}, // Mismatched types {1, 1.0, false}, @@ -130,8 +130,8 @@ var deepEqualTests = []DeepEqualTest{ // Possible loops. {&loopy1, &loopy1, true}, {&loopy1, &loopy2, true}, - //{&cycleMap1, &cycleMap2, true}, - //{&cycleMap1, &cycleMap3, false}, + {&cycleMap1, &cycleMap2, true}, + {&cycleMap1, &cycleMap3, false}, } func TestDeepEqual(t *testing.T) { From 6b903a1619e9f5e72b5ad33c613bdf9b96e8b33b Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 18:12:09 -0800 Subject: [PATCH 33/47] reflect: todo++ to handle undef padding bytes --- src/reflect/value.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/reflect/value.go b/src/reflect/value.go index 19b5e68eae..463657fc4e 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -713,6 +713,7 @@ func (v Value) MapIndex(key Value) Value { } else { keyptr = unsafe.Pointer(&key.value) } + //TODO(dgryski): zero out padding bytes in key, if any if ok := hashmapBinaryGet(v.pointer(), keyptr, elem.value, elemType.Size()); !ok { return Value{} } From 8e433c98903a77fd7af7d8be792d92452c6dc255 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 22 Feb 2023 16:07:53 -0800 Subject: [PATCH 34/47] targets: bump default-stack-size so testdata/json.go runs --- targets/cortex-m.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/cortex-m.json b/targets/cortex-m.json index 7b1f751138..5760560192 100644 --- a/targets/cortex-m.json +++ b/targets/cortex-m.json @@ -8,7 +8,7 @@ "rtlib": "compiler-rt", "libc": "picolibc", "automatic-stack-size": true, - "default-stack-size": 2048, + "default-stack-size": 4096, "cflags": [ "-Werror", "-fshort-enums", From 6b6bc95feb28cbc7c447348b55200e308ae0d4cb Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 22 Feb 2023 17:52:47 -0800 Subject: [PATCH 35/47] reflect: add MakeMapWithSize and fix MakeMap size hint --- src/reflect/value.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 463657fc4e..3172ea2ba8 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -1172,8 +1172,10 @@ func (v Value) FieldByName(name string) Value { //go:linkname hashmapMake runtime.hashmapMakeUnsafePtr func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer -// MakeMap creates a new map with the specified type. -func MakeMap(typ Type) Value { +// MakeMapWithSize creates a new map with the specified type and initial space +// for approximately n elements. +func MakeMapWithSize(typ Type, n int) Value { + const ( hashmapAlgorithmBinary uint8 = iota hashmapAlgorithmString @@ -1184,6 +1186,10 @@ func MakeMap(typ Type) Value { panic(&ValueError{"MakeMap", typ.Kind()}) } + if n < 0 { + panic("reflect.MakeMapWithSize: negative size hint") + } + key := typ.Key().(*rawType) val := typ.Elem().(*rawType) @@ -1197,7 +1203,7 @@ func MakeMap(typ Type) Value { panic("reflect.MakeMap: invalid key type") } - m := hashmapMake(key.Size(), val.Size(), 0, alg) + m := hashmapMake(key.Size(), val.Size(), uintptr(n), alg) return Value{ typecode: typ.(*rawType), @@ -1206,6 +1212,11 @@ func MakeMap(typ Type) Value { } } +// MakeMap creates a new map with the specified type. +func MakeMap(typ Type) Value { + return MakeMapWithSize(typ, 8) +} + func (v Value) Call(in []Value) []Value { panic("unimplemented: (reflect.Value).Call()") } From 11fce44e3a78439c49e2ac3de93430b327771f53 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Thu, 23 Feb 2023 21:26:02 -0800 Subject: [PATCH 36/47] reflect: allow looking up the Kind() field of nil rawtype pointers --- src/reflect/type.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/reflect/type.go b/src/reflect/type.go index bbcd4c9c6b..a8025dcd40 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -489,6 +489,9 @@ func (t *rawType) String() string { } func (t *rawType) Kind() Kind { + if t == nil { + return Invalid + } return Kind(t.meta & kindMask) } From b9031558d5b4446cdc5ab712d42f85d32560b4ca Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Thu, 23 Feb 2023 21:26:35 -0800 Subject: [PATCH 37/47] reflect: implement FieldByName and FieldByIndex --- src/reflect/type.go | 3 +++ src/reflect/value.go | 28 ++++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/reflect/type.go b/src/reflect/type.go index a8025dcd40..31b938b1ee 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -532,6 +532,7 @@ func (t *rawType) Field(i int) StructField { Tag: field.Tag, Anonymous: field.Anonymous, Offset: field.Offset, + Index: []int{i}, } } @@ -912,6 +913,7 @@ func (t *rawType) FieldByName(name string) (StructField, bool) { Tag: field.Tag, Anonymous: field.Anonymous, Offset: field.Offset, + Index: []int{i}, }, true } } @@ -939,6 +941,7 @@ func (t *rawType) FieldByIndex(index []int) StructField { Tag: field.Tag, Anonymous: field.Anonymous, Offset: field.Offset, + Index: index, } } diff --git a/src/reflect/value.go b/src/reflect/value.go index 3172ea2ba8..91d1155f2b 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -1157,7 +1157,24 @@ func (v Value) SetMapIndex(key, elem Value) { // FieldByIndex returns the nested field corresponding to index. func (v Value) FieldByIndex(index []int) Value { - panic("unimplemented: (reflect.Value).FieldByIndex()") + if len(index) == 1 { + return v.Field(index[0]) + } + if v.Kind() != Struct { + panic(&ValueError{"FieldByIndex", v.Kind()}) + } + for i, x := range index { + if i > 0 { + if v.Kind() == Pointer && v.typecode.elem().Kind() == Struct { + if v.IsNil() { + panic("reflect: indirection through nil pointer to embedded struct") + } + v = v.Elem() + } + } + v = v.Field(x) + } + return v } // FieldByIndexErr returns the nested field corresponding to index. @@ -1166,7 +1183,14 @@ func (v Value) FieldByIndexErr(index []int) (Value, error) { } func (v Value) FieldByName(name string) Value { - panic("unimplemented: (reflect.Value).FieldByName()") + if v.Kind() != Struct { + panic(&ValueError{"FieldByName", v.Kind()}) + } + + if field, ok := v.typecode.FieldByName(name); ok { + return v.FieldByIndex(field.Index) + } + return Value{} } //go:linkname hashmapMake runtime.hashmapMakeUnsafePtr From 995c03fdbd275983ffd2fb71daf62f3bff5023b5 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Thu, 23 Feb 2023 21:27:01 -0800 Subject: [PATCH 38/47] reflect: fix flags for MakeSlice() --- src/reflect/value.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 91d1155f2b..171bc13b4d 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -947,7 +947,7 @@ func MakeSlice(typ Type, len, cap int) Value { return Value{ typecode: typ.(*rawType), value: unsafe.Pointer(&slice), - flags: valueFlagIndirect, + flags: valueFlagExported, } } From 3f6b3f6a497305815896d0d4a9435e2247f2ac79 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Thu, 23 Feb 2023 21:27:28 -0800 Subject: [PATCH 39/47] reflect: implement Append() --- src/reflect/value.go | 41 +++++++++++++++++++++++++++++++++++++++-- src/runtime/slice.go | 26 ++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 171bc13b4d..297753f4d7 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -1061,10 +1061,47 @@ func Copy(dst, src Value) int { return sliceCopy(dhdr.data, shdr.data, dhdr.len, shdr.len, dst.typecode.Elem().Size()) } +//go:linkname sliceGrow runtime.sliceGrow +func sliceGrow(buf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) + +// extend slice to old n new elements +func (v *Value) extendSlice(n int) { + if v.Kind() != Slice { + panic(&ValueError{Method: "extendSlice", Kind: v.Kind()}) + } + + if v.Cap() > v.Len()+n { + return + } + + var old sliceHeader + if v.value != nil { + old = *(*sliceHeader)(v.value) + } + + nbuf, nlen, ncap := sliceGrow(old.data, old.len, old.cap, old.cap+uintptr(n), v.typecode.elem().Size()) + + newslice := sliceHeader{ + data: nbuf, + len: nlen + uintptr(n), + cap: ncap, + } + + v.value = (unsafe.Pointer)(&newslice) +} + // Append appends the values x to a slice s and returns the resulting slice. // As in Go, each x's value must be assignable to the slice's element type. -func Append(s Value, x ...Value) Value { - panic("unimplemented: reflect.Append()") +func Append(v Value, x ...Value) Value { + if v.Kind() != Slice { + panic(&ValueError{Method: "Append", Kind: v.Kind()}) + } + oldLen := v.Len() + v.extendSlice(len(x)) + for i, xx := range x { + v.Index(oldLen + i).Set(xx) + } + return v } // AppendSlice appends a slice t to a slice s and returns the resulting slice. diff --git a/src/runtime/slice.go b/src/runtime/slice.go index b58fab360c..c719bb3d4d 100644 --- a/src/runtime/slice.go +++ b/src/runtime/slice.go @@ -51,3 +51,29 @@ func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr memmove(dst, src, n*elemSize) return int(n) } + +func sliceGrow(oldBuf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) { + // TODO(dgryski): This duplciated growing logic could be extracted from here and sliceAppend, similar to how upstream Go does. + if oldCap >= newCap { + // No need to grow, return the input slice. + return oldBuf, oldLen, oldCap + } + + // allow nil slice + if oldCap == 0 { + oldCap++ + } + + // grow capacity + for oldCap < newCap { + oldCap *= 2 + } + + buf := alloc(oldCap*elemSize, nil) + if oldLen > 0 { + // copy any data to new slice + memmove(buf, oldBuf, oldLen*elemSize) + } + + return buf, oldLen, oldCap +} From b2f2251377f855e93381f8dad4cb1d7f8f62529e Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Thu, 23 Feb 2023 22:16:27 -0800 Subject: [PATCH 40/47] reflect: implement Zero() --- src/reflect/value.go | 61 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 297753f4d7..1d0d0076cc 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -14,6 +14,7 @@ type valueFlags uint8 const ( valueFlagIndirect valueFlags = 1 << iota valueFlagExported + valueFlagRO ) type Value struct { @@ -36,6 +37,10 @@ func (v Value) isExported() bool { return v.flags&valueFlagExported != 0 } +func (v Value) isRO() bool { + return v.flags&valueFlagRO == valueFlagRO +} + func Indirect(v Value) Value { if v.Kind() != Ptr { return v @@ -213,7 +218,7 @@ func (v Value) CanInterface() bool { } func (v Value) CanAddr() bool { - return v.flags&(valueFlagIndirect) == valueFlagIndirect + return v.flags&(valueFlagIndirect&^valueFlagRO) == valueFlagIndirect&^valueFlagRO } func (v Value) Addr() Value { @@ -229,7 +234,7 @@ func (v Value) Addr() Value { } func (v Value) CanSet() bool { - return v.flags&(valueFlagExported|valueFlagIndirect) == valueFlagExported|valueFlagIndirect + return v.flags&(valueFlagExported|valueFlagIndirect)&^valueFlagRO == (valueFlagExported|valueFlagIndirect)&^valueFlagRO } func (v Value) Bool() bool { @@ -778,6 +783,7 @@ func (it *MapIter) Next() bool { func (v Value) Set(x Value) { v.checkAddressable() + v.checkRO() if !v.typecode.AssignableTo(x.typecode) { panic("reflect: cannot set") } @@ -792,6 +798,7 @@ func (v Value) Set(x Value) { func (v Value) SetBool(x bool) { v.checkAddressable() + v.checkRO() switch v.Kind() { case Bool: *(*bool)(v.value) = x @@ -802,6 +809,7 @@ func (v Value) SetBool(x bool) { func (v Value) SetInt(x int64) { v.checkAddressable() + v.checkRO() switch v.Kind() { case Int: *(*int)(v.value) = int(x) @@ -820,6 +828,7 @@ func (v Value) SetInt(x int64) { func (v Value) SetUint(x uint64) { v.checkAddressable() + v.checkRO() switch v.Kind() { case Uint: *(*uint)(v.value) = uint(x) @@ -840,6 +849,7 @@ func (v Value) SetUint(x uint64) { func (v Value) SetFloat(x float64) { v.checkAddressable() + v.checkRO() switch v.Kind() { case Float32: *(*float32)(v.value) = float32(x) @@ -852,6 +862,7 @@ func (v Value) SetFloat(x float64) { func (v Value) SetComplex(x complex128) { v.checkAddressable() + v.checkRO() switch v.Kind() { case Complex64: *(*complex64)(v.value) = complex64(x) @@ -864,6 +875,7 @@ func (v Value) SetComplex(x complex128) { func (v Value) SetString(x string) { v.checkAddressable() + v.checkRO() switch v.Kind() { case String: *(*string)(v.value) = x @@ -885,6 +897,8 @@ func (v Value) SetLen(n int) { panic(&ValueError{"reflect.Value.SetLen", v.Kind()}) } + v.checkRO() + hdr := (*sliceHeader)(v.value) if uintptr(n) > hdr.cap { panic("reflect.Value.SetLen: slice length out of range") @@ -898,6 +912,12 @@ func (v Value) checkAddressable() { } } +func (v Value) checkRO() { + if v.isRO() { + panic("reflect: value is not settable") + } +} + // OverflowInt reports whether the int64 x cannot be represented by v's type. // It panics if v's Kind is not Int, Int8, Int16, Int32, or Int64. func (v Value) OverflowInt(x int64) bool { @@ -949,11 +969,38 @@ func MakeSlice(typ Type, len, cap int) Value { value: unsafe.Pointer(&slice), flags: valueFlagExported, } - } +var zerobuffer [256]byte + func Zero(typ Type) Value { - panic("unimplemented: reflect.Zero()") + + if typ.Size() < unsafe.Sizeof(uintptr(0)) { + return Value{ + typecode: typ.(*rawType), + value: nil, + flags: valueFlagRO, + } + } + + if typ.Size() < uintptr(len(zerobuffer)) { + flags := valueFlagRO + if typ.Kind() != Slice { + flags |= valueFlagIndirect + } + + return Value{ + typecode: typ.(*rawType), + value: unsafe.Pointer(&zerobuffer[0]), + flags: flags, + } + } + + return Value{ + typecode: typ.(*rawType), + value: alloc(typ.Size(), nil), + flags: valueFlagRO | valueFlagIndirect, + } } // New is the reflect equivalent of the new(T) keyword, returning a pointer to a @@ -1055,6 +1102,8 @@ func Copy(dst, src Value) int { panic("type mismatch") } + dst.checkRO() + dhdr := (*sliceHeader)(dst.value) shdr := (*sliceHeader)(src.value) @@ -1093,6 +1142,7 @@ func (v *Value) extendSlice(n int) { // Append appends the values x to a slice s and returns the resulting slice. // As in Go, each x's value must be assignable to the slice's element type. func Append(v Value, x ...Value) Value { + //TODO(dgrysk): v.checkRO() ? if v.Kind() != Slice { panic(&ValueError{Method: "Append", Kind: v.Kind()}) } @@ -1116,6 +1166,7 @@ func AppendSlice(s, t Value) Value { // One of the sides was not exported, so can't access the data. panic("reflect.AppendSlice: unexported") } + // TODO(dgryski): s.checkRO() sSlice := (*sliceHeader)(s.value) tSlice := (*sliceHeader)(t.value) elemSize := s.typecode.elem().Size() @@ -1143,6 +1194,8 @@ func (v Value) SetMapIndex(key, elem Value) { panic(&ValueError{Method: "SetMapIndex", Kind: v.Kind()}) } + v.checkRO() + // compare key type with actual key type of map if key.typecode != v.typecode.key() { panic("reflect.Value.SetMapIndex: incompatible types for key") From b10163dfd0b9a97e5a12332064d9074e4edd509f Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Fri, 24 Feb 2023 10:34:12 -0800 Subject: [PATCH 41/47] reflect: implement Slice() --- src/reflect/value.go | 72 ++++++++++++++++++++++++++++++++++++++- src/reflect/value_test.go | 32 +++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 1d0d0076cc..06f868a263 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -395,7 +395,77 @@ func (v Value) Bytes() []byte { } func (v Value) Slice(i, j int) Value { - panic("unimplemented: (reflect.Value).Slice()") + + switch v.Kind() { + case Slice: + hdr := *(*sliceHeader)(v.value) + + i, j := uintptr(i), uintptr(j) + + if i < 0 || i > hdr.len || j < 0 || j > hdr.len { + slicePanic() + } + + elemSize := v.typecode.underlying().elem().Size() + + hdr.len = j - i + hdr.cap = hdr.cap - i + hdr.data = unsafe.Add(hdr.data, i*elemSize) + + return Value{ + typecode: v.typecode, + value: unsafe.Pointer(&hdr), + flags: v.flags, + } + + case Array: + // TODO(dgryski): can't do this yet because the resulting value needs type slice of v.elem(), not array of v.elem(). + // need to be able to look up this "new" type so pointer equality of types still works + + /* + if i < 0 || i > hdr.len || j < 0 || j > hdr.len { + slicePanic() + } + + elemSize := v.typecode.Size() + hdr := sliceHeader { + data: unsafe.Add(hdr.data, elemSize * i) + hdr.len: j - i + hdr.cap: v.typecode.Len() - i + } + + return Value{ + typecode: v.typecode, <-- new type goes here + value: unsafe.Pointer(&hdr), + flags: v.flags, + } + */ + + case String: + + i, j := uintptr(i), uintptr(j) + + str := *(*stringHeader)(v.value) + + if i < 0 || i > str.len || j < 0 || j > str.len { + slicePanic() + } + + hdr := sliceHeader{ + data: unsafe.Add(str.data, i), + len: j - i, + cap: str.len - i, + } + + return Value{ + typecode: v.typecode, + value: unsafe.Pointer(&hdr), + flags: v.flags, + } + } + + panic(&ValueError{"Slice", v.Kind()}) + } func (v Value) Slice3(i, j, k int) Value { diff --git a/src/reflect/value_test.go b/src/reflect/value_test.go index 78503aaa41..56ddade480 100644 --- a/src/reflect/value_test.go +++ b/src/reflect/value_test.go @@ -173,6 +173,38 @@ func TestStruct(t *testing.T) { } +func TestSlice(t *testing.T) { + var a [10]int + v := reflect.ValueOf(&a) + + for i := 0; i < v.Elem().Len(); i++ { + v.Elem().Index(i).Set(reflect.ValueOf(i)) + } + + for i := 0; i < len(a); i++ { + if a[i] != i { + t.Errorf("v.Elem().Index(%d).Set(reflect.ValueOf(%d)=%v, want %d\n", i, i, a[i], i) + } + } + + s := a[:] + v = reflect.ValueOf(s) + + s28 := a[2:8] + s28ref := v.Slice(2, 8) + + if len(s28) != s28ref.Len() || cap(s28) != s28ref.Cap() { + t.Errorf("len(s28)=%d s28ref.Len()=%d cap(s28)=%d s28ref.Cap()=%d\n", len(s28), s28ref.Len(), cap(s28), s28ref.Cap()) + } + + for i, got := range s28 { + want := int(s28ref.Index(i).Int()) + if got != want { + t.Errorf("s28[%d]=%d, want %d", i, got, want) + } + } +} + func equal[T comparable](a, b []T) bool { if len(a) != len(b) { return false From 18db66fee18819b1d2dce8ff6db76ad4889c819f Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Fri, 24 Feb 2023 10:44:58 -0800 Subject: [PATCH 42/47] reflect: clean up some panic messages --- src/reflect/value.go | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 06f868a263..7a7ab2fdbc 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -493,7 +493,7 @@ func (v Value) Len() int { case String: return int((*stringHeader)(v.value).len) default: - panic(&ValueError{Method: "Len"}) + panic(&ValueError{Method: "Len", Kind: v.Kind()}) } } @@ -511,7 +511,7 @@ func (v Value) Cap() int { case Slice: return int((*sliceHeader)(v.value).cap) default: - panic(&ValueError{Method: "Cap"}) + panic(&ValueError{Method: "Cap", Kind: v.Kind()}) } } @@ -541,12 +541,16 @@ func (v Value) Elem() Value { flags: v.flags &^ valueFlagIndirect, } default: - panic(&ValueError{Method: "Elem"}) + panic(&ValueError{Method: "Elem", Kind: v.Kind()}) } } // Field returns the value of the i'th field of this struct. func (v Value) Field(i int) Value { + if v.Kind() != Struct { + panic(&ValueError{Method: "Field", Kind: v.Kind()}) + } + structField := v.typecode.rawField(i) flags := v.flags if structField.PkgPath != "" { @@ -691,7 +695,7 @@ func (v Value) Index(i int) Value { value: unsafe.Pointer(value), } default: - panic(&ValueError{Method: "Index"}) + panic(&ValueError{Method: "Index", Kind: v.Kind()}) } } @@ -770,6 +774,7 @@ func (v Value) MapIndex(key Value) Value { // compare key type with actual key type of map if key.typecode != v.typecode.key() { + // type error? panic(&ValueError{Method: "MapIndex"}) } @@ -799,6 +804,10 @@ func (v Value) MapIndex(key Value) Value { } func (v Value) MapRange() *MapIter { + if v.Kind() != Map { + panic(&ValueError{Method: "MapRange", Kind: v.Kind()}) + } + return &MapIter{ m: v, it: hashmapNewIterator(), @@ -873,7 +882,7 @@ func (v Value) SetBool(x bool) { case Bool: *(*bool)(v.value) = x default: - panic(&ValueError{Method: "SetBool"}) + panic(&ValueError{Method: "SetBool", Kind: v.Kind()}) } } @@ -892,7 +901,7 @@ func (v Value) SetInt(x int64) { case Int64: *(*int64)(v.value) = x default: - panic(&ValueError{Method: "SetInt"}) + panic(&ValueError{Method: "SetInt", Kind: v.Kind()}) } } @@ -913,7 +922,7 @@ func (v Value) SetUint(x uint64) { case Uintptr: *(*uintptr)(v.value) = uintptr(x) default: - panic(&ValueError{Method: "SetUint"}) + panic(&ValueError{Method: "SetUint", Kind: v.Kind()}) } } @@ -926,7 +935,7 @@ func (v Value) SetFloat(x float64) { case Float64: *(*float64)(v.value) = x default: - panic(&ValueError{Method: "SetFloat"}) + panic(&ValueError{Method: "SetFloat", Kind: v.Kind()}) } } @@ -939,7 +948,7 @@ func (v Value) SetComplex(x complex128) { case Complex128: *(*complex128)(v.value) = x default: - panic(&ValueError{Method: "SetComplex"}) + panic(&ValueError{Method: "SetComplex", Kind: v.Kind()}) } } @@ -950,7 +959,7 @@ func (v Value) SetString(x string) { case String: *(*string)(v.value) = x default: - panic(&ValueError{Method: "SetString"}) + panic(&ValueError{Method: "SetString", Kind: v.Kind()}) } } From c1bfca6a072a044e5c9bdb9ca83d47c46ae75e7c Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Fri, 24 Feb 2023 10:52:47 -0800 Subject: [PATCH 43/47] reflect: add Slice3() --- src/reflect/value.go | 28 ++++++++++++++++++++++++++++ src/reflect/value_test.go | 15 +++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/reflect/value.go b/src/reflect/value.go index 7a7ab2fdbc..a68fc1b6b4 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -469,6 +469,34 @@ func (v Value) Slice(i, j int) Value { } func (v Value) Slice3(i, j, k int) Value { + switch v.Kind() { + case Slice: + hdr := *(*sliceHeader)(v.value) + + i, j, k := uintptr(i), uintptr(j), uintptr(k) + + if i < 0 || i > hdr.len || j < 0 || j > hdr.len || k < 0 || k > hdr.len { + slicePanic() + } + + elemSize := v.typecode.underlying().elem().Size() + + hdr.len = j - i + hdr.cap = k - i + hdr.data = unsafe.Add(hdr.data, i*elemSize) + + return Value{ + typecode: v.typecode, + value: unsafe.Pointer(&hdr), + flags: v.flags, + } + + case Array: + // TODO(dgryski): can't do this yet because the resulting value needs type v.elem(), not array of v.elem(). + // need to be able to look up this "new" type so pointer equality of types still works + + } + panic("unimplemented: (reflect.Value).Slice3()") } diff --git a/src/reflect/value_test.go b/src/reflect/value_test.go index 56ddade480..20b3c57ead 100644 --- a/src/reflect/value_test.go +++ b/src/reflect/value_test.go @@ -203,6 +203,21 @@ func TestSlice(t *testing.T) { t.Errorf("s28[%d]=%d, want %d", i, got, want) } } + + s268 := a[2:6:8] + s268ref := v.Slice3(2, 6, 8) + + if len(s268) != s268ref.Len() || cap(s268) != s268ref.Cap() { + t.Errorf("len(s268)=%d s268ref.Len()=%d cap(s268)=%d s268ref.Cap()=%d\n", len(s268), s268ref.Len(), cap(s268), s268ref.Cap()) + } + + for i, got := range s268 { + want := int(s268ref.Index(i).Int()) + if got != want { + t.Errorf("s268[%d]=%d, want %d", i, got, want) + } + } + } func equal[T comparable](a, b []T) bool { From 1ed5d8a9485c6983f44ae3069495d51117e9a04a Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Fri, 24 Feb 2023 12:47:08 -0800 Subject: [PATCH 44/47] reflect: support Value.Stirng() reporting simple types --- src/reflect/type.go | 17 +++++++++++++++++ src/reflect/value.go | 2 +- src/reflect/value_test.go | 4 ++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/reflect/type.go b/src/reflect/type.go index 31b938b1ee..4387afd636 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -53,6 +53,7 @@ package reflect import ( + "internal/itoa" "unsafe" ) @@ -485,6 +486,22 @@ func (t *rawType) String() string { return "T" } // TODO(dgryski): doesn't yet handle complex types + + switch t.Kind() { + case Chan: + return "chan " + t.elem().String() + case Pointer: + return "*" + t.elem().String() + case Slice: + return "[]" + t.elem().String() + case Array: + return "[" + itoa.Itoa(t.Len()) + "]" + t.elem().String() + case Map: + return "map[" + t.key().String() + "]" + t.elem().String() + default: + return t.Kind().String() + } + return t.Kind().String() } diff --git a/src/reflect/value.go b/src/reflect/value.go index a68fc1b6b4..4a6dcd9966 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -386,7 +386,7 @@ func (v Value) String() string { return *(*string)(v.value) default: // Special case because of the special treatment of .String() in Go. - return "" + return "<" + v.typecode.String() + " Value>" } } diff --git a/src/reflect/value_test.go b/src/reflect/value_test.go index 20b3c57ead..8bc3c60e49 100644 --- a/src/reflect/value_test.go +++ b/src/reflect/value_test.go @@ -177,6 +177,10 @@ func TestSlice(t *testing.T) { var a [10]int v := reflect.ValueOf(&a) + if got, want := v.String(), "<*[10]int Value>"; got != want { + t.Errorf("v.String()=%q, want %q", got, want) + } + for i := 0; i < v.Elem().Len(); i++ { v.Elem().Index(i).Set(reflect.ValueOf(i)) } From ff20f2c25f2b7e4589c0793d493149bcb835de13 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Fri, 24 Feb 2023 13:15:21 -0800 Subject: [PATCH 45/47] reflect: todo++ --- src/reflect/type.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/reflect/type.go b/src/reflect/type.go index 4387afd636..f28994c726 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -471,6 +471,8 @@ func PointerTo(t Type) Type { func pointerTo(t *rawType) *rawType { switch t.Kind() { case Pointer: + // TODO(dgryski): This is blocking https://github.com/tinygo-org/tinygo/issues/3131 + // We need to be able to create types that match existing types to prevent typecode equality. panic("reflect: cannot make **T type") case Struct: return (*structType)(unsafe.Pointer(t)).ptrTo From 18457d71cc57464df58998c480e0ee92733210af Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Fri, 24 Feb 2023 14:19:08 -0800 Subject: [PATCH 46/47] reflect: add Bytes() and fix up Copy() to support arrays --- src/reflect/value.go | 80 ++++++++++++++++++++++++++++++++------- src/reflect/value_test.go | 30 +++++++++++++++ 2 files changed, 97 insertions(+), 13 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index 4a6dcd9966..df60371d4d 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -391,7 +391,33 @@ func (v Value) String() string { } func (v Value) Bytes() []byte { - panic("unimplemented: (reflect.Value).Bytes()") + + switch v.Kind() { + case Slice: + if v.typecode.elem().Kind() != Uint8 { + panic(&ValueError{Method: "Bytes", Kind: v.Kind()}) + } + hdr := *(*sliceHeader)(v.value) + return *(*[]byte)(unsafe.Pointer(&hdr)) + + case Array: + if v.typecode.elem().Kind() != Uint8 { + panic(&ValueError{Method: "Bytes", Kind: v.Kind()}) + } + + var hdr sliceHeader + + l := uintptr(v.Len()) + + hdr.len = l + hdr.cap = l + hdr.data = v.value + + return *(*[]byte)(unsafe.Pointer(&hdr)) + + } + + panic(&ValueError{Method: "Bytes", Kind: v.Kind()}) } func (v Value) Slice(i, j int) Value { @@ -1196,25 +1222,53 @@ func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr // Copy copies the contents of src into dst until either // dst has been filled or src has been exhausted. func Copy(dst, src Value) int { + dst.checkRO() - if dst.typecode.Kind() != Slice { - panic("dst not slice " + dst.typecode.Kind().String()) - } + compatibleTypes := false || + // dst and src are both slices or arrays with equal types + ((dst.typecode.Kind() == Slice || dst.typecode.Kind() == Array) && + (src.typecode.Kind() == Slice || src.typecode.Kind() == Array) && + (dst.typecode.elem() == src.typecode.elem())) || + // dst is array or slice of uint8 and src is string + ((dst.typecode.Kind() == Slice || dst.typecode.Kind() == Array) && + dst.typecode.elem().Kind() == Uint8 && + src.typecode.Kind() == String) - if src.typecode.Kind() != Slice { - panic("src not slice") + if !compatibleTypes { + panic("Copy: type mismatch: " + dst.typecode.String() + "/" + src.typecode.String()) } - if dst.typecode != src.typecode { - panic("type mismatch") - } + dstbuf, dstlen := buflen(dst) + srcbuf, srclen := buflen(src) - dst.checkRO() + return sliceCopy(dstbuf, srcbuf, dstlen, srclen, dst.typecode.elem().Size()) +} - dhdr := (*sliceHeader)(dst.value) - shdr := (*sliceHeader)(src.value) +func buflen(v Value) (unsafe.Pointer, uintptr) { + var buf unsafe.Pointer + var len uintptr + switch v.typecode.Kind() { + case Slice: + hdr := (*sliceHeader)(v.value) + buf = hdr.data + len = hdr.len + case Array: + if v.isIndirect() { + buf = v.value + len = uintptr(v.Len()) + } else { + // TODO(dgryski): Need to fix this + panic("Copy: does not support small inline arrays") + } + case String: + hdr := (*stringHeader)(v.value) + buf = hdr.data + len = hdr.len + default: + panic("reflect.Copy: not slice or array or string") + } - return sliceCopy(dhdr.data, shdr.data, dhdr.len, shdr.len, dst.typecode.Elem().Size()) + return buf, len } //go:linkname sliceGrow runtime.sliceGrow diff --git a/src/reflect/value_test.go b/src/reflect/value_test.go index 8bc3c60e49..321fa35327 100644 --- a/src/reflect/value_test.go +++ b/src/reflect/value_test.go @@ -221,6 +221,36 @@ func TestSlice(t *testing.T) { t.Errorf("s268[%d]=%d, want %d", i, got, want) } } +} + +func TestBytes(t *testing.T) { + + b := []byte("12345") + bref := reflect.ValueOf(b) + + refbytes := bref.Bytes() + + if !reflect.DeepEqual(refbytes, b) { + t.Errorf("Failed to get bytes back: %v != %v", refbytes, b) + } + + var a [16]byte + aref := reflect.ValueOf(&a) + + reflect.Copy(aref.Elem(), bref) + + var want [16]byte + copy(want[:], b) + + if want != a || reflect.DeepEqual(aref.Elem(), reflect.ValueOf(want)) { + t.Errorf("Copy/Bytes failed: %v != %v", a, want) + } + + reflect.Copy(bref, reflect.ValueOf("abcde")) + + if string(b) != "abcde" { + t.Errorf("Copy()=%q, want `abcde`", string(b)) + } } From 77d366e895b453abb44aff0d5edca87922678ce2 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 22 Feb 2023 18:01:49 -0800 Subject: [PATCH 47/47] Makefile: more stdlib packages thanks to reflect improvements --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index a0acb881a0..ac1660d688 100644 --- a/Makefile +++ b/Makefile @@ -312,6 +312,7 @@ TEST_PACKAGES_FAST = \ net \ net/http/internal/ascii \ net/mail \ + net/textproto \ os \ path \ reflect \ @@ -337,9 +338,11 @@ endif # compress/lzw appears to hang on wasi # crypto/hmac fails on wasi, it exits with a "slice out of range" panic # debug/plan9obj requires os.ReadAt, which is not yet supported on windows +# go/build/constraint requires recover(), which is not yet supported on wasi # image requires recover(), which is not yet supported on wasi # io/ioutil requires os.ReadDir, which is not yet supported on windows or wasi # mime/quotedprintable requires syscall.Faccessat +# mime/multipart hangs on wasi and fails on windows # strconv requires recover() which is not yet supported on wasi # text/tabwriter requries recover(), which is not yet supported on wasi # text/template/parse requires recover(), which is not yet supported on wasi @@ -354,8 +357,10 @@ TEST_PACKAGES_LINUX := \ crypto/hmac \ debug/dwarf \ debug/plan9obj \ + go/build/constraint \ image \ io/ioutil \ + mime/multipart \ mime/quotedprintable \ strconv \ testing/fstest \