aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2018-12-12 14:34:07 +0100
committerAyke van Laethem <[email protected]>2019-02-05 17:11:09 +0100
commitfb23e9c212241a0611a0d3cc7afe54696e7174f8 (patch)
treead3ad57d2c12fb92afc989f9ae04d1eead533b14
parent222c4c75b17fd3f3d4d32dee9bdb9f9b4b433a5b (diff)
downloadtinygo-fb23e9c212241a0611a0d3cc7afe54696e7174f8.tar.gz
tinygo-fb23e9c212241a0611a0d3cc7afe54696e7174f8.zip
reflect: add support for non-named basic types
-rw-r--r--compiler/interface-lowering.go15
-rw-r--r--compiler/interface.go53
-rw-r--r--compiler/reflect.go55
-rw-r--r--src/reflect/type.go54
-rw-r--r--src/reflect/value.go134
-rw-r--r--testdata/reflect.go62
-rw-r--r--testdata/reflect.txt50
7 files changed, 397 insertions, 26 deletions
diff --git a/compiler/interface-lowering.go b/compiler/interface-lowering.go
index 00f0df206..520932334 100644
--- a/compiler/interface-lowering.go
+++ b/compiler/interface-lowering.go
@@ -104,15 +104,6 @@ func (t *typeInfo) getMethod(signature *signatureInfo) *methodInfo {
panic("could not find method")
}
-// id returns the fully-qualified type name including import path, removing the
-// $type suffix.
-func (t *typeInfo) id() string {
- if !strings.HasSuffix(t.name, "$type") {
- panic("concrete type does not have $type suffix: " + t.name)
- }
- return t.name[:len(t.name)-len("$type")]
-}
-
// typeInfoSlice implements sort.Slice, sorting the most commonly used types
// first.
type typeInfoSlice []*typeInfo
@@ -423,9 +414,7 @@ func (p *lowerInterfacesPass) run() {
}
// Assign a type code for each type.
- for i, t := range typeSlice {
- t.num = uint64(i + 1)
- }
+ p.assignTypeCodes(typeSlice)
// Replace each call to runtime.makeInterface with the constant type code.
for _, use := range makeInterfaceUses {
@@ -691,7 +680,7 @@ func (p *lowerInterfacesPass) createInterfaceMethodFunc(itf *interfaceInfo, sign
// Define all possible functions that can be called.
for _, typ := range itf.types {
- bb := llvm.AddBasicBlock(fn, typ.id())
+ bb := llvm.AddBasicBlock(fn, typ.name)
sw.AddCase(llvm.ConstInt(p.uintptrType, typ.num, false), bb)
// The function we will redirect to when the interface has this type.
diff --git a/compiler/interface.go b/compiler/interface.go
index 7600d4628..1f90b84ae 100644
--- a/compiler/interface.go
+++ b/compiler/interface.go
@@ -79,9 +79,58 @@ 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 {
- global := c.mod.NamedGlobal(typ.String() + "$type")
+ var globalName string
+ switch typ := typ.(type) {
+ case *types.Basic:
+ var name string
+ switch typ.Kind() {
+ case types.Bool:
+ name = "bool"
+ case types.Int:
+ name = "int"
+ case types.Int8:
+ name = "int8"
+ case types.Int16:
+ name = "int16"
+ case types.Int32:
+ name = "int32"
+ case types.Int64:
+ name = "int64"
+ case types.Uint:
+ name = "uint"
+ case types.Uint8:
+ name = "uint8"
+ case types.Uint16:
+ name = "uint16"
+ case types.Uint32:
+ name = "uint32"
+ case types.Uint64:
+ name = "uint64"
+ case types.Uintptr:
+ name = "uintptr"
+ case types.Float32:
+ name = "float32"
+ case types.Float64:
+ name = "float64"
+ case types.Complex64:
+ name = "complex64"
+ case types.Complex128:
+ name = "complex128"
+ case types.String:
+ name = "string"
+ case types.UnsafePointer:
+ name = "unsafeptr"
+ default:
+ panic("unknown basic type: " + typ.Name())
+ }
+ globalName = "type:basic:" + name
+ default:
+ // Unknown type, fall back to the .String() method for identification.
+ globalName = "type:other:" + typ.String()
+ }
+ global := c.mod.NamedGlobal(globalName)
if global.IsNil() {
- global = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), typ.String()+"$type")
+ global = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), globalName)
global.SetGlobalConstant(true)
}
return global
diff --git a/compiler/reflect.go b/compiler/reflect.go
new file mode 100644
index 000000000..80ccd795b
--- /dev/null
+++ b/compiler/reflect.go
@@ -0,0 +1,55 @@
+package compiler
+
+import (
+ "strings"
+)
+
+var basicTypes = map[string]uint64{
+ "bool": 1,
+ "int": 2,
+ "int8": 3,
+ "int16": 4,
+ "int32": 5,
+ "int64": 6,
+ "uint": 7,
+ "uint8": 8,
+ "uint16": 9,
+ "uint32": 10,
+ "uint64": 11,
+ "uintptr": 12,
+ "float32": 13,
+ "float64": 14,
+ "complex64": 15,
+ "complex128": 16,
+ "string": 17,
+ "unsafeptr": 18,
+}
+
+func (c *Compiler) assignTypeCodes(typeSlice typeInfoSlice) {
+ fn := c.mod.NamedFunction("reflect.ValueOf")
+ if fn.IsNil() {
+ // reflect.ValueOf is never used, so we can use the most efficient
+ // encoding possible.
+ for i, t := range typeSlice {
+ t.num = uint64(i + 1)
+ }
+ return
+ }
+
+ // 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)
+ fallbackIndex++
+ }
+ }
+}
diff --git a/src/reflect/type.go b/src/reflect/type.go
index 848bdaffe..a0fe132d0 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -26,6 +26,8 @@ const (
Float64
Complex64
Complex128
+ String
+ UnsafePointer
Array
Chan
Func
@@ -33,11 +35,52 @@ const (
Map
Ptr
Slice
- String
Struct
- UnsafePointer
)
+func (k Kind) String() string {
+ switch k {
+ case Bool:
+ return "bool"
+ case Int:
+ return "int"
+ case Int8:
+ return "int8"
+ case Int16:
+ return "int16"
+ case Int32:
+ return "int32"
+ case Int64:
+ return "int64"
+ case Uint:
+ return "uint"
+ case Uint8:
+ return "uint8"
+ case Uint16:
+ return "uint16"
+ case Uint32:
+ return "uint32"
+ case Uint64:
+ return "uint64"
+ case Uintptr:
+ return "uintptr"
+ case Float32:
+ return "float32"
+ case Float64:
+ return "float64"
+ case Complex64:
+ return "complex64"
+ case Complex128:
+ return "complex128"
+ case String:
+ return "string"
+ case UnsafePointer:
+ return "unsafe.Pointer"
+ default:
+ return "T"
+ }
+}
+
// The typecode as used in an interface{}.
type Type uintptr
@@ -50,7 +93,12 @@ func (t Type) String() string {
}
func (t Type) Kind() Kind {
- return Invalid // TODO
+ if t & 1 == 0 {
+ // Basic type
+ return Kind(t >> 1)
+ } else {
+ return Invalid // TODO
+ }
}
func (t Type) Elem() Type {
diff --git a/src/reflect/value.go b/src/reflect/value.go
index 35bb2a891..5747ca70c 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -33,7 +33,14 @@ func (v Value) IsNil() bool {
}
func (v Value) Pointer() uintptr {
- panic("unimplemented: (reflect.Value).Pointer()")
+ switch v.Type().Kind() {
+ case UnsafePointer:
+ return uintptr(v.value)
+ case Chan, Func, Map, Ptr, Slice:
+ panic("unimplemented: (reflect.Value).Pointer()")
+ default:
+ panic(&ValueError{"Pointer"})
+ }
}
func (v Value) IsValid() bool {
@@ -57,27 +64,132 @@ func (v Value) CanSet() bool {
}
func (v Value) Bool() bool {
- panic("unimplemented: (reflect.Value).Bool()")
+ switch v.Type().Kind() {
+ case Bool:
+ return uintptr(v.value) != 0
+ default:
+ panic(&ValueError{"Bool"})
+ }
}
func (v Value) Int() int64 {
- panic("unimplemented: (reflect.Value).Int()")
+ switch v.Type().Kind() {
+ case Int:
+ if unsafe.Sizeof(int(0)) <= unsafe.Sizeof(uintptr(0)) {
+ return int64(int(uintptr(v.value)))
+ } else {
+ return int64(*(*int)(v.value))
+ }
+ case Int8:
+ return int64(int8(uintptr(v.value)))
+ case Int16:
+ return int64(int16(uintptr(v.value)))
+ case Int32:
+ if unsafe.Sizeof(int32(0)) <= unsafe.Sizeof(uintptr(0)) {
+ return int64(int32(uintptr(v.value)))
+ } else {
+ return int64(*(*int32)(v.value))
+ }
+ return int64(uintptr(v.value))
+ case Int64:
+ if unsafe.Sizeof(int64(0)) <= unsafe.Sizeof(uintptr(0)) {
+ return int64(uintptr(v.value))
+ } else {
+ return *(*int64)(v.value)
+ }
+ default:
+ panic(&ValueError{"Int"})
+ }
}
func (v Value) Uint() uint64 {
- panic("unimplemented: (reflect.Value).Uint()")
+ switch v.Type().Kind() {
+ case Uintptr, Uint8, Uint16:
+ return uint64(uintptr(v.value))
+ case Uint:
+ if unsafe.Sizeof(uint(0)) <= unsafe.Sizeof(uintptr(0)) {
+ return uint64(uintptr(v.value))
+ } else {
+ // For systems with 16-bit pointers.
+ return uint64(*(*uint)(v.value))
+ }
+ case Uint32:
+ if unsafe.Sizeof(uint32(0)) <= unsafe.Sizeof(uintptr(0)) {
+ return uint64(uintptr(v.value))
+ } else {
+ // For systems with 16-bit pointers.
+ return uint64(*(*uint32)(v.value))
+ }
+ case Uint64:
+ if unsafe.Sizeof(uint64(0)) <= unsafe.Sizeof(uintptr(0)) {
+ return uint64(uintptr(v.value))
+ } else {
+ // For systems with 16-bit or 32-bit pointers.
+ return *(*uint64)(v.value)
+ }
+ default:
+ panic(&ValueError{"Uint"})
+ }
}
func (v Value) Float() float64 {
- panic("unimplemented: (reflect.Value).Float()")
+ switch v.Type().Kind() {
+ case Float32:
+ if unsafe.Sizeof(float32(0)) <= unsafe.Sizeof(uintptr(0)) {
+ // The float is directly stored in the interface value on systems
+ // with 32-bit and 64-bit pointers.
+ return float64(*(*float32)(unsafe.Pointer(&v.value)))
+ } else {
+ // The float is stored as an external value on systems with 16-bit
+ // pointers.
+ return float64(*(*float32)(v.value))
+ }
+ case Float64:
+ if unsafe.Sizeof(float64(0)) <= unsafe.Sizeof(uintptr(0)) {
+ // The float is directly stored in the interface value on systems
+ // with 64-bit pointers.
+ return *(*float64)(unsafe.Pointer(&v.value))
+ } else {
+ // For systems with 16-bit and 32-bit pointers.
+ return *(*float64)(v.value)
+ }
+ default:
+ panic(&ValueError{"Float"})
+ }
}
func (v Value) Complex() complex128 {
- panic("unimplemented: (reflect.Value).Complex()")
+ switch v.Type().Kind() {
+ case Complex64:
+ if unsafe.Sizeof(complex64(0)) <= unsafe.Sizeof(uintptr(0)) {
+ // The complex number is directly stored in the interface value on
+ // systems with 64-bit pointers.
+ return complex128(*(*complex64)(unsafe.Pointer(&v.value)))
+ } else {
+ // The complex number is stored as an external value on systems with
+ // 16-bit and 32-bit pointers.
+ return complex128(*(*complex64)(v.value))
+ }
+ case Complex128:
+ // This is a 128-bit value, which is always stored as an external value.
+ // It may be stored in the pointer directly on very uncommon
+ // architectures with 128-bit pointers, however.
+ return *(*complex128)(v.value)
+ default:
+ panic(&ValueError{"Complex"})
+ }
}
func (v Value) String() string {
- panic("unimplemented: (reflect.Value).String()")
+ switch v.Type().Kind() {
+ case String:
+ // A string value is always bigger than a pointer as it is made of a
+ // pointer and a length.
+ return *(*string)(v.value)
+ default:
+ // Special case because of the special treatment of .String() in Go.
+ return "<T>"
+ }
}
func (v Value) Bytes() []byte {
@@ -147,3 +259,11 @@ func (v Value) SetString(x string) {
func MakeSlice(typ Type, len, cap int) Value {
panic("unimplemented: reflect.MakeSlice()")
}
+
+type ValueError struct {
+ Method string
+}
+
+func (e *ValueError) Error() string {
+ return "reflect: call of reflect.Value." + e.Method + " on invalid type"
+}
diff --git a/testdata/reflect.go b/testdata/reflect.go
index 4493cdc54..2d191e2d7 100644
--- a/testdata/reflect.go
+++ b/testdata/reflect.go
@@ -1,6 +1,9 @@
package main
-import "reflect"
+import (
+ "reflect"
+ "unsafe"
+)
type myint int
@@ -9,4 +12,61 @@ func main() {
println(reflect.TypeOf(int(3)) == reflect.TypeOf(int(5)))
println(reflect.TypeOf(int(3)) == reflect.TypeOf(uint(5)))
println(reflect.TypeOf(myint(3)) == reflect.TypeOf(int(5)))
+
+ println("\nvalues of interfaces")
+ for _, v := range []interface{}{
+ true,
+ false,
+ int(2000),
+ int(-2000),
+ uint(2000),
+ int8(-3),
+ int8(3),
+ uint8(200),
+ int16(-300),
+ int16(300),
+ uint16(50000),
+ int32(7 << 20),
+ int32(-7 << 20),
+ uint32(7 << 20),
+ int64(9 << 40),
+ int64(-9 << 40),
+ uint64(9 << 40),
+ uintptr(12345),
+ float32(3.14),
+ float64(3.14),
+ complex64(1.2 + 0.3i),
+ complex128(1.3 + 0.4i),
+ "foo",
+ unsafe.Pointer(new(int)),
+ } {
+ showValue(v)
+ }
+}
+
+func showValue(v interface{}) {
+ rv := reflect.ValueOf(v)
+ rt := rv.Type()
+ if reflect.TypeOf(v) != rt {
+ panic("direct TypeOf() is different from ValueOf().Type()")
+ }
+ println("reflect type:", rt.Kind().String())
+ switch rt.Kind() {
+ case reflect.Bool:
+ println(" bool:", rv.Bool())
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ println(" int:", rv.Int())
+ case reflect.Uint, reflect.Uintptr, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ println(" uint:", rv.Uint())
+ case reflect.Float32, reflect.Float64:
+ println(" float:", rv.Float())
+ case reflect.Complex64, reflect.Complex128:
+ println(" complex:", rv.Complex())
+ case reflect.String:
+ println(" string:", rv.String())
+ case reflect.UnsafePointer:
+ println(" pointer:", rv.Pointer() != 0)
+ default:
+ println(" unknown type kind!")
+ }
}
diff --git a/testdata/reflect.txt b/testdata/reflect.txt
index 5228cf6b1..8926e9b48 100644
--- a/testdata/reflect.txt
+++ b/testdata/reflect.txt
@@ -2,3 +2,53 @@ matching types
true
false
false
+
+values of interfaces
+reflect type: bool
+ bool: true
+reflect type: bool
+ bool: false
+reflect type: int
+ int: 2000
+reflect type: int
+ int: -2000
+reflect type: uint
+ uint: 2000
+reflect type: int8
+ int: -3
+reflect type: int8
+ int: 3
+reflect type: uint8
+ uint: 200
+reflect type: int16
+ int: -300
+reflect type: int16
+ int: 300
+reflect type: uint16
+ uint: 50000
+reflect type: int32
+ int: 7340032
+reflect type: int32
+ int: -7340032
+reflect type: uint32
+ uint: 7340032
+reflect type: int64
+ int: 9895604649984
+reflect type: int64
+ int: -9895604649984
+reflect type: uint64
+ uint: 9895604649984
+reflect type: uintptr
+ uint: 12345
+reflect type: float32
+ float: +3.140000e+000
+reflect type: float64
+ float: +3.140000e+000
+reflect type: complex64
+ complex: (+1.200000e+000+3.000000e-001i)
+reflect type: complex128
+ complex: (+1.300000e+000+4.000000e-001i)
+reflect type: string
+ string: foo
+reflect type: unsafe.Pointer
+ pointer: true