diff options
author | Ayke van Laethem <[email protected]> | 2018-12-12 14:34:07 +0100 |
---|---|---|
committer | Ayke van Laethem <[email protected]> | 2019-02-05 17:11:09 +0100 |
commit | fb23e9c212241a0611a0d3cc7afe54696e7174f8 (patch) | |
tree | ad3ad57d2c12fb92afc989f9ae04d1eead533b14 | |
parent | 222c4c75b17fd3f3d4d32dee9bdb9f9b4b433a5b (diff) | |
download | tinygo-fb23e9c212241a0611a0d3cc7afe54696e7174f8.tar.gz tinygo-fb23e9c212241a0611a0d3cc7afe54696e7174f8.zip |
reflect: add support for non-named basic types
-rw-r--r-- | compiler/interface-lowering.go | 15 | ||||
-rw-r--r-- | compiler/interface.go | 53 | ||||
-rw-r--r-- | compiler/reflect.go | 55 | ||||
-rw-r--r-- | src/reflect/type.go | 54 | ||||
-rw-r--r-- | src/reflect/value.go | 134 | ||||
-rw-r--r-- | testdata/reflect.go | 62 | ||||
-rw-r--r-- | testdata/reflect.txt | 50 |
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 |