Skip to content

Commit

Permalink
reflect: support slices and indexing of strings and slices
Browse files Browse the repository at this point in the history
  • Loading branch information
aykevl committed Dec 13, 2018
1 parent 4a5703e commit 1ed3123
Show file tree
Hide file tree
Showing 6 changed files with 279 additions and 52 deletions.
32 changes: 20 additions & 12 deletions compiler/interface.go
Expand Up @@ -79,11 +79,23 @@ func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, global str
// It returns a pointer to an external global which should be replaced with the
// real type in the interface lowering pass.
func (c *Compiler) getTypeCode(typ types.Type) llvm.Value {
var globalName string
switch typ := typ.(type) {
globalName := "type:" + getTypeCodeName(typ)
global := c.mod.NamedGlobal(globalName)
if global.IsNil() {
global = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), globalName)
global.SetGlobalConstant(true)
}
return global
}

// 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
// package. See getTypeCodeNum.
func getTypeCodeName(t types.Type) string {
switch t := t.(type) {
case *types.Basic:
var name string
switch typ.Kind() {
switch t.Kind() {
case types.Bool:
name = "bool"
case types.Int:
Expand Down Expand Up @@ -121,19 +133,15 @@ func (c *Compiler) getTypeCode(typ types.Type) llvm.Value {
case types.UnsafePointer:
name = "unsafeptr"
default:
panic("unknown basic type: " + typ.Name())
panic("unknown basic type: " + t.Name())
}
globalName = "type:basic:" + name
return "basic:" + name
case *types.Slice:
return "slice:" + getTypeCodeName(t.Elem())
default:
// Unknown type, fall back to the .String() method for identification.
globalName = "type:other:" + typ.String()
return "other:" + t.String()
}
global := c.mod.NamedGlobal(globalName)
if global.IsNil() {
global = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), globalName)
global.SetGlobalConstant(true)
}
return global
}

// getTypeMethodSet returns a reference (GEP) to a global method set. This
Expand Down
51 changes: 40 additions & 11 deletions compiler/reflect.go
@@ -1,10 +1,11 @@
package compiler

import (
"math/big"
"strings"
)

var basicTypes = map[string]uint64{
var basicTypes = map[string]int64{
"bool": 1,
"int": 2,
"int8": 3,
Expand Down Expand Up @@ -39,17 +40,45 @@ func (c *Compiler) assignTypeCodes(typeSlice typeInfoSlice) {
// Assign typecodes the way the reflect package expects.
fallbackIndex := 1
for _, t := range typeSlice {
if strings.HasPrefix(t.name, "type:basic:") {
// Basic types have a typecode with the lowest bit set to 0.
num, ok := basicTypes[t.name[len("type:basic:"):]]
if !ok {
panic("invalid basic type: " + t.name)
}
t.num = num<<1 | 0
} else {
// Fallback types have a typecode with the lowest bit set to 1.
t.num = uint64(fallbackIndex<<1 | 1)
if t.name[:5] != "type:" {
panic("expected type name to start with 'type:'")
}
num := c.getTypeCodeNum(t.name[5:])
if num == nil {
// Fallback/unsupported types have a typecode with the lowest bits
// set to 11.
t.num = uint64(fallbackIndex<<2 | 3)
fallbackIndex++
continue
}
if num.BitLen() > c.uintptrType.IntTypeWidth() || !num.IsUint64() {
// TODO: support this in some way, using a side table for example.
// That's less efficient but better than not working at all.
// Particularly important on systems with 16-bit pointers (e.g.
// AVR).
panic("compiler: could not store type code number inside interface type code")
}
t.num = num.Uint64()
}
}

// getTypeCodeNum returns the typecode for a given type as expected by the
// reflect package. Also see getTypeCodeName, which serializes types to a string
// based on a types.Type value for this function.
func (c *Compiler) getTypeCodeNum(name string) *big.Int {
if strings.HasPrefix(name, "basic:") {
// Basic types have a typecode with the lowest bits set to 00.
num, ok := basicTypes[name[len("basic:"):]]
if !ok {
panic("invalid basic type: " + name)
}
return big.NewInt(num<<2 | 0)
} else if strings.HasPrefix(name, "slice:") {
// Slices have a typecode with the lowest bits set to 01.
num := c.getTypeCodeNum(name[len("slice:"):])
num.Lsh(num, 2).Or(num, big.NewInt(1))
return num
} else {
return nil
}
}
59 changes: 54 additions & 5 deletions src/reflect/type.go
@@ -1,5 +1,9 @@
package reflect

import (
"unsafe"
)

// A Kind is the number that the compiler uses for this type.
//
// Not used directly. These types are all replaced with the number the compiler
Expand Down Expand Up @@ -76,11 +80,18 @@ func (k Kind) String() string {
return "string"
case UnsafePointer:
return "unsafe.Pointer"
case Slice:
return "slice"
default:
return "T"
return "invalid"
}
}

// basicType returns a new Type for this kind if Kind is a basic type.
func (k Kind) basicType() Type {
return Type(k << 2 | 0)
}

// The typecode as used in an interface{}.
type Type uintptr

Expand All @@ -93,16 +104,24 @@ func (t Type) String() string {
}

func (t Type) Kind() Kind {
if t & 1 == 0 {
if t % 4 == 0 {
// Basic type
return Kind(t >> 1)
return Kind(t >> 2)
} else if t % 4 == 1 {
// Slice
return Slice
} else {
return Invalid // TODO
}
}

func (t Type) Elem() Type {
panic("unimplemented: (reflect.Type).Elem()")
switch t.Kind() {
case Slice:
return t >> 2
default: // not implemented: Array, Chan, Map, Ptr
panic("unimplemented: (reflect.Type).Elem()")
}
}

func (t Type) Field(i int) StructField {
Expand All @@ -122,7 +141,37 @@ func (t Type) NumField() int {
}

func (t Type) Size() uintptr {
panic("unimplemented: (reflect.Type).Size()")
switch t.Kind() {
case Bool, Int8, Uint8:
return 1
case Int16, Uint16:
return 2
case Int32, Uint32:
return 4
case Int64, Uint64:
return 8
case Int, Uint:
return unsafe.Sizeof(int(0))
case Uintptr:
return unsafe.Sizeof(uintptr(0))
case Float32:
return 4
case Float64:
return 8
case Complex64:
return 8
case Complex128:
return 16
case String:
return unsafe.Sizeof(StringHeader{})
case UnsafePointer:
return unsafe.Sizeof(uintptr(0))
case Slice:
return unsafe.Sizeof(SliceHeader{})
default:
// Size unknown.
return 0
}
}

type StructField struct {
Expand Down

0 comments on commit 1ed3123

Please sign in to comment.