From 2c9bb65028899ec89ca70359221a331bf5c7b641 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 15 Feb 2023 12:11:13 -0800 Subject: [PATCH 01/37] 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 d3dd8d74d7655183625261eec98aab3026f90aed Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 15 Feb 2023 12:12:51 -0800 Subject: [PATCH 02/37] 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 2db09f5908..3240214c6a 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 d570aeb734e8bdf7fd6cd6080662643fc89e0d0a Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 15 Feb 2023 12:33:00 -0800 Subject: [PATCH 03/37] 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 3240214c6a..0a57dcf190 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 1f31bc2a228676ab7b10966b25954c573de454b0 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 15 Feb 2023 12:50:10 -0800 Subject: [PATCH 04/37] 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 5555672f44a72e834f4c860fb4c3599c1be40925 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 15 Feb 2023 13:03:00 -0800 Subject: [PATCH 05/37] 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 090eeb0fe0d97f6a90f414c4acc89548eeabd52b Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 15 Feb 2023 13:03:53 -0800 Subject: [PATCH 06/37] 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 4afb7ea3a49361e2968568f9364025609c0d9415 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 15 Feb 2023 13:41:51 -0800 Subject: [PATCH 07/37] 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 0a57dcf190..2fc2a01b92 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 46fa7d3ee22aef2721120a7ff769fc71355fd983 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 15 Feb 2023 14:02:30 -0800 Subject: [PATCH 08/37] 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 23b7c171d5943101e359c77bfda3535f3f23b63a Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 15 Feb 2023 21:46:11 -0800 Subject: [PATCH 09/37] 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 2fc2a01b92..35ee4e79e5 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 8eb486f53cd3fa15b51c9c16262ec45c80aa6aef Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Thu, 16 Feb 2023 12:32:55 -0800 Subject: [PATCH 10/37] 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 3bc434346107b6d9bfc35fd486fde77838f54ca9 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Thu, 16 Feb 2023 13:08:47 -0800 Subject: [PATCH 11/37] 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 c6d8ff01fc6096b2d2ddc1abc501c6db266216c0 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 14:16:18 -0800 Subject: [PATCH 12/37] 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 f686e1a0309c43233cb82309186678ab55bcf50a Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 14:16:58 -0800 Subject: [PATCH 13/37] 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 8686aa56d93a9ed94bf18681156c88205497e040 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 14:59:08 -0800 Subject: [PATCH 14/37] 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 db1a0a3c1086026657a76d727d6a51eb773c3f22 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 14:59:48 -0800 Subject: [PATCH 15/37] 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 074ab03b9c6b6548952b50130e358b6ffa9caba1 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 15:00:06 -0800 Subject: [PATCH 16/37] 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 c145f5b6e086ccd7efc6dbdc08e880f051759097 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 15:15:32 -0800 Subject: [PATCH 17/37] 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 4c8e69e9633b2c1910f12dd04509049b0b6fa8f1 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 15:35:21 -0800 Subject: [PATCH 18/37] 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 d981d7503e9ba15a3ee98cea76b5718c0361ab3c Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 16:41:08 -0800 Subject: [PATCH 19/37] 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 b5500c28e2d223a60315dabcafaa55e4784efffa Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 17:06:11 -0800 Subject: [PATCH 20/37] 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 d6af53b93d5c88139c8bfb91492016db36483858 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Mon, 20 Feb 2023 17:07:23 -0800 Subject: [PATCH 21/37] 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 dfe1e82bd264091375328e52001717ad1cd01497 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 09:32:42 -0800 Subject: [PATCH 22/37] 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 07c5a6a85d5d19c58bc84be033f55fd10ac2a635 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 09:37:48 -0800 Subject: [PATCH 23/37] 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 5a96f37f207ca015f1f8202c79c03ec9f9e96f45 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 11:41:01 -0800 Subject: [PATCH 24/37] 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 091ad6358a03cbc895eb2d87bc753d3382c61163 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 12:41:14 -0800 Subject: [PATCH 25/37] 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 d5bf5bc545d8a82becdb5426e1122f6886891386 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 13:34:54 -0800 Subject: [PATCH 26/37] 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 35ee4e79e5..3ffcfec31a 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 1e66a53f1a797043179a7dd836e9d622b5ec855d Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 13:53:45 -0800 Subject: [PATCH 27/37] 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 3ffcfec31a..42516e1ffb 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 5319c5967e933a2904b4e1470ddd6ccc04dd46ed Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 17:40:27 -0800 Subject: [PATCH 28/37] 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 ffc3f85ce3bc081d5b67c9aea72185fa9be6746f Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 17:40:58 -0800 Subject: [PATCH 29/37] 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 505c4a5b89449f89634e867a4f0e5e96bf066435 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 17:43:03 -0800 Subject: [PATCH 30/37] 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 ab9f881176112f1dea76d73f768d5ce973b04b27 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 17:45:08 -0800 Subject: [PATCH 31/37] 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 2d77299bd094a1c8cb7dd10631a4aedab9d77800 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 18:11:46 -0800 Subject: [PATCH 32/37] 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 2c081362f41b39ac4b46a1f099b637c888febdd1 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 21 Feb 2023 18:12:09 -0800 Subject: [PATCH 33/37] 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 5c22c9d7a81f945797c92d4b360ea059132de2bf Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 22 Feb 2023 16:07:53 -0800 Subject: [PATCH 34/37] 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 2e288803fb9c48197be8c3f9124c10a8107104de Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 22 Feb 2023 17:52:47 -0800 Subject: [PATCH 35/37] 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 5e6719015f88c52e8dc2d03d22231ff365a36ef6 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 22 Feb 2023 18:01:49 -0800 Subject: [PATCH 36/37] Makefile: more stdlib packages thanks to reflect improvements --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index a019a357b6..9b5e1ca968 100644 --- a/Makefile +++ b/Makefile @@ -309,9 +309,11 @@ TEST_PACKAGES_FAST = \ internal/profile \ math \ math/cmplx \ + mime/multipart \ net \ net/http/internal/ascii \ net/mail \ + net/textproto \ os \ path \ reflect \ From 87757bb0c5237c38367a6302b93d0e2106029218 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 22 Feb 2023 18:49:51 -0800 Subject: [PATCH 37/37] Makefile: move mime/multipart to linux tests only --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9b5e1ca968..f799c5fe72 100644 --- a/Makefile +++ b/Makefile @@ -309,7 +309,6 @@ TEST_PACKAGES_FAST = \ internal/profile \ math \ math/cmplx \ - mime/multipart \ net \ net/http/internal/ascii \ net/mail \ @@ -342,6 +341,7 @@ endif # 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 @@ -358,6 +358,7 @@ TEST_PACKAGES_LINUX := \ debug/plan9obj \ image \ io/ioutil \ + mime/multipart \ mime/quotedprintable \ strconv \ testing/fstest \