-
Notifications
You must be signed in to change notification settings - Fork 996
random reflection improvements #3470
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
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 d3dd8d7
reflect: add MapIndex
dgryski d570aeb
reflect: add SetMapIndex
dgryski 1f31bc2
reflect: handle indirect values in SetMapIndex
dgryski 5555672
reflect: handle Type.Key()
dgryski 090eeb0
reflect: use t.isNamed()
dgryski 4afb7ea
reflect: add MapKeys()
dgryski 46fa7d3
reflect: add MapRange()
dgryski 23b7c17
reflect,runtime: remove UnsafePointer wrappers
dgryski 8eb486f
reflect: add some slice reflect methods
dgryski 3bc4343
reflect: flesh out MakeMap for string keys
dgryski c6d8ff0
reflect: fix extra pointer in MakeMap
dgryski f686e1a
reflect: add pointerto() that works with rawTypes
dgryski 8686aa5
reflect: preallocate keys slice in MapKeys()
dgryski db1a0a3
reflect: use pointer() for accessing map pointer
dgryski 074ab03
reflect: set valueFlagIndirect for slices
dgryski c145f5b
reflect: simplify MakeSlice() panic logic
dgryski 4c8e69e
reflect: add stubs for channel operations
dgryski d981d75
reflect: use ValueError() in SetLen()
dgryski b5500c2
reflect: fix panic message
dgryski d6af53b
reflect: add OverflowFloat and OverflowUint
dgryski dfe1e82
reflect: better setlen panic message
dgryski 07c5a6a
reflect: add binary map keys
dgryski 5a96f37
compiler,reflect: add Name() support
dgryski 091ad63
reflect: add FieldByName and FieldByIndex
dgryski d5bf5bc
reflect,runtime: add LLVM14 unsafe.Pointer compat wrappers
dgryski 1e66a53
reflect, runtime: a zero reflect.Value in SetMapIndex means delete()
dgryski 5319c59
reflect: MapIndex returns Value{} if no key found
dgryski ffc3f85
reflect: fix SetMapIndex panic messages
dgryski 505c4a5
reflect: add unit test to cover all the new goodies
dgryski ab9f881
compiler: update unit tests golden output
dgryski 2d77299
reflect: uncomment a bunch of deep equal tests that now pass
dgryski 2c08136
reflect: todo++ to handle undef padding bytes
dgryski 5c22c9d
targets: bump default-stack-size so testdata/json.go runs
dgryski 2e28880
reflect: add MakeMapWithSize and fix MakeMap size hint
dgryski 5e67190
Makefile: more stdlib packages thanks to reflect improvements
dgryski 87757bb
Makefile: move mime/multipart to linux tests only
dgryski File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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)) | ||||||
soypat marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| } | ||||||
|
|
||||||
| 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), | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
|
||||||
| } | ||||||
| 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{ | ||||||
|
|
||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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,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,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 | ||
dgryski marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| 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
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 { | ||
|
|
@@ -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()") | ||
dgryski marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
|
|
||
| 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 { | ||
|
|
@@ -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()") | ||
| } | ||
|
|
@@ -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)) | ||
dgryski marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| 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 +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 | ||
dgryski marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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. | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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.