reflect: add support for struct types
Этот коммит содержится в:
родитель
5012be337f
коммит
e2c8654237
9 изменённых файлов: 654 добавлений и 134 удалений
|
@ -53,21 +53,28 @@ func (c *Compiler) getTypeCode(typ types.Type) llvm.Value {
|
||||||
// Some type classes contain more information for underlying types or
|
// Some type classes contain more information for underlying types or
|
||||||
// element types. Store it directly in the typecode global to make
|
// element types. Store it directly in the typecode global to make
|
||||||
// reflect lowering simpler.
|
// reflect lowering simpler.
|
||||||
var elementType types.Type
|
var references llvm.Value
|
||||||
switch typ := typ.(type) {
|
switch typ := typ.(type) {
|
||||||
case *types.Named:
|
case *types.Named:
|
||||||
elementType = typ.Underlying()
|
references = c.getTypeCode(typ.Underlying())
|
||||||
case *types.Chan:
|
case *types.Chan:
|
||||||
elementType = typ.Elem()
|
references = c.getTypeCode(typ.Elem())
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
elementType = typ.Elem()
|
references = c.getTypeCode(typ.Elem())
|
||||||
case *types.Slice:
|
case *types.Slice:
|
||||||
elementType = typ.Elem()
|
references = c.getTypeCode(typ.Elem())
|
||||||
|
case *types.Struct:
|
||||||
|
// Take a pointer to the typecodeID of the first field (if it exists).
|
||||||
|
structGlobal := c.makeStructTypeFields(typ)
|
||||||
|
references = llvm.ConstGEP(structGlobal, []llvm.Value{
|
||||||
|
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
||||||
|
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if elementType != nil {
|
if !references.IsNil() {
|
||||||
// Set the 'references' field of the runtime.typecodeID struct.
|
// Set the 'references' field of the runtime.typecodeID struct.
|
||||||
globalValue := c.getZeroValue(global.Type().ElementType())
|
globalValue := c.getZeroValue(global.Type().ElementType())
|
||||||
globalValue = llvm.ConstInsertValue(globalValue, c.getTypeCode(elementType), []uint32{0})
|
globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
|
||||||
global.SetInitializer(globalValue)
|
global.SetInitializer(globalValue)
|
||||||
global.SetLinkage(llvm.PrivateLinkage)
|
global.SetLinkage(llvm.PrivateLinkage)
|
||||||
}
|
}
|
||||||
|
@ -76,6 +83,48 @@ func (c *Compiler) getTypeCode(typ types.Type) llvm.Value {
|
||||||
return global
|
return global
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// makeStructTypeFields creates a new global that stores all type information
|
||||||
|
// related to this struct type, and returns the resulting global. This global is
|
||||||
|
// actually an array of all the fields in the structs.
|
||||||
|
func (c *Compiler) makeStructTypeFields(typ *types.Struct) llvm.Value {
|
||||||
|
// The global is an array of runtime.structField structs.
|
||||||
|
runtimeStructField := c.getLLVMRuntimeType("structField")
|
||||||
|
structGlobalType := llvm.ArrayType(runtimeStructField, typ.NumFields())
|
||||||
|
structGlobal := llvm.AddGlobal(c.mod, structGlobalType, "reflect/types.structFields")
|
||||||
|
structGlobalValue := c.getZeroValue(structGlobalType)
|
||||||
|
for i := 0; i < typ.NumFields(); i++ {
|
||||||
|
fieldGlobalValue := c.getZeroValue(runtimeStructField)
|
||||||
|
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, c.getTypeCode(typ.Field(i).Type()), []uint32{0})
|
||||||
|
fieldName := c.makeGlobalBytes([]byte(typ.Field(i).Name()), "reflect/types.structFieldName")
|
||||||
|
fieldName = llvm.ConstGEP(fieldName, []llvm.Value{
|
||||||
|
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
||||||
|
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
||||||
|
})
|
||||||
|
fieldName.SetLinkage(llvm.PrivateLinkage)
|
||||||
|
fieldName.SetUnnamedAddr(true)
|
||||||
|
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldName, []uint32{1})
|
||||||
|
if typ.Tag(i) != "" {
|
||||||
|
fieldTag := c.makeGlobalBytes([]byte(typ.Tag(i)), "reflect/types.structFieldTag")
|
||||||
|
fieldTag = llvm.ConstGEP(fieldTag, []llvm.Value{
|
||||||
|
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
||||||
|
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
||||||
|
})
|
||||||
|
fieldTag.SetLinkage(llvm.PrivateLinkage)
|
||||||
|
fieldTag.SetUnnamedAddr(true)
|
||||||
|
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldTag, []uint32{2})
|
||||||
|
}
|
||||||
|
if typ.Field(i).Embedded() {
|
||||||
|
fieldEmbedded := llvm.ConstInt(c.ctx.Int1Type(), 1, false)
|
||||||
|
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldEmbedded, []uint32{3})
|
||||||
|
}
|
||||||
|
structGlobalValue = llvm.ConstInsertValue(structGlobalValue, fieldGlobalValue, []uint32{uint32(i)})
|
||||||
|
}
|
||||||
|
structGlobal.SetInitializer(structGlobalValue)
|
||||||
|
structGlobal.SetUnnamedAddr(true)
|
||||||
|
structGlobal.SetLinkage(llvm.PrivateLinkage)
|
||||||
|
return structGlobal
|
||||||
|
}
|
||||||
|
|
||||||
// getTypeCodeName returns a name for this type that can be used in the
|
// getTypeCodeName returns a name for this type that can be used in the
|
||||||
// interface lowering pass to assign type codes as expected by the reflect
|
// interface lowering pass to assign type codes as expected by the reflect
|
||||||
// package. See getTypeCodeNum.
|
// package. See getTypeCodeNum.
|
||||||
|
|
|
@ -152,3 +152,46 @@ func (c *Compiler) splitBasicBlock(afterInst llvm.Value, insertAfter llvm.BasicB
|
||||||
|
|
||||||
return newBlock
|
return newBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// makeGlobalBytes creates a new LLVM global with the given name and bytes as
|
||||||
|
// contents, and returns the global.
|
||||||
|
// Note that it is left with the default linkage etc., you should set
|
||||||
|
// linkage/constant/etc properties yourself.
|
||||||
|
func (c *Compiler) makeGlobalBytes(buf []byte, name string) llvm.Value {
|
||||||
|
globalType := llvm.ArrayType(c.ctx.Int8Type(), len(buf))
|
||||||
|
global := llvm.AddGlobal(c.mod, globalType, name)
|
||||||
|
value := llvm.Undef(globalType)
|
||||||
|
for i, ch := range buf {
|
||||||
|
value = llvm.ConstInsertValue(value, llvm.ConstInt(c.ctx.Int8Type(), uint64(ch), false), []uint32{uint32(i)})
|
||||||
|
}
|
||||||
|
global.SetInitializer(value)
|
||||||
|
return global
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGlobalBytes returns the byte slice contained in the i8 array of the
|
||||||
|
// provided global. It can recover the bytes originally created using
|
||||||
|
// makeGlobalBytes.
|
||||||
|
func getGlobalBytes(global llvm.Value) []byte {
|
||||||
|
value := global.Initializer()
|
||||||
|
buf := make([]byte, value.Type().ArrayLength())
|
||||||
|
for i := range buf {
|
||||||
|
buf[i] = byte(llvm.ConstExtractValue(value, []uint32{uint32(i)}).ZExtValue())
|
||||||
|
}
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// replaceGlobalByteWithArray replaces a global i8 in the module with a byte
|
||||||
|
// array, using a GEP to make the types match. It is a convenience function used
|
||||||
|
// for creating reflection sidetables, for example.
|
||||||
|
func (c *Compiler) replaceGlobalByteWithArray(name string, buf []byte) llvm.Value {
|
||||||
|
global := c.makeGlobalBytes(buf, name+".tmp")
|
||||||
|
oldGlobal := c.mod.NamedGlobal(name)
|
||||||
|
gep := llvm.ConstGEP(global, []llvm.Value{
|
||||||
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||||
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||||
|
})
|
||||||
|
oldGlobal.ReplaceAllUsesWith(gep)
|
||||||
|
oldGlobal.EraseFromParentAsGlobal()
|
||||||
|
global.SetName(name)
|
||||||
|
return global
|
||||||
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ package compiler
|
||||||
// non-basic types have their underlying type stored in a sidetable.
|
// non-basic types have their underlying type stored in a sidetable.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"go/ast"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -65,11 +67,25 @@ type typeCodeAssignmentState struct {
|
||||||
// package (or are simply unused in the compiled program).
|
// package (or are simply unused in the compiled program).
|
||||||
fallbackIndex int
|
fallbackIndex int
|
||||||
|
|
||||||
|
// This is the length of an uintptr. Only used occasionally to know whether
|
||||||
|
// a given number can be encoded as a varint.
|
||||||
|
uintptrLen int
|
||||||
|
|
||||||
// Map of named types to their type code. It is important that named types
|
// Map of named types to their type code. It is important that named types
|
||||||
// get unique IDs for each type.
|
// get unique IDs for each type.
|
||||||
namedBasicTypes map[string]int
|
namedBasicTypes map[string]int
|
||||||
namedNonBasicTypes map[string]int
|
namedNonBasicTypes map[string]int
|
||||||
|
|
||||||
|
// Map of struct types to their type code.
|
||||||
|
structTypes map[string]int
|
||||||
|
structTypesSidetable []byte
|
||||||
|
needsStructNamesSidetable bool
|
||||||
|
|
||||||
|
// Map of struct names and tags to their name string.
|
||||||
|
structNames map[string]int
|
||||||
|
structNamesSidetable []byte
|
||||||
|
needsStructTypesSidetable bool
|
||||||
|
|
||||||
// This byte array is stored in reflect.namedNonBasicTypesSidetable and is
|
// This byte array is stored in reflect.namedNonBasicTypesSidetable and is
|
||||||
// used at runtime to get details about a named non-basic type.
|
// used at runtime to get details about a named non-basic type.
|
||||||
// Entries are varints (see makeVarint below and readVarint in
|
// Entries are varints (see makeVarint below and readVarint in
|
||||||
|
@ -82,10 +98,6 @@ type typeCodeAssignmentState struct {
|
||||||
// needsNamedTypesSidetable.
|
// needsNamedTypesSidetable.
|
||||||
namedNonBasicTypesSidetable []byte
|
namedNonBasicTypesSidetable []byte
|
||||||
|
|
||||||
// This is the length of an uintptr. Only used occasionally to know whether
|
|
||||||
// a given number can be encoded as a varint.
|
|
||||||
uintptrLen int
|
|
||||||
|
|
||||||
// This indicates whether namedNonBasicTypesSidetable needs to be created at
|
// This indicates whether namedNonBasicTypesSidetable needs to be created at
|
||||||
// all. If it is false, namedNonBasicTypesSidetable will contain simple
|
// all. If it is false, namedNonBasicTypesSidetable will contain simple
|
||||||
// monotonically increasing numbers.
|
// monotonically increasing numbers.
|
||||||
|
@ -109,13 +121,17 @@ func (c *Compiler) assignTypeCodes(typeSlice typeInfoSlice) {
|
||||||
// Assign typecodes the way the reflect package expects.
|
// Assign typecodes the way the reflect package expects.
|
||||||
state := typeCodeAssignmentState{
|
state := typeCodeAssignmentState{
|
||||||
fallbackIndex: 1,
|
fallbackIndex: 1,
|
||||||
|
uintptrLen: c.uintptrType.IntTypeWidth(),
|
||||||
namedBasicTypes: make(map[string]int),
|
namedBasicTypes: make(map[string]int),
|
||||||
namedNonBasicTypes: make(map[string]int),
|
namedNonBasicTypes: make(map[string]int),
|
||||||
uintptrLen: c.uintptrType.IntTypeWidth(),
|
structTypes: make(map[string]int),
|
||||||
|
structNames: make(map[string]int),
|
||||||
needsNamedNonBasicTypesSidetable: len(getUses(c.mod.NamedGlobal("reflect.namedNonBasicTypesSidetable"))) != 0,
|
needsNamedNonBasicTypesSidetable: len(getUses(c.mod.NamedGlobal("reflect.namedNonBasicTypesSidetable"))) != 0,
|
||||||
|
needsStructTypesSidetable: len(getUses(c.mod.NamedGlobal("reflect.structTypesSidetable"))) != 0,
|
||||||
|
needsStructNamesSidetable: len(getUses(c.mod.NamedGlobal("reflect.structNamesSidetable"))) != 0,
|
||||||
}
|
}
|
||||||
for _, t := range typeSlice {
|
for _, t := range typeSlice {
|
||||||
num := c.getTypeCodeNum(t.typecode, &state)
|
num := state.getTypeCodeNum(t.typecode)
|
||||||
if num.BitLen() > c.uintptrType.IntTypeWidth() || !num.IsUint64() {
|
if num.BitLen() > c.uintptrType.IntTypeWidth() || !num.IsUint64() {
|
||||||
// TODO: support this in some way, using a side table for example.
|
// TODO: support this in some way, using a side table for example.
|
||||||
// That's less efficient but better than not working at all.
|
// That's less efficient but better than not working at all.
|
||||||
|
@ -128,29 +144,26 @@ func (c *Compiler) assignTypeCodes(typeSlice typeInfoSlice) {
|
||||||
|
|
||||||
// Only create this sidetable when it is necessary.
|
// Only create this sidetable when it is necessary.
|
||||||
if state.needsNamedNonBasicTypesSidetable {
|
if state.needsNamedNonBasicTypesSidetable {
|
||||||
// Create the sidetable and replace the old dummy global with this value.
|
global := c.replaceGlobalByteWithArray("reflect.namedNonBasicTypesSidetable", state.namedNonBasicTypesSidetable)
|
||||||
globalType := llvm.ArrayType(c.ctx.Int8Type(), len(state.namedNonBasicTypesSidetable))
|
global.SetLinkage(llvm.InternalLinkage)
|
||||||
global := llvm.AddGlobal(c.mod, globalType, "reflect.namedNonBasicTypesSidetable.tmp")
|
global.SetUnnamedAddr(true)
|
||||||
value := llvm.Undef(globalType)
|
}
|
||||||
for i, ch := range state.namedNonBasicTypesSidetable {
|
if state.needsStructTypesSidetable {
|
||||||
value = llvm.ConstInsertValue(value, llvm.ConstInt(c.ctx.Int8Type(), uint64(ch), false), []uint32{uint32(i)})
|
global := c.replaceGlobalByteWithArray("reflect.structTypesSidetable", state.structTypesSidetable)
|
||||||
}
|
global.SetLinkage(llvm.InternalLinkage)
|
||||||
global.SetInitializer(value)
|
global.SetUnnamedAddr(true)
|
||||||
oldGlobal := c.mod.NamedGlobal("reflect.namedNonBasicTypesSidetable")
|
}
|
||||||
gep := llvm.ConstGEP(global, []llvm.Value{
|
if state.needsStructNamesSidetable {
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
global := c.replaceGlobalByteWithArray("reflect.structNamesSidetable", state.structNamesSidetable)
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
global.SetLinkage(llvm.InternalLinkage)
|
||||||
})
|
global.SetUnnamedAddr(true)
|
||||||
oldGlobal.ReplaceAllUsesWith(gep)
|
|
||||||
oldGlobal.EraseFromParentAsGlobal()
|
|
||||||
global.SetName("reflect.namedNonBasicTypesSidetable")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getTypeCodeNum returns the typecode for a given type as expected by the
|
// getTypeCodeNum returns the typecode for a given type as expected by the
|
||||||
// reflect package. Also see getTypeCodeName, which serializes types to a string
|
// reflect package. Also see getTypeCodeName, which serializes types to a string
|
||||||
// based on a types.Type value for this function.
|
// based on a types.Type value for this function.
|
||||||
func (c *Compiler) getTypeCodeNum(typecode llvm.Value, state *typeCodeAssignmentState) *big.Int {
|
func (state *typeCodeAssignmentState) getTypeCodeNum(typecode llvm.Value) *big.Int {
|
||||||
// Note: see src/reflect/type.go for bit allocations.
|
// Note: see src/reflect/type.go for bit allocations.
|
||||||
class, value := getClassAndValueFromTypeCode(typecode)
|
class, value := getClassAndValueFromTypeCode(typecode)
|
||||||
name := ""
|
name := ""
|
||||||
|
@ -186,7 +199,7 @@ func (c *Compiler) getTypeCodeNum(typecode llvm.Value, state *typeCodeAssignment
|
||||||
switch class {
|
switch class {
|
||||||
case "chan":
|
case "chan":
|
||||||
sub := llvm.ConstExtractValue(typecode.Initializer(), []uint32{0})
|
sub := llvm.ConstExtractValue(typecode.Initializer(), []uint32{0})
|
||||||
num = c.getTypeCodeNum(sub, state)
|
num = state.getTypeCodeNum(sub)
|
||||||
classNumber = 0
|
classNumber = 0
|
||||||
case "interface":
|
case "interface":
|
||||||
num = big.NewInt(int64(state.fallbackIndex))
|
num = big.NewInt(int64(state.fallbackIndex))
|
||||||
|
@ -194,11 +207,11 @@ func (c *Compiler) getTypeCodeNum(typecode llvm.Value, state *typeCodeAssignment
|
||||||
classNumber = 1
|
classNumber = 1
|
||||||
case "pointer":
|
case "pointer":
|
||||||
sub := llvm.ConstExtractValue(typecode.Initializer(), []uint32{0})
|
sub := llvm.ConstExtractValue(typecode.Initializer(), []uint32{0})
|
||||||
num = c.getTypeCodeNum(sub, state)
|
num = state.getTypeCodeNum(sub)
|
||||||
classNumber = 2
|
classNumber = 2
|
||||||
case "slice":
|
case "slice":
|
||||||
sub := llvm.ConstExtractValue(typecode.Initializer(), []uint32{0})
|
sub := llvm.ConstExtractValue(typecode.Initializer(), []uint32{0})
|
||||||
num = c.getTypeCodeNum(sub, state)
|
num = state.getTypeCodeNum(sub)
|
||||||
classNumber = 3
|
classNumber = 3
|
||||||
case "array":
|
case "array":
|
||||||
num = big.NewInt(int64(state.fallbackIndex))
|
num = big.NewInt(int64(state.fallbackIndex))
|
||||||
|
@ -213,8 +226,7 @@ func (c *Compiler) getTypeCodeNum(typecode llvm.Value, state *typeCodeAssignment
|
||||||
state.fallbackIndex++
|
state.fallbackIndex++
|
||||||
classNumber = 6
|
classNumber = 6
|
||||||
case "struct":
|
case "struct":
|
||||||
num = big.NewInt(int64(state.fallbackIndex))
|
num = big.NewInt(int64(state.getStructTypeNum(typecode)))
|
||||||
state.fallbackIndex++
|
|
||||||
classNumber = 7
|
classNumber = 7
|
||||||
default:
|
default:
|
||||||
panic("unknown type kind: " + class)
|
panic("unknown type kind: " + class)
|
||||||
|
@ -283,36 +295,125 @@ func (state *typeCodeAssignmentState) getNonBasicNamedTypeNum(name string, value
|
||||||
return num
|
return num
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeVarint encodes a varint in a way that should be easy to decode.
|
// getStructTypeNum returns the struct type number, which is an index into
|
||||||
// It may need to be decoded very quickly at runtime at low-powered processors
|
// reflect.structTypesSidetable or an unique number for every struct if this
|
||||||
// so should be efficient to decode.
|
// sidetable is not needed in the to-be-compiled program.
|
||||||
// The current algorithm is probably not even close to efficient, but it is easy
|
func (state *typeCodeAssignmentState) getStructTypeNum(typecode llvm.Value) int {
|
||||||
// to change as the format is only used inside the same program.
|
name := typecode.Name()
|
||||||
func makeVarint(n uint64) []byte {
|
if num, ok := state.structTypes[name]; ok {
|
||||||
// This is the reverse of what src/runtime/sidetables.go does.
|
// This struct already has an assigned type code.
|
||||||
buf := make([]byte, 0, 8)
|
return num
|
||||||
for {
|
}
|
||||||
c := byte(n & 0x7f << 1)
|
|
||||||
n >>= 7
|
if !state.needsStructTypesSidetable {
|
||||||
if n != 0 {
|
// We don't need struct sidetables, so we can just assign monotonically
|
||||||
c |= 1
|
// increasing numbers to each struct type.
|
||||||
|
num := len(state.structTypes)
|
||||||
|
state.structTypes[name] = num
|
||||||
|
return num
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the fields this struct type contains.
|
||||||
|
// The struct number will be the start index of
|
||||||
|
structTypeGlobal := llvm.ConstExtractValue(typecode.Initializer(), []uint32{0}).Operand(0).Initializer()
|
||||||
|
numFields := structTypeGlobal.Type().ArrayLength()
|
||||||
|
|
||||||
|
// The first data that is stored in the struct sidetable is the number of
|
||||||
|
// fields this struct contains. This is usually just a single byte because
|
||||||
|
// most structs don't contain that many fields, but make it a varint just
|
||||||
|
// to be sure.
|
||||||
|
buf := makeVarint(uint64(numFields))
|
||||||
|
|
||||||
|
// Iterate over every field in the struct.
|
||||||
|
// Every field is stored sequentially in the struct sidetable. Fields can
|
||||||
|
// be retrieved from this list of fields at runtime by iterating over all
|
||||||
|
// of them until the right field has been found.
|
||||||
|
// Perhaps adding some index would speed things up, but it would also make
|
||||||
|
// the sidetable bigger.
|
||||||
|
for i := 0; i < numFields; i++ {
|
||||||
|
// Collect some information about this field.
|
||||||
|
field := llvm.ConstExtractValue(structTypeGlobal, []uint32{uint32(i)})
|
||||||
|
|
||||||
|
nameGlobal := llvm.ConstExtractValue(field, []uint32{1})
|
||||||
|
if nameGlobal == llvm.ConstPointerNull(nameGlobal.Type()) {
|
||||||
|
panic("compiler: no name for this struct field")
|
||||||
}
|
}
|
||||||
buf = append(buf, c)
|
fieldNameBytes := getGlobalBytes(nameGlobal.Operand(0))
|
||||||
if n == 0 {
|
fieldNameNumber := state.getStructNameNumber(fieldNameBytes)
|
||||||
break
|
|
||||||
|
// See whether this struct field has an associated tag, and if so,
|
||||||
|
// store that tag in the tags sidetable.
|
||||||
|
tagGlobal := llvm.ConstExtractValue(field, []uint32{2})
|
||||||
|
hasTag := false
|
||||||
|
tagNumber := 0
|
||||||
|
if tagGlobal != llvm.ConstPointerNull(tagGlobal.Type()) {
|
||||||
|
hasTag = true
|
||||||
|
tagBytes := getGlobalBytes(tagGlobal.Operand(0))
|
||||||
|
tagNumber = state.getStructNameNumber(tagBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The 'embedded' or 'anonymous' flag for this field.
|
||||||
|
embedded := llvm.ConstExtractValue(field, []uint32{3}).ZExtValue() != 0
|
||||||
|
|
||||||
|
// The first byte in the struct types sidetable is a flags byte with
|
||||||
|
// two bits in it.
|
||||||
|
flagsByte := byte(0)
|
||||||
|
if embedded {
|
||||||
|
flagsByte |= 1
|
||||||
|
}
|
||||||
|
if hasTag {
|
||||||
|
flagsByte |= 2
|
||||||
|
}
|
||||||
|
if ast.IsExported(string(fieldNameBytes)) {
|
||||||
|
flagsByte |= 4
|
||||||
|
}
|
||||||
|
buf = append(buf, flagsByte)
|
||||||
|
|
||||||
|
// Get the type number and add it to the buffer.
|
||||||
|
// All fields have a type, so include it directly here.
|
||||||
|
typeNum := state.getTypeCodeNum(llvm.ConstExtractValue(field, []uint32{0}))
|
||||||
|
if typeNum.BitLen() > state.uintptrLen || !typeNum.IsUint64() {
|
||||||
|
// TODO: make this a regular error
|
||||||
|
panic("struct field has a type code that is too big")
|
||||||
|
}
|
||||||
|
buf = append(buf, makeVarint(typeNum.Uint64())...)
|
||||||
|
|
||||||
|
// Add the name.
|
||||||
|
buf = append(buf, makeVarint(uint64(fieldNameNumber))...)
|
||||||
|
|
||||||
|
// Add the tag, if there is one.
|
||||||
|
if hasTag {
|
||||||
|
buf = append(buf, makeVarint(uint64(tagNumber))...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reverseBytes(buf)
|
|
||||||
return buf
|
num := len(state.structTypesSidetable)
|
||||||
|
state.structTypes[name] = num
|
||||||
|
state.structTypesSidetable = append(state.structTypesSidetable, buf...)
|
||||||
|
return num
|
||||||
}
|
}
|
||||||
|
|
||||||
func reverseBytes(s []byte) {
|
// getStructNameNumber stores this string (name or tag) onto the struct names
|
||||||
// Actually copied from https://blog.golang.org/why-generics
|
// sidetable. The format is a varint of the length of the struct, followed by
|
||||||
first := 0
|
// the raw bytes of the name. Multiple identical strings are stored under the
|
||||||
last := len(s) - 1
|
// same name for space efficiency.
|
||||||
for first < last {
|
func (state *typeCodeAssignmentState) getStructNameNumber(nameBytes []byte) int {
|
||||||
s[first], s[last] = s[last], s[first]
|
name := string(nameBytes)
|
||||||
first++
|
if n, ok := state.structNames[name]; ok {
|
||||||
last--
|
// This name was used before, re-use it now (for space efficiency).
|
||||||
|
return n
|
||||||
}
|
}
|
||||||
|
// This name is not yet in the names sidetable. Add it now.
|
||||||
|
n := len(state.structNamesSidetable)
|
||||||
|
state.structNames[name] = n
|
||||||
|
state.structNamesSidetable = append(state.structNamesSidetable, makeVarint(uint64(len(nameBytes)))...)
|
||||||
|
state.structNamesSidetable = append(state.structNamesSidetable, nameBytes...)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeVarint is a small helper function that returns the bytes of the number in
|
||||||
|
// varint encoding.
|
||||||
|
func makeVarint(n uint64) []byte {
|
||||||
|
buf := make([]byte, binary.MaxVarintLen64)
|
||||||
|
return buf[:binary.PutUvarint(buf, n)]
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,22 +10,48 @@ import (
|
||||||
//go:extern reflect.namedNonBasicTypesSidetable
|
//go:extern reflect.namedNonBasicTypesSidetable
|
||||||
var namedNonBasicTypesSidetable byte
|
var namedNonBasicTypesSidetable byte
|
||||||
|
|
||||||
func readVarint(buf unsafe.Pointer) Type {
|
//go:extern reflect.structTypesSidetable
|
||||||
var t Type
|
var structTypesSidetable byte
|
||||||
|
|
||||||
|
//go:extern reflect.structNamesSidetable
|
||||||
|
var structNamesSidetable byte
|
||||||
|
|
||||||
|
// readStringSidetable reads a string from the given table (like
|
||||||
|
// structNamesSidetable) and returns this string. No heap allocation is
|
||||||
|
// necessary because it makes the string point directly to the raw bytes of the
|
||||||
|
// table.
|
||||||
|
func readStringSidetable(table unsafe.Pointer, index uintptr) string {
|
||||||
|
nameLen, namePtr := readVarint(unsafe.Pointer(uintptr(table) + index))
|
||||||
|
return *(*string)(unsafe.Pointer(&StringHeader{
|
||||||
|
Data: uintptr(namePtr),
|
||||||
|
Len: nameLen,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// readVarint decodes a varint as used in the encoding/binary package.
|
||||||
|
// It has an input pointer and returns the read varint and the pointer
|
||||||
|
// incremented to the next field in the data structure, just after the varint.
|
||||||
|
//
|
||||||
|
// Details:
|
||||||
|
// https://github.com/golang/go/blob/e37a1b1c/src/encoding/binary/varint.go#L7-L25
|
||||||
|
func readVarint(buf unsafe.Pointer) (uintptr, unsafe.Pointer) {
|
||||||
|
var n uintptr
|
||||||
|
shift := uintptr(0)
|
||||||
for {
|
for {
|
||||||
// Read the next byte.
|
// Read the next byte in the buffer.
|
||||||
c := *(*byte)(buf)
|
c := *(*byte)(buf)
|
||||||
|
|
||||||
// Add this byte to the type code. The upper 7 bits are the value.
|
// Decode the bits from this byte and add them to the output number.
|
||||||
t = t<<7 | Type(c>>1)
|
n |= uintptr(c&0x7f) << shift
|
||||||
|
shift += 7
|
||||||
// Check whether this is the last byte of this varint. The lower bit
|
|
||||||
// indicates whether any bytes follow.
|
|
||||||
if c%1 == 0 {
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increment the buf pointer (pointer arithmetic!).
|
// Increment the buf pointer (pointer arithmetic!).
|
||||||
buf = unsafe.Pointer(uintptr(buf) + 1)
|
buf = unsafe.Pointer(uintptr(buf) + 1)
|
||||||
|
|
||||||
|
// Check whether this is the last byte of this varint. The upper bit
|
||||||
|
// (msb) indicates whether any bytes follow.
|
||||||
|
if c>>7 == 0 {
|
||||||
|
return n, buf
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,23 +145,102 @@ func (t Type) Kind() Kind {
|
||||||
func (t Type) Elem() Type {
|
func (t Type) Elem() Type {
|
||||||
switch t.Kind() {
|
switch t.Kind() {
|
||||||
case Chan, Ptr, Slice:
|
case Chan, Ptr, Slice:
|
||||||
// Look at the 'n' bit in the type code (see the top of this file) to
|
return t.stripPrefix()
|
||||||
// see whether this is a named type.
|
|
||||||
if (t>>4)%2 != 0 {
|
|
||||||
// This is a named type. The element type is stored in a sidetable.
|
|
||||||
namedTypeNum := t >> 5
|
|
||||||
return readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&namedNonBasicTypesSidetable)) + uintptr(namedTypeNum)))
|
|
||||||
}
|
|
||||||
// Not a named type, so the element type is stored directly in the type
|
|
||||||
// code.
|
|
||||||
return t >> 5
|
|
||||||
default: // not implemented: Array, Map
|
default: // not implemented: Array, Map
|
||||||
panic("unimplemented: (reflect.Type).Elem()")
|
panic("unimplemented: (reflect.Type).Elem()")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stripPrefix removes the "prefix" (the first 5 bytes of the type code) from
|
||||||
|
// the type code. If this is a named type, it will resolve the underlying type
|
||||||
|
// (which is the data for this named type). If it is not, the lower bits are
|
||||||
|
// simply shifted off.
|
||||||
|
//
|
||||||
|
// The behavior is only defined for non-basic types.
|
||||||
|
func (t Type) stripPrefix() Type {
|
||||||
|
// Look at the 'n' bit in the type code (see the top of this file) to see
|
||||||
|
// whether this is a named type.
|
||||||
|
if (t>>4)%2 != 0 {
|
||||||
|
// This is a named type. The data is stored in a sidetable.
|
||||||
|
namedTypeNum := t >> 5
|
||||||
|
n, _ := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&namedNonBasicTypesSidetable)) + uintptr(namedTypeNum)))
|
||||||
|
return Type(n)
|
||||||
|
}
|
||||||
|
// Not a named type, so the value is stored directly in the type code.
|
||||||
|
return t >> 5
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field returns the type of the i'th field of this struct type. It panics if t
|
||||||
|
// is not a struct type.
|
||||||
func (t Type) Field(i int) StructField {
|
func (t Type) Field(i int) StructField {
|
||||||
panic("unimplemented: (reflect.Type).Field()")
|
if t.Kind() != Struct {
|
||||||
|
panic(&TypeError{"Field"})
|
||||||
|
}
|
||||||
|
structIdentifier := t.stripPrefix()
|
||||||
|
|
||||||
|
numField, p := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&structTypesSidetable)) + uintptr(structIdentifier)))
|
||||||
|
if uint(i) >= uint(numField) {
|
||||||
|
panic("reflect: field index out of range")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over every field in the struct and update the StructField each
|
||||||
|
// time, until the target field has been reached. This is very much not
|
||||||
|
// efficient, but it is easy to implement.
|
||||||
|
// Adding a jump table at the start to jump to the field directly would
|
||||||
|
// make this much faster, but that would also impact code size.
|
||||||
|
field := StructField{}
|
||||||
|
offset := uintptr(0)
|
||||||
|
for fieldNum := 0; fieldNum <= i; fieldNum++ {
|
||||||
|
// Read some flags of this field, like whether the field is an
|
||||||
|
// embedded field.
|
||||||
|
flagsByte := *(*uint8)(p)
|
||||||
|
p = unsafe.Pointer(uintptr(p) + 1)
|
||||||
|
|
||||||
|
// Read the type of this struct field.
|
||||||
|
var fieldType uintptr
|
||||||
|
fieldType, p = readVarint(p)
|
||||||
|
field.Type = Type(fieldType)
|
||||||
|
|
||||||
|
// Move Offset forward to align it to this field's alignment.
|
||||||
|
// Assume alignment is a power of two.
|
||||||
|
offset = align(offset, uintptr(field.Type.Align()))
|
||||||
|
field.Offset = offset
|
||||||
|
offset += field.Type.Size() // starting (unaligned) offset for next field
|
||||||
|
|
||||||
|
// Read the field name.
|
||||||
|
var nameNum uintptr
|
||||||
|
nameNum, p = readVarint(p)
|
||||||
|
field.Name = readStringSidetable(unsafe.Pointer(&structNamesSidetable), nameNum)
|
||||||
|
|
||||||
|
// The first bit in the flagsByte indicates whether this is an embedded
|
||||||
|
// field.
|
||||||
|
field.Anonymous = flagsByte&1 != 0
|
||||||
|
|
||||||
|
// The second bit indicates whether there is a tag.
|
||||||
|
if flagsByte&2 != 0 {
|
||||||
|
// There is a tag.
|
||||||
|
var tagNum uintptr
|
||||||
|
tagNum, p = readVarint(p)
|
||||||
|
field.Tag = readStringSidetable(unsafe.Pointer(&structNamesSidetable), tagNum)
|
||||||
|
} else {
|
||||||
|
// There is no tag.
|
||||||
|
field.Tag = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// The third bit indicates whether this field is exported.
|
||||||
|
if flagsByte&4 != 0 {
|
||||||
|
// This field is exported.
|
||||||
|
field.PkgPath = ""
|
||||||
|
} else {
|
||||||
|
// This field is unexported.
|
||||||
|
// TODO: list the real package path here. Storing it should not
|
||||||
|
// significantly impact binary size as there is only a limited
|
||||||
|
// number of packages in any program.
|
||||||
|
field.PkgPath = "<unimplemented>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return field
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bits returns the number of bits that this type uses. It is only valid for
|
// Bits returns the number of bits that this type uses. It is only valid for
|
||||||
|
@ -179,10 +258,19 @@ func (t Type) Len() int {
|
||||||
panic("unimplemented: (reflect.Type).Len()")
|
panic("unimplemented: (reflect.Type).Len()")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NumField returns the number of fields of a struct type. It panics for other
|
||||||
|
// type kinds.
|
||||||
func (t Type) NumField() int {
|
func (t Type) NumField() int {
|
||||||
panic("unimplemented: (reflect.Type).NumField()")
|
if t.Kind() != Struct {
|
||||||
|
panic(&TypeError{"NumField"})
|
||||||
|
}
|
||||||
|
structIdentifier := t.stripPrefix()
|
||||||
|
n, _ := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&structTypesSidetable)) + uintptr(structIdentifier)))
|
||||||
|
return int(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size returns the size in bytes of a given type. It is similar to
|
||||||
|
// unsafe.Sizeof.
|
||||||
func (t Type) Size() uintptr {
|
func (t Type) Size() uintptr {
|
||||||
switch t.Kind() {
|
switch t.Kind() {
|
||||||
case Bool, Int8, Uint8:
|
case Bool, Int8, Uint8:
|
||||||
|
@ -211,6 +299,15 @@ func (t Type) Size() uintptr {
|
||||||
return unsafe.Sizeof(uintptr(0))
|
return unsafe.Sizeof(uintptr(0))
|
||||||
case Slice:
|
case Slice:
|
||||||
return unsafe.Sizeof(SliceHeader{})
|
return unsafe.Sizeof(SliceHeader{})
|
||||||
|
case Interface:
|
||||||
|
return unsafe.Sizeof(interfaceHeader{})
|
||||||
|
case Struct:
|
||||||
|
numField := t.NumField()
|
||||||
|
if numField == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
lastField := t.Field(numField - 1)
|
||||||
|
return lastField.Offset + lastField.Type.Size()
|
||||||
default:
|
default:
|
||||||
panic("unimplemented: size of type")
|
panic("unimplemented: size of type")
|
||||||
}
|
}
|
||||||
|
@ -246,6 +343,18 @@ func (t Type) Align() int {
|
||||||
return int(unsafe.Alignof(uintptr(0)))
|
return int(unsafe.Alignof(uintptr(0)))
|
||||||
case Slice:
|
case Slice:
|
||||||
return int(unsafe.Alignof(SliceHeader{}))
|
return int(unsafe.Alignof(SliceHeader{}))
|
||||||
|
case Interface:
|
||||||
|
return int(unsafe.Alignof(interfaceHeader{}))
|
||||||
|
case Struct:
|
||||||
|
numField := t.NumField()
|
||||||
|
alignment := 1
|
||||||
|
for i := 0; i < numField; i++ {
|
||||||
|
fieldAlignment := t.Field(i).Type.Align()
|
||||||
|
if fieldAlignment > alignment {
|
||||||
|
alignment = fieldAlignment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return alignment
|
||||||
default:
|
default:
|
||||||
panic("unimplemented: alignment of type")
|
panic("unimplemented: alignment of type")
|
||||||
}
|
}
|
||||||
|
@ -269,9 +378,19 @@ func (t Type) AssignableTo(u Type) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A StructField describes a single field in a struct.
|
||||||
type StructField struct {
|
type StructField struct {
|
||||||
|
// Name indicates the field name.
|
||||||
Name string
|
Name string
|
||||||
Type Type
|
|
||||||
|
// PkgPath is the package path where the struct containing this field is
|
||||||
|
// declared for unexported fields, or the empty string for exported fields.
|
||||||
|
PkgPath string
|
||||||
|
|
||||||
|
Type Type
|
||||||
|
Tag string
|
||||||
|
Anonymous bool
|
||||||
|
Offset uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeError is the error that is used in a panic when invoking a method on a
|
// TypeError is the error that is used in a panic when invoking a method on a
|
||||||
|
|
|
@ -4,10 +4,28 @@ import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type valueFlags uint8
|
||||||
|
|
||||||
|
// Flags list some useful flags that contain some extra information not
|
||||||
|
// contained in an interface{} directly, like whether this value was exported at
|
||||||
|
// all (it is possible to read unexported fields using reflection, but it is not
|
||||||
|
// possible to modify them).
|
||||||
|
const (
|
||||||
|
valueFlagIndirect valueFlags = 1 << iota
|
||||||
|
valueFlagExported
|
||||||
|
)
|
||||||
|
|
||||||
type Value struct {
|
type Value struct {
|
||||||
typecode Type
|
typecode Type
|
||||||
value unsafe.Pointer
|
value unsafe.Pointer
|
||||||
indirect bool
|
flags valueFlags
|
||||||
|
}
|
||||||
|
|
||||||
|
// isIndirect returns whether the value pointer in this Value is always a
|
||||||
|
// pointer to the value. If it is false, it is only a pointer to the value if
|
||||||
|
// the value is bigger than a pointer.
|
||||||
|
func (v Value) isIndirect() bool {
|
||||||
|
return v.flags&valueFlagIndirect != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func Indirect(v Value) Value {
|
func Indirect(v Value) Value {
|
||||||
|
@ -22,6 +40,7 @@ func ValueOf(i interface{}) Value {
|
||||||
return Value{
|
return Value{
|
||||||
typecode: v.typecode,
|
typecode: v.typecode,
|
||||||
value: v.value,
|
value: v.value,
|
||||||
|
flags: valueFlagExported,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +49,7 @@ func (v Value) Interface() interface{} {
|
||||||
typecode: v.typecode,
|
typecode: v.typecode,
|
||||||
value: v.value,
|
value: v.value,
|
||||||
}
|
}
|
||||||
if v.indirect && v.Type().Size() <= unsafe.Sizeof(uintptr(0)) {
|
if v.isIndirect() && v.Type().Size() <= unsafe.Sizeof(uintptr(0)) {
|
||||||
// Value was indirect but must be put back directly in the interface
|
// Value was indirect but must be put back directly in the interface
|
||||||
// value.
|
// value.
|
||||||
var value uintptr
|
var value uintptr
|
||||||
|
@ -109,13 +128,13 @@ func (v Value) Addr() Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) CanSet() bool {
|
func (v Value) CanSet() bool {
|
||||||
return v.indirect
|
return v.flags&(valueFlagExported|valueFlagIndirect) == valueFlagExported|valueFlagIndirect
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) Bool() bool {
|
func (v Value) Bool() bool {
|
||||||
switch v.Kind() {
|
switch v.Kind() {
|
||||||
case Bool:
|
case Bool:
|
||||||
if v.indirect {
|
if v.isIndirect() {
|
||||||
return *((*bool)(v.value))
|
return *((*bool)(v.value))
|
||||||
} else {
|
} else {
|
||||||
return uintptr(v.value) != 0
|
return uintptr(v.value) != 0
|
||||||
|
@ -128,31 +147,31 @@ func (v Value) Bool() bool {
|
||||||
func (v Value) Int() int64 {
|
func (v Value) Int() int64 {
|
||||||
switch v.Kind() {
|
switch v.Kind() {
|
||||||
case Int:
|
case Int:
|
||||||
if v.indirect || unsafe.Sizeof(int(0)) > unsafe.Sizeof(uintptr(0)) {
|
if v.isIndirect() || unsafe.Sizeof(int(0)) > unsafe.Sizeof(uintptr(0)) {
|
||||||
return int64(*(*int)(v.value))
|
return int64(*(*int)(v.value))
|
||||||
} else {
|
} else {
|
||||||
return int64(int(uintptr(v.value)))
|
return int64(int(uintptr(v.value)))
|
||||||
}
|
}
|
||||||
case Int8:
|
case Int8:
|
||||||
if v.indirect {
|
if v.isIndirect() {
|
||||||
return int64(*(*int8)(v.value))
|
return int64(*(*int8)(v.value))
|
||||||
} else {
|
} else {
|
||||||
return int64(int8(uintptr(v.value)))
|
return int64(int8(uintptr(v.value)))
|
||||||
}
|
}
|
||||||
case Int16:
|
case Int16:
|
||||||
if v.indirect {
|
if v.isIndirect() {
|
||||||
return int64(*(*int16)(v.value))
|
return int64(*(*int16)(v.value))
|
||||||
} else {
|
} else {
|
||||||
return int64(int16(uintptr(v.value)))
|
return int64(int16(uintptr(v.value)))
|
||||||
}
|
}
|
||||||
case Int32:
|
case Int32:
|
||||||
if v.indirect || unsafe.Sizeof(int32(0)) > unsafe.Sizeof(uintptr(0)) {
|
if v.isIndirect() || unsafe.Sizeof(int32(0)) > unsafe.Sizeof(uintptr(0)) {
|
||||||
return int64(*(*int32)(v.value))
|
return int64(*(*int32)(v.value))
|
||||||
} else {
|
} else {
|
||||||
return int64(int32(uintptr(v.value)))
|
return int64(int32(uintptr(v.value)))
|
||||||
}
|
}
|
||||||
case Int64:
|
case Int64:
|
||||||
if v.indirect || unsafe.Sizeof(int64(0)) > unsafe.Sizeof(uintptr(0)) {
|
if v.isIndirect() || unsafe.Sizeof(int64(0)) > unsafe.Sizeof(uintptr(0)) {
|
||||||
return int64(*(*int64)(v.value))
|
return int64(*(*int64)(v.value))
|
||||||
} else {
|
} else {
|
||||||
return int64(int64(uintptr(v.value)))
|
return int64(int64(uintptr(v.value)))
|
||||||
|
@ -165,37 +184,37 @@ func (v Value) Int() int64 {
|
||||||
func (v Value) Uint() uint64 {
|
func (v Value) Uint() uint64 {
|
||||||
switch v.Kind() {
|
switch v.Kind() {
|
||||||
case Uintptr:
|
case Uintptr:
|
||||||
if v.indirect {
|
if v.isIndirect() {
|
||||||
return uint64(*(*uintptr)(v.value))
|
return uint64(*(*uintptr)(v.value))
|
||||||
} else {
|
} else {
|
||||||
return uint64(uintptr(v.value))
|
return uint64(uintptr(v.value))
|
||||||
}
|
}
|
||||||
case Uint8:
|
case Uint8:
|
||||||
if v.indirect {
|
if v.isIndirect() {
|
||||||
return uint64(*(*uint8)(v.value))
|
return uint64(*(*uint8)(v.value))
|
||||||
} else {
|
} else {
|
||||||
return uint64(uintptr(v.value))
|
return uint64(uintptr(v.value))
|
||||||
}
|
}
|
||||||
case Uint16:
|
case Uint16:
|
||||||
if v.indirect {
|
if v.isIndirect() {
|
||||||
return uint64(*(*uint16)(v.value))
|
return uint64(*(*uint16)(v.value))
|
||||||
} else {
|
} else {
|
||||||
return uint64(uintptr(v.value))
|
return uint64(uintptr(v.value))
|
||||||
}
|
}
|
||||||
case Uint:
|
case Uint:
|
||||||
if v.indirect || unsafe.Sizeof(uint(0)) > unsafe.Sizeof(uintptr(0)) {
|
if v.isIndirect() || unsafe.Sizeof(uint(0)) > unsafe.Sizeof(uintptr(0)) {
|
||||||
return uint64(*(*uint)(v.value))
|
return uint64(*(*uint)(v.value))
|
||||||
} else {
|
} else {
|
||||||
return uint64(uintptr(v.value))
|
return uint64(uintptr(v.value))
|
||||||
}
|
}
|
||||||
case Uint32:
|
case Uint32:
|
||||||
if v.indirect || unsafe.Sizeof(uint32(0)) > unsafe.Sizeof(uintptr(0)) {
|
if v.isIndirect() || unsafe.Sizeof(uint32(0)) > unsafe.Sizeof(uintptr(0)) {
|
||||||
return uint64(*(*uint32)(v.value))
|
return uint64(*(*uint32)(v.value))
|
||||||
} else {
|
} else {
|
||||||
return uint64(uintptr(v.value))
|
return uint64(uintptr(v.value))
|
||||||
}
|
}
|
||||||
case Uint64:
|
case Uint64:
|
||||||
if v.indirect || unsafe.Sizeof(uint64(0)) > unsafe.Sizeof(uintptr(0)) {
|
if v.isIndirect() || unsafe.Sizeof(uint64(0)) > unsafe.Sizeof(uintptr(0)) {
|
||||||
return uint64(*(*uint64)(v.value))
|
return uint64(*(*uint64)(v.value))
|
||||||
} else {
|
} else {
|
||||||
return uint64(uintptr(v.value))
|
return uint64(uintptr(v.value))
|
||||||
|
@ -208,7 +227,7 @@ func (v Value) Uint() uint64 {
|
||||||
func (v Value) Float() float64 {
|
func (v Value) Float() float64 {
|
||||||
switch v.Kind() {
|
switch v.Kind() {
|
||||||
case Float32:
|
case Float32:
|
||||||
if v.indirect || unsafe.Sizeof(float32(0)) > unsafe.Sizeof(uintptr(0)) {
|
if v.isIndirect() || unsafe.Sizeof(float32(0)) > unsafe.Sizeof(uintptr(0)) {
|
||||||
// The float is stored as an external value on systems with 16-bit
|
// The float is stored as an external value on systems with 16-bit
|
||||||
// pointers.
|
// pointers.
|
||||||
return float64(*(*float32)(v.value))
|
return float64(*(*float32)(v.value))
|
||||||
|
@ -218,7 +237,7 @@ func (v Value) Float() float64 {
|
||||||
return float64(*(*float32)(unsafe.Pointer(&v.value)))
|
return float64(*(*float32)(unsafe.Pointer(&v.value)))
|
||||||
}
|
}
|
||||||
case Float64:
|
case Float64:
|
||||||
if v.indirect || unsafe.Sizeof(float64(0)) > unsafe.Sizeof(uintptr(0)) {
|
if v.isIndirect() || unsafe.Sizeof(float64(0)) > unsafe.Sizeof(uintptr(0)) {
|
||||||
// For systems with 16-bit and 32-bit pointers.
|
// For systems with 16-bit and 32-bit pointers.
|
||||||
return *(*float64)(v.value)
|
return *(*float64)(v.value)
|
||||||
} else {
|
} else {
|
||||||
|
@ -234,7 +253,7 @@ func (v Value) Float() float64 {
|
||||||
func (v Value) Complex() complex128 {
|
func (v Value) Complex() complex128 {
|
||||||
switch v.Kind() {
|
switch v.Kind() {
|
||||||
case Complex64:
|
case Complex64:
|
||||||
if v.indirect || unsafe.Sizeof(complex64(0)) > unsafe.Sizeof(uintptr(0)) {
|
if v.isIndirect() || unsafe.Sizeof(complex64(0)) > unsafe.Sizeof(uintptr(0)) {
|
||||||
// The complex number is stored as an external value on systems with
|
// The complex number is stored as an external value on systems with
|
||||||
// 16-bit and 32-bit pointers.
|
// 16-bit and 32-bit pointers.
|
||||||
return complex128(*(*complex64)(v.value))
|
return complex128(*(*complex64)(v.value))
|
||||||
|
@ -295,15 +314,17 @@ func (v Value) Cap() int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NumField returns the number of fields of this struct. It panics for other
|
||||||
|
// value types.
|
||||||
func (v Value) NumField() int {
|
func (v Value) NumField() int {
|
||||||
panic("unimplemented: (reflect.Value).NumField()")
|
return v.Type().NumField()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) Elem() Value {
|
func (v Value) Elem() Value {
|
||||||
switch v.Kind() {
|
switch v.Kind() {
|
||||||
case Ptr:
|
case Ptr:
|
||||||
ptr := v.value
|
ptr := v.value
|
||||||
if v.indirect {
|
if v.isIndirect() {
|
||||||
ptr = *(*unsafe.Pointer)(ptr)
|
ptr = *(*unsafe.Pointer)(ptr)
|
||||||
}
|
}
|
||||||
if ptr == nil {
|
if ptr == nil {
|
||||||
|
@ -312,15 +333,77 @@ func (v Value) Elem() Value {
|
||||||
return Value{
|
return Value{
|
||||||
typecode: v.Type().Elem(),
|
typecode: v.Type().Elem(),
|
||||||
value: ptr,
|
value: ptr,
|
||||||
indirect: true,
|
flags: v.flags | valueFlagIndirect,
|
||||||
}
|
}
|
||||||
default: // not implemented: Interface
|
default: // not implemented: Interface
|
||||||
panic(&ValueError{"Elem"})
|
panic(&ValueError{"Elem"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Field returns the value of the i'th field of this struct.
|
||||||
func (v Value) Field(i int) Value {
|
func (v Value) Field(i int) Value {
|
||||||
panic("unimplemented: (reflect.Value).Field()")
|
structField := v.Type().Field(i)
|
||||||
|
flags := v.flags
|
||||||
|
if structField.PkgPath != "" {
|
||||||
|
// The fact that PkgPath is present means that this field is not
|
||||||
|
// exported.
|
||||||
|
flags &^= valueFlagExported
|
||||||
|
}
|
||||||
|
|
||||||
|
size := v.Type().Size()
|
||||||
|
fieldSize := structField.Type.Size()
|
||||||
|
if v.isIndirect() || fieldSize > unsafe.Sizeof(uintptr(0)) {
|
||||||
|
// v.value was already a pointer to the value and it should stay that
|
||||||
|
// way.
|
||||||
|
return Value{
|
||||||
|
flags: flags,
|
||||||
|
typecode: structField.Type,
|
||||||
|
value: unsafe.Pointer(uintptr(v.value) + structField.Offset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The fieldSize is smaller than uintptr, which means that the value will
|
||||||
|
// have to be stored directly in the interface value.
|
||||||
|
|
||||||
|
if fieldSize == 0 {
|
||||||
|
// The struct field is zero sized.
|
||||||
|
// This is a rare situation, but because it's undefined behavior
|
||||||
|
// to shift the size of the value (zeroing the value), handle this
|
||||||
|
// situation explicitly.
|
||||||
|
return Value{
|
||||||
|
flags: flags,
|
||||||
|
typecode: structField.Type,
|
||||||
|
value: unsafe.Pointer(uintptr(0)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if size > unsafe.Sizeof(uintptr(0)) {
|
||||||
|
// The value was not stored in the interface before but will be
|
||||||
|
// afterwards, so load the value (from the correct offset) and return
|
||||||
|
// it.
|
||||||
|
ptr := unsafe.Pointer(uintptr(v.value) + structField.Offset)
|
||||||
|
loadedValue := uintptr(0)
|
||||||
|
shift := uintptr(0)
|
||||||
|
for i := uintptr(0); i < fieldSize; i++ {
|
||||||
|
loadedValue |= uintptr(*(*byte)(ptr)) << shift
|
||||||
|
shift += 8
|
||||||
|
ptr = unsafe.Pointer(uintptr(ptr) + 1)
|
||||||
|
}
|
||||||
|
return Value{
|
||||||
|
flags: 0,
|
||||||
|
typecode: structField.Type,
|
||||||
|
value: unsafe.Pointer(loadedValue),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The value was already stored directly in the interface and it still
|
||||||
|
// is. Cut out the part of the value that we need.
|
||||||
|
mask := ^uintptr(0) >> ((unsafe.Sizeof(uintptr(0)) - fieldSize) * 8)
|
||||||
|
return Value{
|
||||||
|
flags: flags,
|
||||||
|
typecode: structField.Type,
|
||||||
|
value: unsafe.Pointer((uintptr(v.value) >> (structField.Offset * 8)) & mask),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) Index(i int) Value {
|
func (v Value) Index(i int) Value {
|
||||||
|
@ -333,7 +416,7 @@ func (v Value) Index(i int) Value {
|
||||||
}
|
}
|
||||||
elem := Value{
|
elem := Value{
|
||||||
typecode: v.Type().Elem(),
|
typecode: v.Type().Elem(),
|
||||||
indirect: true,
|
flags: v.flags | valueFlagIndirect,
|
||||||
}
|
}
|
||||||
addr := uintptr(slice.Data) + elem.Type().Size()*uintptr(i) // pointer to new value
|
addr := uintptr(slice.Data) + elem.Type().Size()*uintptr(i) // pointer to new value
|
||||||
elem.value = unsafe.Pointer(addr)
|
elem.value = unsafe.Pointer(addr)
|
||||||
|
@ -385,15 +468,13 @@ func (it *MapIter) Next() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) Set(x Value) {
|
func (v Value) Set(x Value) {
|
||||||
if !v.indirect {
|
v.checkAddressable()
|
||||||
panic("reflect: value is not addressable")
|
|
||||||
}
|
|
||||||
if !v.Type().AssignableTo(x.Type()) {
|
if !v.Type().AssignableTo(x.Type()) {
|
||||||
panic("reflect: cannot set")
|
panic("reflect: cannot set")
|
||||||
}
|
}
|
||||||
size := v.Type().Size()
|
size := v.Type().Size()
|
||||||
xptr := x.value
|
xptr := x.value
|
||||||
if size <= unsafe.Sizeof(uintptr(0)) && !x.indirect {
|
if size <= unsafe.Sizeof(uintptr(0)) && !x.isIndirect() {
|
||||||
value := x.value
|
value := x.value
|
||||||
xptr = unsafe.Pointer(&value)
|
xptr = unsafe.Pointer(&value)
|
||||||
}
|
}
|
||||||
|
@ -401,9 +482,7 @@ func (v Value) Set(x Value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) SetBool(x bool) {
|
func (v Value) SetBool(x bool) {
|
||||||
if !v.indirect {
|
v.checkAddressable()
|
||||||
panic("reflect: value is not addressable")
|
|
||||||
}
|
|
||||||
switch v.Kind() {
|
switch v.Kind() {
|
||||||
case Bool:
|
case Bool:
|
||||||
*(*bool)(v.value) = x
|
*(*bool)(v.value) = x
|
||||||
|
@ -413,9 +492,7 @@ func (v Value) SetBool(x bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) SetInt(x int64) {
|
func (v Value) SetInt(x int64) {
|
||||||
if !v.indirect {
|
v.checkAddressable()
|
||||||
panic("reflect: value is not addressable")
|
|
||||||
}
|
|
||||||
switch v.Kind() {
|
switch v.Kind() {
|
||||||
case Int:
|
case Int:
|
||||||
*(*int)(v.value) = int(x)
|
*(*int)(v.value) = int(x)
|
||||||
|
@ -433,9 +510,7 @@ func (v Value) SetInt(x int64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) SetUint(x uint64) {
|
func (v Value) SetUint(x uint64) {
|
||||||
if !v.indirect {
|
v.checkAddressable()
|
||||||
panic("reflect: value is not addressable")
|
|
||||||
}
|
|
||||||
switch v.Kind() {
|
switch v.Kind() {
|
||||||
case Uint:
|
case Uint:
|
||||||
*(*uint)(v.value) = uint(x)
|
*(*uint)(v.value) = uint(x)
|
||||||
|
@ -455,9 +530,7 @@ func (v Value) SetUint(x uint64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) SetFloat(x float64) {
|
func (v Value) SetFloat(x float64) {
|
||||||
if !v.indirect {
|
v.checkAddressable()
|
||||||
panic("reflect: value is not addressable")
|
|
||||||
}
|
|
||||||
switch v.Kind() {
|
switch v.Kind() {
|
||||||
case Float32:
|
case Float32:
|
||||||
*(*float32)(v.value) = float32(x)
|
*(*float32)(v.value) = float32(x)
|
||||||
|
@ -469,9 +542,7 @@ func (v Value) SetFloat(x float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) SetComplex(x complex128) {
|
func (v Value) SetComplex(x complex128) {
|
||||||
if !v.indirect {
|
v.checkAddressable()
|
||||||
panic("reflect: value is not addressable")
|
|
||||||
}
|
|
||||||
switch v.Kind() {
|
switch v.Kind() {
|
||||||
case Complex64:
|
case Complex64:
|
||||||
*(*complex64)(v.value) = complex64(x)
|
*(*complex64)(v.value) = complex64(x)
|
||||||
|
@ -483,9 +554,7 @@ func (v Value) SetComplex(x complex128) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) SetString(x string) {
|
func (v Value) SetString(x string) {
|
||||||
if !v.indirect {
|
v.checkAddressable()
|
||||||
panic("reflect: value is not addressable")
|
|
||||||
}
|
|
||||||
switch v.Kind() {
|
switch v.Kind() {
|
||||||
case String:
|
case String:
|
||||||
*(*string)(v.value) = x
|
*(*string)(v.value) = x
|
||||||
|
@ -494,6 +563,12 @@ func (v Value) SetString(x string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v Value) checkAddressable() {
|
||||||
|
if !v.isIndirect() {
|
||||||
|
panic("reflect: value is not addressable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func MakeSlice(typ Type, len, cap int) Value {
|
func MakeSlice(typ Type, len, cap int) Value {
|
||||||
panic("unimplemented: reflect.MakeSlice()")
|
panic("unimplemented: reflect.MakeSlice()")
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,10 +50,20 @@ type typecodeID struct {
|
||||||
// * named type: the underlying type
|
// * named type: the underlying type
|
||||||
// * interface: null
|
// * interface: null
|
||||||
// * chan/pointer/slice: the element type
|
// * chan/pointer/slice: the element type
|
||||||
// * array/func/map/struct: TODO
|
// * struct: GEP of structField array (to typecode field)
|
||||||
|
// * array/func/map: TODO
|
||||||
references *typecodeID
|
references *typecodeID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// structField is used by the compiler to pass information to the interface
|
||||||
|
// lowering pass. It is not used in the final binary.
|
||||||
|
type structField struct {
|
||||||
|
typecode *typecodeID // type of this struct field
|
||||||
|
name *uint8 // pointer to char array
|
||||||
|
tag *uint8 // pointer to char array, or nil
|
||||||
|
embedded bool
|
||||||
|
}
|
||||||
|
|
||||||
// Pseudo type used before interface lowering. By using a struct instead of a
|
// Pseudo type used before interface lowering. By using a struct instead of a
|
||||||
// function call, this is simpler to reason about during init interpretation
|
// function call, this is simpler to reason about during init interpretation
|
||||||
// than a function call. Also, by keeping the method set around it is easier to
|
// than a function call. Also, by keeping the method set around it is easier to
|
||||||
|
|
26
testdata/reflect.go
предоставленный
26
testdata/reflect.go
предоставленный
|
@ -11,6 +11,17 @@ type (
|
||||||
myslice2 []myint
|
myslice2 []myint
|
||||||
mychan chan int
|
mychan chan int
|
||||||
myptr *int
|
myptr *int
|
||||||
|
point struct {
|
||||||
|
X int16
|
||||||
|
Y int16
|
||||||
|
}
|
||||||
|
mystruct struct {
|
||||||
|
n int `foo:"bar"`
|
||||||
|
some point
|
||||||
|
zero struct{}
|
||||||
|
buf []byte
|
||||||
|
Buf []byte
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -86,6 +97,12 @@ func main() {
|
||||||
// structs
|
// structs
|
||||||
struct{}{},
|
struct{}{},
|
||||||
struct{ error }{},
|
struct{ error }{},
|
||||||
|
struct {
|
||||||
|
a uint8
|
||||||
|
b int16
|
||||||
|
c int8
|
||||||
|
}{42, 321, 123},
|
||||||
|
mystruct{5, point{-5, 3}, struct{}{}, []byte{'G', 'o'}, []byte{'X'}},
|
||||||
} {
|
} {
|
||||||
showValue(reflect.ValueOf(v), "")
|
showValue(reflect.ValueOf(v), "")
|
||||||
}
|
}
|
||||||
|
@ -291,7 +308,14 @@ func showValue(rv reflect.Value, indent string) {
|
||||||
showValue(rv.Index(i), indent+" ")
|
showValue(rv.Index(i), indent+" ")
|
||||||
}
|
}
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
println(indent + " struct")
|
println(indent+" struct:", rt.NumField())
|
||||||
|
for i := 0; i < rv.NumField(); i++ {
|
||||||
|
field := rt.Field(i)
|
||||||
|
println(indent+" field:", i, field.Name)
|
||||||
|
println(indent+" tag:", field.Tag)
|
||||||
|
println(indent+" embedded:", field.Anonymous)
|
||||||
|
showValue(rv.Field(i), indent+" ")
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
println(indent + " unknown type kind!")
|
println(indent + " unknown type kind!")
|
||||||
}
|
}
|
||||||
|
|
77
testdata/reflect.txt
предоставленный
77
testdata/reflect.txt
предоставленный
|
@ -217,9 +217,82 @@ reflect type: map
|
||||||
map
|
map
|
||||||
nil: false
|
nil: false
|
||||||
reflect type: struct
|
reflect type: struct
|
||||||
struct
|
struct: 0
|
||||||
reflect type: struct
|
reflect type: struct
|
||||||
struct
|
struct: 1
|
||||||
|
field: 0 error
|
||||||
|
tag:
|
||||||
|
embedded: true
|
||||||
|
reflect type: interface
|
||||||
|
interface
|
||||||
|
nil: true
|
||||||
|
reflect type: struct
|
||||||
|
struct: 3
|
||||||
|
field: 0 a
|
||||||
|
tag:
|
||||||
|
embedded: false
|
||||||
|
reflect type: uint8
|
||||||
|
uint: 42
|
||||||
|
field: 1 b
|
||||||
|
tag:
|
||||||
|
embedded: false
|
||||||
|
reflect type: int16
|
||||||
|
int: 321
|
||||||
|
field: 2 c
|
||||||
|
tag:
|
||||||
|
embedded: false
|
||||||
|
reflect type: int8
|
||||||
|
int: 123
|
||||||
|
reflect type: struct
|
||||||
|
struct: 5
|
||||||
|
field: 0 n
|
||||||
|
tag: foo:"bar"
|
||||||
|
embedded: false
|
||||||
|
reflect type: int
|
||||||
|
int: 5
|
||||||
|
field: 1 some
|
||||||
|
tag:
|
||||||
|
embedded: false
|
||||||
|
reflect type: struct
|
||||||
|
struct: 2
|
||||||
|
field: 0 X
|
||||||
|
tag:
|
||||||
|
embedded: false
|
||||||
|
reflect type: int16
|
||||||
|
int: -5
|
||||||
|
field: 1 Y
|
||||||
|
tag:
|
||||||
|
embedded: false
|
||||||
|
reflect type: int16
|
||||||
|
int: 3
|
||||||
|
field: 2 zero
|
||||||
|
tag:
|
||||||
|
embedded: false
|
||||||
|
reflect type: struct
|
||||||
|
struct: 0
|
||||||
|
field: 3 buf
|
||||||
|
tag:
|
||||||
|
embedded: false
|
||||||
|
reflect type: slice
|
||||||
|
slice: uint8 2 2
|
||||||
|
pointer: true
|
||||||
|
nil: false
|
||||||
|
indexing: 0
|
||||||
|
reflect type: uint8
|
||||||
|
uint: 71
|
||||||
|
indexing: 1
|
||||||
|
reflect type: uint8
|
||||||
|
uint: 111
|
||||||
|
field: 4 Buf
|
||||||
|
tag:
|
||||||
|
embedded: false
|
||||||
|
reflect type: slice
|
||||||
|
slice: uint8 1 1
|
||||||
|
pointer: true
|
||||||
|
nil: false
|
||||||
|
indexing: 0
|
||||||
|
reflect type: uint8 settable=true
|
||||||
|
uint: 88
|
||||||
|
|
||||||
sizes:
|
sizes:
|
||||||
int8 1 8
|
int8 1 8
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче