diff --git a/internal/lib/reflect/type.go b/internal/lib/reflect/type.go index a1773775..6f3da0b6 100644 --- a/internal/lib/reflect/type.go +++ b/internal/lib/reflect/type.go @@ -1463,6 +1463,79 @@ func funcStr(ft *funcType) string { return string(repr) } +// MapOf returns the map type with the given key and element types. +// For example, if k represents int and e represents string, +// MapOf(k, e) represents map[int]string. +// +// If the key type is not a valid map key type (that is, if it does +// not implement Go's == operator), MapOf panics. +func MapOf(key, elem Type) Type { + ktyp := key.common() + etyp := elem.common() + + if ktyp.Equal == nil { + panic("reflect.MapOf: invalid key type " + stringFor(ktyp)) + } + + // Look in cache. + ckey := cacheKey{Map, ktyp, etyp, 0} + if mt, ok := lookupCache.Load(ckey); ok { + return mt.(Type) + } + + // Look in known types. + s := "map[" + stringFor(ktyp) + "]" + stringFor(etyp) + // for _, tt := range typesByString(s) { + // mt := (*mapType)(unsafe.Pointer(tt)) + // if mt.Key == ktyp && mt.Elem == etyp { + // ti, _ := lookupCache.LoadOrStore(ckey, toRType(tt)) + // return ti.(Type) + // } + // } + + // Make a map type. + // Note: flag values must match those used in the TMAP case + // in ../cmd/compile/internal/reflectdata/reflect.go:writeType. + var imap any = (map[unsafe.Pointer]unsafe.Pointer)(nil) + mt := **(**mapType)(unsafe.Pointer(&imap)) + mt.Str_ = s + mt.TFlag = 0 + mt.Hash = fnv1(etyp.Hash, 'm', byte(ktyp.Hash>>24), byte(ktyp.Hash>>16), byte(ktyp.Hash>>8), byte(ktyp.Hash)) + mt.Key = ktyp + mt.Elem = etyp + mt.Bucket = bucketOf(ktyp, etyp) + mt.Hasher = func(p unsafe.Pointer, seed uintptr) uintptr { + return typehash(ktyp, p, seed) + } + mt.Flags = 0 + if ktyp.Size_ > maxKeySize { + mt.KeySize = uint8(goarch.PtrSize) + mt.Flags |= 1 // indirect key + } else { + mt.KeySize = uint8(ktyp.Size_) + } + if etyp.Size_ > maxValSize { + mt.ValueSize = uint8(goarch.PtrSize) + mt.Flags |= 2 // indirect value + } else { + mt.MapType.ValueSize = uint8(etyp.Size_) + } + mt.MapType.BucketSize = uint16(mt.Bucket.Size_) + if isReflexive(ktyp) { + mt.Flags |= 4 + } + if needKeyUpdate(ktyp) { + mt.Flags |= 8 + } + if hashMightPanic(ktyp) { + mt.Flags |= 16 + } + mt.PtrToThis_ = nil + + ti, _ := lookupCache.LoadOrStore(ckey, toRType(&mt.Type)) + return ti.(Type) +} + // isReflexive reports whether the == operation on the type is reflexive. // That is, x == x for all values x of type t. func isReflexive(t *abi.Type) bool { diff --git a/internal/lib/reflect/value.go b/internal/lib/reflect/value.go index 60800460..0fd637fd 100644 --- a/internal/lib/reflect/value.go +++ b/internal/lib/reflect/value.go @@ -2297,9 +2297,9 @@ func (v Value) MapIndex(key Value) Value { } else { k = unsafe.Pointer(&key.ptr) } - e = mapaccess(v.typ(), v.pointer(), k) - // } - if e == nil { + var ok bool + e, ok = mapaccess(v.typ(), v.pointer(), k) + if !ok { return Value{} } typ := tt.Elem @@ -2519,7 +2519,7 @@ func (v Value) MapRange() *MapIter { // Force slow panicking path not inlined, so it won't add to the // inlining budget of the caller. -// TODO: undo when the inliner is no longer bottom-up only. +// TODO undo when the inliner is no longer bottom-up only. // //go:noinline func (f flag) panicNotMap() { @@ -2587,11 +2587,14 @@ func chancap(ch unsafe.Pointer) int //go:linkname chanlen github.com/goplus/llgo/internal/runtime.ChanLen func chanlen(ch unsafe.Pointer) int +//go:linkname makemap github.com/goplus/llgo/internal/runtime.MakeMap +func makemap(t *abi.Type, cap int) (m unsafe.Pointer) + //go:linkname maplen github.com/goplus/llgo/internal/runtime.MapLen func maplen(ch unsafe.Pointer) int -//go:linkname mapaccess github.com/goplus/llgo/internal/runtime.MapAccess1 -func mapaccess(t *abi.Type, m unsafe.Pointer, key unsafe.Pointer) (val unsafe.Pointer) +//go:linkname mapaccess github.com/goplus/llgo/internal/runtime.MapAccess2 +func mapaccess(t *abi.Type, m unsafe.Pointer, key unsafe.Pointer) (val unsafe.Pointer, ok bool) //go:linkname mapassign0 github.com/goplus/llgo/internal/runtime.MapAssign func mapassign0(t *abi.Type, m unsafe.Pointer, key unsafe.Pointer) unsafe.Pointer @@ -2635,6 +2638,9 @@ func mapiternext(it *hiter) //go:linkname mapclear github.com/goplus/llgo/internal/runtime.mapclear func mapclear(t *abi.Type, m unsafe.Pointer) +//go:linkname typehash github.com/goplus/llgo/internal/runtime.typehash +func typehash(t *abi.Type, p unsafe.Pointer, h uintptr) uintptr + // MakeSlice creates a new zero-initialized slice value // for the specified slice type, length, and capacity. func MakeSlice(typ Type, len, cap int) Value { @@ -2655,6 +2661,22 @@ func MakeSlice(typ Type, len, cap int) Value { return Value{&typ.(*rtype).t, unsafe.Pointer(&s), flagIndir | flag(Slice)} } +// MakeMap creates a new map with the specified type. +func MakeMap(typ Type) Value { + return MakeMapWithSize(typ, 0) +} + +// MakeMapWithSize creates a new map with the specified type +// and initial space for approximately n elements. +func MakeMapWithSize(typ Type, n int) Value { + if typ.Kind() != Map { + panic("reflect.MakeMapWithSize of non-map type") + } + t := typ.common() + m := makemap(t, n) + return Value{t, m, flag(Map)} +} + func ifaceE2I(t *abi.Type, src any, dst unsafe.Pointer) { panic("todo: reflect.ifaceE2I") }