diff options
Diffstat (limited to 'Godeps/_workspace/src/github.com/obscuren/qml/datatype.go')
-rw-r--r-- | Godeps/_workspace/src/github.com/obscuren/qml/datatype.go | 531 |
1 files changed, 0 insertions, 531 deletions
diff --git a/Godeps/_workspace/src/github.com/obscuren/qml/datatype.go b/Godeps/_workspace/src/github.com/obscuren/qml/datatype.go deleted file mode 100644 index 875c54622..000000000 --- a/Godeps/_workspace/src/github.com/obscuren/qml/datatype.go +++ /dev/null @@ -1,531 +0,0 @@ -package qml - -// #include <stdlib.h> -// #include "capi.h" -import "C" - -import ( - "bytes" - "fmt" - "image/color" - "reflect" - "strings" - "unicode" - "unsafe" -) - -var ( - intIs64 bool - intDT C.DataType - - ptrSize = C.size_t(unsafe.Sizeof(uintptr(0))) - - nilPtr = unsafe.Pointer(uintptr(0)) - nilCharPtr = (*C.char)(nilPtr) - - typeString = reflect.TypeOf("") - typeBool = reflect.TypeOf(false) - typeInt = reflect.TypeOf(int(0)) - typeInt64 = reflect.TypeOf(int64(0)) - typeInt32 = reflect.TypeOf(int32(0)) - typeFloat64 = reflect.TypeOf(float64(0)) - typeFloat32 = reflect.TypeOf(float32(0)) - typeIface = reflect.TypeOf(new(interface{})).Elem() - typeRGBA = reflect.TypeOf(color.RGBA{}) - typeObjSlice = reflect.TypeOf([]Object(nil)) - typeObject = reflect.TypeOf([]Object(nil)).Elem() - typePainter = reflect.TypeOf(&Painter{}) - typeList = reflect.TypeOf(&List{}) - typeMap = reflect.TypeOf(&Map{}) - typeGenericMap = reflect.TypeOf(map[string]interface{}(nil)) -) - -func init() { - var i int = 1<<31 - 1 - intIs64 = (i+1 > 0) - if intIs64 { - intDT = C.DTInt64 - } else { - intDT = C.DTInt32 - } -} - -// packDataValue packs the provided Go value into a C.DataValue for -// shiping into C++ land. -// -// For simple types (bool, int, etc) value is converted into a -// native C++ value. For anything else, including cases when value -// has a type that has an underlying simple type, the Go value itself -// is encapsulated into a C++ wrapper so that field access and method -// calls work. -// -// This must be run from the main GUI thread due to the cases where -// calling wrapGoValue is necessary. -func packDataValue(value interface{}, dvalue *C.DataValue, engine *Engine, owner valueOwner) { - datap := unsafe.Pointer(&dvalue.data) - if value == nil { - dvalue.dataType = C.DTInvalid - return - } - switch value := value.(type) { - case string: - dvalue.dataType = C.DTString - cstr, cstrlen := unsafeStringData(value) - *(**C.char)(datap) = cstr - dvalue.len = cstrlen - case bool: - dvalue.dataType = C.DTBool - *(*bool)(datap) = value - case int: - if value > 1<<31-1 { - dvalue.dataType = C.DTInt64 - *(*int64)(datap) = int64(value) - } else { - dvalue.dataType = C.DTInt32 - *(*int32)(datap) = int32(value) - } - case int64: - dvalue.dataType = C.DTInt64 - *(*int64)(datap) = value - case int32: - dvalue.dataType = C.DTInt32 - *(*int32)(datap) = value - case uint64: - dvalue.dataType = C.DTUint64 - *(*uint64)(datap) = value - case uint32: - dvalue.dataType = C.DTUint32 - *(*uint32)(datap) = value - case float64: - dvalue.dataType = C.DTFloat64 - *(*float64)(datap) = value - case float32: - dvalue.dataType = C.DTFloat32 - *(*float32)(datap) = value - case *Common: - dvalue.dataType = C.DTObject - *(*unsafe.Pointer)(datap) = value.addr - case color.RGBA: - dvalue.dataType = C.DTColor - *(*uint32)(datap) = uint32(value.A)<<24 | uint32(value.R)<<16 | uint32(value.G)<<8 | uint32(value.B) - default: - dvalue.dataType = C.DTObject - if obj, ok := value.(Object); ok { - *(*unsafe.Pointer)(datap) = obj.Common().addr - } else { - *(*unsafe.Pointer)(datap) = wrapGoValue(engine, value, owner) - } - } -} - -// TODO Handle byte slices. - -// unpackDataValue converts a value shipped by C++ into a native Go value. -// -// HEADS UP: This is considered safe to be run out of the main GUI thread. -// If that changes, fix the call sites. -func unpackDataValue(dvalue *C.DataValue, engine *Engine) interface{} { - datap := unsafe.Pointer(&dvalue.data) - switch dvalue.dataType { - case C.DTString: - s := C.GoStringN(*(**C.char)(datap), dvalue.len) - // TODO If we move all unpackDataValue calls to the GUI thread, - // can we get rid of this allocation somehow? - C.free(unsafe.Pointer(*(**C.char)(datap))) - return s - case C.DTBool: - return *(*bool)(datap) - case C.DTInt64: - return *(*int64)(datap) - case C.DTInt32: - return int(*(*int32)(datap)) - case C.DTUint64: - return *(*uint64)(datap) - case C.DTUint32: - return *(*uint32)(datap) - case C.DTUintptr: - return *(*uintptr)(datap) - case C.DTFloat64: - return *(*float64)(datap) - case C.DTFloat32: - return *(*float32)(datap) - case C.DTColor: - var c uint32 = *(*uint32)(datap) - return color.RGBA{byte(c >> 16), byte(c >> 8), byte(c), byte(c >> 24)} - case C.DTGoAddr: - // ObjectByName also does this fold conversion, to have access - // to the cvalue. Perhaps the fold should be returned. - fold := (*(**valueFold)(datap)) - ensureEngine(engine.addr, unsafe.Pointer(fold)) - return fold.gvalue - case C.DTInvalid: - return nil - case C.DTObject: - // TODO Would be good to preserve identity on the Go side. See initGoType as well. - obj := &Common{ - engine: engine, - addr: *(*unsafe.Pointer)(datap), - } - if len(converters) > 0 { - // TODO Embed the type name in DataValue to drop these calls. - typeName := obj.TypeName() - if typeName == "PlainObject" { - typeName = strings.TrimRight(obj.String("plainType"), "&*") - if strings.HasPrefix(typeName, "const ") { - typeName = typeName[6:] - } - } - if f, ok := converters[typeName]; ok { - return f(engine, obj) - } - } - return obj - case C.DTValueList, C.DTValueMap: - var dvlist []C.DataValue - var dvlisth = (*reflect.SliceHeader)(unsafe.Pointer(&dvlist)) - dvlisth.Data = uintptr(*(*unsafe.Pointer)(datap)) - dvlisth.Len = int(dvalue.len) - dvlisth.Cap = int(dvalue.len) - result := make([]interface{}, len(dvlist)) - for i := range result { - result[i] = unpackDataValue(&dvlist[i], engine) - } - C.free(*(*unsafe.Pointer)(datap)) - if dvalue.dataType == C.DTValueList { - return &List{result} - } else { - return &Map{result} - } - } - panic(fmt.Sprintf("unsupported data type: %d", dvalue.dataType)) -} - -func dataTypeOf(typ reflect.Type) C.DataType { - // Compare against the specific types rather than their kind. - // Custom types may have methods that must be supported. - switch typ { - case typeString: - return C.DTString - case typeBool: - return C.DTBool - case typeInt: - return intDT - case typeInt64: - return C.DTInt64 - case typeInt32: - return C.DTInt32 - case typeFloat32: - return C.DTFloat32 - case typeFloat64: - return C.DTFloat64 - case typeIface: - return C.DTAny - case typeRGBA: - return C.DTColor - case typeObjSlice: - return C.DTListProperty - } - return C.DTObject -} - -var typeInfoSize = C.size_t(unsafe.Sizeof(C.GoTypeInfo{})) -var memberInfoSize = C.size_t(unsafe.Sizeof(C.GoMemberInfo{})) - -var typeInfoCache = make(map[reflect.Type]*C.GoTypeInfo) - -func appendLoweredName(buf []byte, name string) []byte { - var last rune - var lasti int - for i, rune := range name { - if !unicode.IsUpper(rune) { - if lasti == 0 { - last = unicode.ToLower(last) - } - buf = append(buf, string(last)...) - buf = append(buf, name[i:]...) - return buf - } - if i > 0 { - buf = append(buf, string(unicode.ToLower(last))...) - } - lasti, last = i, rune - } - return append(buf, string(unicode.ToLower(last))...) -} - -func typeInfo(v interface{}) *C.GoTypeInfo { - vt := reflect.TypeOf(v) - for vt.Kind() == reflect.Ptr { - vt = vt.Elem() - } - - typeInfo := typeInfoCache[vt] - if typeInfo != nil { - return typeInfo - } - - typeInfo = (*C.GoTypeInfo)(C.malloc(typeInfoSize)) - typeInfo.typeName = C.CString(vt.Name()) - typeInfo.metaObject = nilPtr - typeInfo.paint = (*C.GoMemberInfo)(nilPtr) - - var setters map[string]int - var getters map[string]int - - // TODO Only do that if it's a struct? - vtptr := reflect.PtrTo(vt) - - if vt.Kind() != reflect.Struct { - panic(fmt.Sprintf("handling of %s (%#v) is incomplete; please report to the developers", vt, v)) - } - - numField := vt.NumField() - numMethod := vtptr.NumMethod() - privateFields := 0 - privateMethods := 0 - - // struct { FooBar T; Baz T } => "fooBar\0baz\0" - namesLen := 0 - for i := 0; i < numField; i++ { - field := vt.Field(i) - if field.PkgPath != "" { - privateFields++ - continue - } - namesLen += len(field.Name) + 1 - } - for i := 0; i < numMethod; i++ { - method := vtptr.Method(i) - if method.PkgPath != "" { - privateMethods++ - continue - } - namesLen += len(method.Name) + 1 - - // Track setters and getters. - if len(method.Name) > 3 && method.Name[:3] == "Set" { - if method.Type.NumIn() == 2 { - if setters == nil { - setters = make(map[string]int) - } - setters[method.Name[3:]] = i - } - } else if method.Type.NumIn() == 1 && method.Type.NumOut() == 1 { - if getters == nil { - getters = make(map[string]int) - } - getters[method.Name] = i - } - } - names := make([]byte, 0, namesLen) - for i := 0; i < numField; i++ { - field := vt.Field(i) - if field.PkgPath != "" { - continue // not exported - } - names = appendLoweredName(names, field.Name) - names = append(names, 0) - } - for i := 0; i < numMethod; i++ { - method := vtptr.Method(i) - if method.PkgPath != "" { - continue // not exported - } - if _, ok := getters[method.Name]; !ok { - continue - } - if _, ok := setters[method.Name]; !ok { - delete(getters, method.Name) - continue - } - // This is a getter method - names = appendLoweredName(names, method.Name) - names = append(names, 0) - } - for i := 0; i < numMethod; i++ { - method := vtptr.Method(i) - if method.PkgPath != "" { - continue // not exported - } - if _, ok := getters[method.Name]; ok { - continue // getter already handled above - } - names = appendLoweredName(names, method.Name) - names = append(names, 0) - } - if len(names) != namesLen { - panic("pre-allocated buffer size was wrong") - } - typeInfo.memberNames = C.CString(string(names)) - - // Assemble information on members. - membersLen := numField - privateFields + numMethod - privateMethods - membersi := uintptr(0) - mnamesi := uintptr(0) - members := uintptr(C.malloc(memberInfoSize * C.size_t(membersLen))) - mnames := uintptr(unsafe.Pointer(typeInfo.memberNames)) - for i := 0; i < numField; i++ { - field := vt.Field(i) - if field.PkgPath != "" { - continue // not exported - } - memberInfo := (*C.GoMemberInfo)(unsafe.Pointer(members + uintptr(memberInfoSize)*membersi)) - memberInfo.memberName = (*C.char)(unsafe.Pointer(mnames + mnamesi)) - memberInfo.memberType = dataTypeOf(field.Type) - memberInfo.reflectIndex = C.int(i) - memberInfo.reflectGetIndex = -1 - memberInfo.reflectSetIndex = -1 - memberInfo.addrOffset = C.int(field.Offset) - membersi += 1 - mnamesi += uintptr(len(field.Name)) + 1 - if methodIndex, ok := setters[field.Name]; ok { - memberInfo.reflectSetIndex = C.int(methodIndex) - } - } - for i := 0; i < numMethod; i++ { - method := vtptr.Method(i) - if method.PkgPath != "" { - continue // not exported - } - if _, ok := getters[method.Name]; !ok { - continue // not a getter - } - memberInfo := (*C.GoMemberInfo)(unsafe.Pointer(members + uintptr(memberInfoSize)*membersi)) - memberInfo.memberName = (*C.char)(unsafe.Pointer(mnames + mnamesi)) - memberInfo.memberType = dataTypeOf(method.Type.Out(0)) - memberInfo.reflectIndex = -1 - memberInfo.reflectGetIndex = C.int(getters[method.Name]) - memberInfo.reflectSetIndex = C.int(setters[method.Name]) - memberInfo.addrOffset = 0 - membersi += 1 - mnamesi += uintptr(len(method.Name)) + 1 - } - for i := 0; i < numMethod; i++ { - method := vtptr.Method(i) - if method.PkgPath != "" { - continue // not exported - } - if _, ok := getters[method.Name]; ok { - continue // getter already handled above - } - memberInfo := (*C.GoMemberInfo)(unsafe.Pointer(members + uintptr(memberInfoSize)*membersi)) - memberInfo.memberName = (*C.char)(unsafe.Pointer(mnames + mnamesi)) - memberInfo.memberType = C.DTMethod - memberInfo.reflectIndex = C.int(i) - memberInfo.reflectGetIndex = -1 - memberInfo.reflectSetIndex = -1 - memberInfo.addrOffset = 0 - signature, result := methodQtSignature(method) - // TODO The signature data might be embedded in the same array as the member names. - memberInfo.methodSignature = C.CString(signature) - memberInfo.resultSignature = C.CString(result) - // TODO Sort out methods with a variable number of arguments. - // It's called while bound, so drop the receiver. - memberInfo.numIn = C.int(method.Type.NumIn() - 1) - memberInfo.numOut = C.int(method.Type.NumOut()) - membersi += 1 - mnamesi += uintptr(len(method.Name)) + 1 - - if method.Name == "Paint" && memberInfo.numIn == 1 && memberInfo.numOut == 0 && method.Type.In(1) == typePainter { - typeInfo.paint = memberInfo - } - } - typeInfo.members = (*C.GoMemberInfo)(unsafe.Pointer(members)) - typeInfo.membersLen = C.int(membersLen) - - typeInfo.fields = typeInfo.members - typeInfo.fieldsLen = C.int(numField - privateFields + len(getters)) - typeInfo.methods = (*C.GoMemberInfo)(unsafe.Pointer(members + uintptr(memberInfoSize)*uintptr(typeInfo.fieldsLen))) - typeInfo.methodsLen = C.int(numMethod - privateMethods - len(getters)) - - if int(membersi) != membersLen { - panic("used more space than allocated for member names") - } - if int(mnamesi) != namesLen { - panic("allocated buffer doesn't match used space") - } - if typeInfo.fieldsLen+typeInfo.methodsLen != typeInfo.membersLen { - panic("lengths are inconsistent") - } - - typeInfoCache[vt] = typeInfo - return typeInfo -} - -func methodQtSignature(method reflect.Method) (signature, result string) { - var buf bytes.Buffer - for i, rune := range method.Name { - if i == 0 { - buf.WriteRune(unicode.ToLower(rune)) - } else { - buf.WriteString(method.Name[i:]) - break - } - } - buf.WriteByte('(') - n := method.Type.NumIn() - for i := 1; i < n; i++ { - if i > 1 { - buf.WriteByte(',') - } - buf.WriteString("QVariant") - } - buf.WriteByte(')') - signature = buf.String() - - switch method.Type.NumOut() { - case 0: - // keep it as "" - case 1: - result = "QVariant" - default: - result = "QVariantList" - } - return -} - -func hashable(value interface{}) (hashable bool) { - defer func() { recover() }() - return value == value -} - -// unsafeString returns a Go string backed by C data. -// -// If the C data is deallocated or moved, the string will be -// invalid and will crash the program if used. As such, the -// resulting string must only be used inside the implementation -// of the qml package and while the life time of the C data -// is guaranteed. -func unsafeString(data *C.char, size C.int) string { - var s string - sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) - sh.Data = uintptr(unsafe.Pointer(data)) - sh.Len = int(size) - return s -} - -// unsafeStringData returns a C string backed by Go data. The C -// string is NOT null-terminated, so its length must be taken -// into account. -// -// If the s Go string is garbage collected, the returned C data -// will be invalid and will crash the program if used. As such, -// the resulting data must only be used inside the implementation -// of the qml package and while the life time of the Go string -// is guaranteed. -func unsafeStringData(s string) (*C.char, C.int) { - return *(**C.char)(unsafe.Pointer(&s)), C.int(len(s)) -} - -// unsafeBytesData returns a C string backed by Go data. The C -// string is NOT null-terminated, so its length must be taken -// into account. -// -// If the array backing the b Go slice is garbage collected, the -// returned C data will be invalid and will crash the program if -// used. As such, the resulting data must only be used inside the -// implementation of the qml package and while the life time of -// the Go array is guaranteed. -func unsafeBytesData(b []byte) (*C.char, C.int) { - return *(**C.char)(unsafe.Pointer(&b)), C.int(len(b)) -} |