Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
2c9bb65
compiler, reflect: add map key and element type info
dgryski Feb 15, 2023
d3dd8d7
reflect: add MapIndex
dgryski Feb 15, 2023
d570aeb
reflect: add SetMapIndex
dgryski Feb 15, 2023
1f31bc2
reflect: handle indirect values in SetMapIndex
dgryski Feb 15, 2023
5555672
reflect: handle Type.Key()
dgryski Feb 15, 2023
090eeb0
reflect: use t.isNamed()
dgryski Feb 15, 2023
4afb7ea
reflect: add MapKeys()
dgryski Feb 15, 2023
46fa7d3
reflect: add MapRange()
dgryski Feb 15, 2023
23b7c17
reflect,runtime: remove UnsafePointer wrappers
dgryski Feb 16, 2023
8eb486f
reflect: add some slice reflect methods
dgryski Feb 16, 2023
3bc4343
reflect: flesh out MakeMap for string keys
dgryski Feb 16, 2023
c6d8ff0
reflect: fix extra pointer in MakeMap
dgryski Feb 20, 2023
f686e1a
reflect: add pointerto() that works with rawTypes
dgryski Feb 20, 2023
8686aa5
reflect: preallocate keys slice in MapKeys()
dgryski Feb 20, 2023
db1a0a3
reflect: use pointer() for accessing map pointer
dgryski Feb 20, 2023
074ab03
reflect: set valueFlagIndirect for slices
dgryski Feb 20, 2023
c145f5b
reflect: simplify MakeSlice() panic logic
dgryski Feb 20, 2023
4c8e69e
reflect: add stubs for channel operations
dgryski Feb 20, 2023
d981d75
reflect: use ValueError() in SetLen()
dgryski Feb 21, 2023
b5500c2
reflect: fix panic message
dgryski Feb 21, 2023
d6af53b
reflect: add OverflowFloat and OverflowUint
dgryski Feb 21, 2023
dfe1e82
reflect: better setlen panic message
dgryski Feb 21, 2023
07c5a6a
reflect: add binary map keys
dgryski Feb 21, 2023
5a96f37
compiler,reflect: add Name() support
dgryski Feb 21, 2023
091ad63
reflect: add FieldByName and FieldByIndex
dgryski Feb 21, 2023
d5bf5bc
reflect,runtime: add LLVM14 unsafe.Pointer compat wrappers
dgryski Feb 21, 2023
1e66a53
reflect, runtime: a zero reflect.Value in SetMapIndex means delete()
dgryski Feb 21, 2023
5319c59
reflect: MapIndex returns Value{} if no key found
dgryski Feb 22, 2023
ffc3f85
reflect: fix SetMapIndex panic messages
dgryski Feb 22, 2023
505c4a5
reflect: add unit test to cover all the new goodies
dgryski Feb 22, 2023
ab9f881
compiler: update unit tests golden output
dgryski Feb 22, 2023
2d77299
reflect: uncomment a bunch of deep equal tests that now pass
dgryski Feb 22, 2023
2c08136
reflect: todo++ to handle undef padding bytes
dgryski Feb 22, 2023
5c22c9d
targets: bump default-stack-size so testdata/json.go runs
dgryski Feb 23, 2023
2e28880
reflect: add MakeMapWithSize and fix MakeMap size hint
dgryski Feb 23, 2023
5e67190
Makefile: more stdlib packages thanks to reflect improvements
dgryski Feb 23, 2023
87757bb
Makefile: move mime/multipart to linux tests only
dgryski Feb 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ TEST_PACKAGES_FAST = \
net \
net/http/internal/ascii \
net/mail \
net/textproto \
os \
path \
reflect \
Expand Down Expand Up @@ -340,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
Expand All @@ -356,6 +358,7 @@ TEST_PACKAGES_LINUX := \
debug/plan9obj \
image \
io/ioutil \
mime/multipart \
mime/quotedprintable \
strconv \
testing/fstest \
Expand Down
19 changes: 17 additions & 2 deletions compiler/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())))),
)
Comment on lines 112 to 117
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are missing the corresponding update to the comment at the top of src/reflect/type.go.

case *types.Chan, *types.Slice:
typeFieldTypes = append(typeFieldTypes,
Expand All @@ -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,
Expand Down Expand Up @@ -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),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this has the same effect and may be a bit faster or more readable:

Suggested change
llvm.ConstArray(c.ctx.Int8Type(), buf),
llvm.ConstString(name, false),

}
metabyte |= 1 << 5 // "named" flag
case *types.Chan:
Expand All @@ -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{
Expand Down
2 changes: 1 addition & 1 deletion compiler/testdata/interface.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 12 additions & 12 deletions src/reflect/all_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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},
Expand All @@ -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},
Expand All @@ -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) {
Expand Down
139 changes: 127 additions & 12 deletions src/reflect/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -408,6 +410,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
Expand All @@ -430,31 +447,45 @@ 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
}

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
Comment on lines 476 to +477
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

}

}

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 {
Expand All @@ -472,13 +503,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 {
Expand Down Expand Up @@ -768,6 +807,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()")
}
Expand All @@ -793,11 +855,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 {
Expand All @@ -816,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.
Expand Down
Loading