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 \ diff --git a/compiler/interface.go b/compiler/interface.go index a359f33a4b..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, @@ -131,6 +133,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, @@ -167,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: @@ -193,6 +206,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/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 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) { diff --git a/src/reflect/type.go b/src/reflect/type.go index cc106a70d3..f28994c726 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 @@ -51,6 +53,7 @@ package reflect import ( + "internal/itoa" "unsafe" ) @@ -408,6 +411,21 @@ type arrayType struct { arrayLen uintptr } +type mapType struct { + rawType + ptrTo *rawType + elem *rawType + 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 @@ -430,12 +448,16 @@ 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 } +func (t *rawType) isNamed() bool { + return t.meta&flagNamed != 0 +} + func TypeOf(i interface{}) Type { return ValueOf(i).typecode } @@ -443,21 +465,52 @@ 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: + // 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.(*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 { - return "T" + if t.isNamed() { + // TODO(dgryski): lookup named type here + 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() } func (t *rawType) Kind() Kind { + if t == nil { + return Invalid + } return Kind(t.meta & kindMask) } @@ -472,13 +525,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 { @@ -490,6 +551,7 @@ func (t *rawType) Field(i int) StructField { Tag: field.Tag, Anonymous: field.Anonymous, Offset: field.Offset, + Index: []int{i}, } } @@ -768,6 +830,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()") } @@ -793,11 +878,24 @@ func (t *rawType) NumMethod() int { } func (t *rawType) Name() string { - panic("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 { - panic("unimplemented: (reflect.Type).Key()") + if t.Kind() != Map { + panic(TypeError{"Key"}) + } + + return t.key() } func (t rawType) In(i int) Type { @@ -816,12 +914,54 @@ 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, + Index: []int{i}, + }, 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, + Index: index, + } } // A StructField describes a single field in a struct. diff --git a/src/reflect/value.go b/src/reflect/value.go index 32898f0d22..df60371d4d 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,15 +218,23 @@ 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 { - panic("unimplemented: (reflect.Value).Addr()") + if !v.CanAddr() { + panic("taking addr of un-addr-able value") + } + + return Value{ + typecode: pointerTo(v.typecode), + value: unsafe.Pointer(&v.value), + flags: v.flags, + } } 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 { @@ -373,26 +386,150 @@ 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>" } } 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 { - 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 { + 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()") } -//go:linkname maplen runtime.hashmapLenUnsafePointer +//go:linkname maplen runtime.hashmapLenUnsafePtr func maplen(p unsafe.Pointer) int -//go:linkname chanlen runtime.chanLenUnsafePointer +//go:linkname chanlen runtime.chanLenUnsafePtr func chanlen(p unsafe.Pointer) int // Len returns the length of this value for slices, strings, arrays, channels, @@ -410,11 +547,11 @@ 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()}) } } -//go:linkname chancap runtime.chanCapUnsafePointer +//go:linkname chancap runtime.chanCapUnsafePtr func chancap(p unsafe.Pointer) int // Cap returns the capacity of this value for arrays, channels and slices. @@ -428,7 +565,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()}) } } @@ -458,12 +595,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 != "" { @@ -608,7 +749,7 @@ func (v Value) Index(i int) Value { value: unsafe.Pointer(value), } default: - panic(&ValueError{Method: "Index"}) + panic(&ValueError{Method: "Index", Kind: v.Kind()}) } } @@ -636,39 +777,146 @@ 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 { - panic("unimplemented: (reflect.Value).MapKeys()") + if v.Kind() != Map { + panic(&ValueError{Method: "MapKeys", Kind: v.Kind()}) + } + + // empty map + if v.Len() == 0 { + return nil + } + + keys := make([]Value, 0, v.Len()) + + it := hashmapNewIterator() + k := New(v.typecode.Key()) + e := New(v.typecode.Elem()) + + for hashmapNext(v.pointer(), it, k.value, e.value) { + keys = append(keys, k.Elem()) + k = New(v.typecode.Key()) + } + + return keys } func (v Value) MapIndex(key Value) Value { + 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() { + // type error? + panic(&ValueError{Method: "MapIndex"}) + } + + elemType := v.Type().Elem() + elem := New(elemType) + + if key.Kind() == String { + 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 + if key.isIndirect() { + keyptr = key.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{} + } + return elem.Elem() + } + panic("unimplemented: (reflect.Value).MapIndex()") } func (v Value) MapRange() *MapIter { - panic("unimplemented: (reflect.Value).MapRange()") + if v.Kind() != Map { + panic(&ValueError{Method: "MapRange", Kind: v.Kind()}) + } + + 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.pointer(), it.it, it.key.value, it.val.value) + if !ok { + it.done = true + } + + return ok } func (v Value) Set(x Value) { v.checkAddressable() + v.checkRO() if !v.typecode.AssignableTo(x.typecode) { panic("reflect: cannot set") } @@ -683,16 +931,18 @@ 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 default: - panic(&ValueError{Method: "SetBool"}) + panic(&ValueError{Method: "SetBool", Kind: v.Kind()}) } } func (v Value) SetInt(x int64) { v.checkAddressable() + v.checkRO() switch v.Kind() { case Int: *(*int)(v.value) = int(x) @@ -705,12 +955,13 @@ func (v Value) SetInt(x int64) { case Int64: *(*int64)(v.value) = x default: - panic(&ValueError{Method: "SetInt"}) + panic(&ValueError{Method: "SetInt", Kind: v.Kind()}) } } func (v Value) SetUint(x uint64) { v.checkAddressable() + v.checkRO() switch v.Kind() { case Uint: *(*uint)(v.value) = uint(x) @@ -725,41 +976,44 @@ 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()}) } } func (v Value) SetFloat(x float64) { v.checkAddressable() + v.checkRO() switch v.Kind() { case Float32: *(*float32)(v.value) = float32(x) case Float64: *(*float64)(v.value) = x default: - panic(&ValueError{Method: "SetFloat"}) + panic(&ValueError{Method: "SetFloat", Kind: v.Kind()}) } } func (v Value) SetComplex(x complex128) { v.checkAddressable() + v.checkRO() switch v.Kind() { case Complex64: *(*complex64)(v.value) = complex64(x) case Complex128: *(*complex128)(v.value) = x default: - panic(&ValueError{Method: "SetComplex"}) + panic(&ValueError{Method: "SetComplex", Kind: v.Kind()}) } } func (v Value) SetString(x string) { v.checkAddressable() + v.checkRO() switch v.Kind() { case String: *(*string)(v.value) = x default: - panic(&ValueError{Method: "SetString"}) + panic(&ValueError{Method: "SetString", Kind: v.Kind()}) } } @@ -772,7 +1026,17 @@ func (v Value) SetCap(n int) { } func (v Value) SetLen(n int) { - panic("unimplemented: (reflect.Value).SetLen()") + if v.typecode.Kind() != Slice { + 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") + } + hdr.len = uintptr(n) } func (v Value) checkAddressable() { @@ -781,31 +1045,102 @@ 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 { - 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()}) } +// 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 { panic("unimplemented: (reflect.Value).Convert()") } +//go:linkname slicePanic runtime.slicePanic +func slicePanic() + 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 || cap < 0 || len > cap || int(uintptr(len)) != len || int(uintptr(cap)) != cap { + slicePanic() + } + + 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: 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 // 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, } @@ -863,16 +1198,121 @@ 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.hashmapStringGetUnsafePtr +func hashmapStringGet(m unsafe.Pointer, key string, value unsafe.Pointer, valueSize uintptr) bool + +//go:linkname hashmapStringSet runtime.hashmapStringSetUnsafePtr +func hashmapStringSet(m unsafe.Pointer, key string, value unsafe.Pointer) + +//go:linkname hashmapBinaryGet runtime.hashmapBinaryGetUnsafePtr +func hashmapBinaryGet(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool + +//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.hashmapNextUnsafePtr +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()") + dst.checkRO() + + 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 !compatibleTypes { + panic("Copy: type mismatch: " + dst.typecode.String() + "/" + src.typecode.String()) + } + + dstbuf, dstlen := buflen(dst) + srcbuf, srclen := buflen(src) + + return sliceCopy(dstbuf, srcbuf, dstlen, srclen, dst.typecode.elem().Size()) +} + +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 buf, len +} + +//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 { + //TODO(dgrysk): v.checkRO() ? + 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. @@ -887,6 +1327,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() @@ -903,13 +1344,88 @@ 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) { - panic("unimplemented: (reflect.Value).SetMapIndex()") + if v.Kind() != Map { + 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") + } + + // if elem is the zero Value, it means delete + del := elem == Value{} + + if !del && elem.typecode != v.typecode.elem() { + panic("reflect.Value.SetMapIndex: incompatible types for value") + } + + if key.Kind() == String { + if del { + hashmapStringDelete(v.pointer(), *(*string)(key.value)) + } else { + 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() { + var keyptr unsafe.Pointer + if key.isIndirect() { + keyptr = key.value + } else { + keyptr = unsafe.Pointer(&key.value) + } + + if del { + hashmapBinaryDelete(v.pointer(), keyptr) + } else { + 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()") + } } // 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. @@ -918,12 +1434,62 @@ 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 +func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer + +// MakeMapWithSize creates a new map with the specified type and initial space +// for approximately n elements. +func MakeMapWithSize(typ Type, n int) Value { + + const ( + hashmapAlgorithmBinary uint8 = iota + hashmapAlgorithmString + hashmapAlgorithmInterface + ) + + if typ.Kind() != Map { + panic(&ValueError{"MakeMap", typ.Kind()}) + } + + if n < 0 { + panic("reflect.MakeMapWithSize: negative size hint") + } + + key := typ.Key().(*rawType) + val := typ.Elem().(*rawType) + + var alg uint8 + + if key.Kind() == String { + alg = hashmapAlgorithmString + } else if key.isBinary() { + alg = hashmapAlgorithmBinary + } else { + panic("reflect.MakeMap: invalid key type") + } + + m := hashmapMake(key.Size(), val.Size(), uintptr(n), alg) + + return Value{ + typecode: typ.(*rawType), + value: unsafe.Pointer(m), + flags: 0, + } } // MakeMap creates a new map with the specified type. func MakeMap(typ Type) Value { - panic("unimplemented: reflect.MakeMap()") + return MakeMapWithSize(typ, 8) } func (v Value) Call(in []Value) []Value { @@ -934,10 +1500,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()") } diff --git a/src/reflect/value_test.go b/src/reflect/value_test.go index 5698ede557..321fa35327 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,249 @@ 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 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)) + } + + 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) + } + } + + 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 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)) + } + +} + +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 +} diff --git a/src/runtime/chan.go b/src/runtime/chan.go index 5bc05929dc..511f0ab80f 100644 --- a/src/runtime/chan.go +++ b/src/runtime/chan.go @@ -148,10 +148,8 @@ 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) +func chanLenUnsafePtr(c unsafe.Pointer) int { + return chanLen((*channel)(c)) } // Return the capacity of this chan, called from the cap builtin. @@ -165,10 +163,8 @@ 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) +func chanCapUnsafePtr(c unsafe.Pointer) int { + return chanCap((*channel)(c)) } // resumeRX resumes the next receiver and returns the destination pointer. diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go index e00b223d4d..9f41542fe8 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) @@ -84,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: @@ -142,10 +151,8 @@ 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) +func hashmapLenUnsafePtr(m unsafe.Pointer) int { + return hashmapLen((*hashmap)(m)) } // Set a specified key to a given value. Grow the map if necessary. @@ -208,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 { @@ -299,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. // @@ -409,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 { @@ -418,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)) @@ -427,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 @@ -435,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 { @@ -459,6 +490,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)) @@ -468,6 +503,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 @@ -476,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 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 +} 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",