Skip to content

Commit ed6968d

Browse files
author
Matt Peterson
committed
Move a few things to an internal util package for use by more things
1 parent 9adc292 commit ed6968d

File tree

9 files changed

+107
-82
lines changed

9 files changed

+107
-82
lines changed

bridge.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"unsafe"
2121

2222
"github.com/limetext/qml-go/cdata"
23+
"github.com/limetext/qml-go/internal/util"
2324
)
2425

2526
type mainThreadFunc struct {
@@ -240,7 +241,7 @@ const (
240241
func wrapGoValue(engine *Engine, gvalue interface{}, owner valueOwner) (cvalue unsafe.Pointer) {
241242
gvaluev := reflect.ValueOf(gvalue)
242243
gvaluek := gvaluev.Kind()
243-
if gvaluek == reflect.Struct && !hashable(gvalue) {
244+
if gvaluek == reflect.Struct && !util.Hashable(gvalue) {
244245
name := gvaluev.Type().Name()
245246
if name != "" {
246247
name = " (" + name + ")"
@@ -256,7 +257,7 @@ func wrapGoValue(engine *Engine, gvalue interface{}, owner valueOwner) (cvalue u
256257
hashableGvalue := gvalue
257258
if gvaluev.Kind() == reflect.Slice {
258259
hashableGvalue = *(*reflect.SliceHeader)(unsafe.Pointer(gvaluev.Pointer()))
259-
} else if !hashable(gvalue) {
260+
} else if !util.Hashable(gvalue) {
260261
panic(fmt.Sprintf("gvalue not hashable: %v %v", gvaluev.Type(), gvaluev.Kind()))
261262
}
262263

@@ -424,7 +425,7 @@ func hookGoValueReadField(enginep, foldp unsafe.Pointer, reflectIndex, getIndex,
424425
if fieldk == reflect.Slice || fieldk == reflect.Struct && field.Type() != typeRGBA {
425426
if field.CanAddr() {
426427
field = field.Addr()
427-
} else if !hashable(field.Interface()) {
428+
} else if !util.Hashable(field.Interface()) {
428429
t := reflect.ValueOf(fold.gvalue).Type()
429430
for t.Kind() == reflect.Ptr {
430431
t = t.Elem()

cpp/capi.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ typedef struct {
106106
int line;
107107
} LogMessage;
108108

109+
109110
void newGuiApplication();
110111
void applicationExec();
111112
void applicationExit();
@@ -117,6 +118,7 @@ void idleTimerStart();
117118
void *currentThread();
118119
void *appThread();
119120

121+
120122
QQmlEngine_ *newEngine(QObject_ *parent);
121123
QQmlContext_ *engineRootContext(QQmlEngine_ *engine);
122124
void engineSetOwnershipCPP(QQmlEngine_ *engine, QObject_ *object);
@@ -128,6 +130,7 @@ void engineAddImportPath(QQmlEngine_ *engine, const char *path, int pathLen);
128130
void engineClearPluginPaths(QQmlEngine_ *engine);
129131
void engineAddPluginPath(QQmlEngine_ *engine, const char *path, int pathLen);
130132
void engineClearComponentCache(QQmlEngine_ *engine);
133+
131134
void coreAddLibraryPath(const char *path, int pathLen);
132135

133136
void contextGetProperty(QQmlContext_ *context, QString_ *name, DataValue *value);

datatype.go

Lines changed: 6 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"strings"
1313
"unicode"
1414
"unsafe"
15+
"github.com/limetext/qml-go/internal/util"
1516
)
1617

1718
var (
@@ -70,9 +71,9 @@ func packDataValue(value interface{}, dvalue *C.DataValue, engine *Engine, owner
7071
switch value := value.(type) {
7172
case string:
7273
dvalue.dataType = C.DTString
73-
cstr, cstrlen := unsafeStringData(value)
74-
*(**C.char)(datap) = cstr
75-
dvalue.len = cstrlen
74+
cstr, cstrlen := util.UnsafeStringData(value)
75+
*(**C.char)(datap) = (*C.char)(cstr)
76+
dvalue.len = C.int(cstrlen)
7677
case bool:
7778
dvalue.dataType = C.DTBool
7879
*(*bool)(datap) = value
@@ -225,8 +226,8 @@ func dataTypeOf(typ reflect.Type) C.DataType {
225226
return C.DTObject
226227
}
227228

228-
var typeInfoSize = C.size_t(unsafe.Sizeof(C.GoTypeInfo{}))
229-
var memberInfoSize = C.size_t(unsafe.Sizeof(C.GoMemberInfo{}))
229+
const typeInfoSize = C.size_t(C.sizeof_GoTypeInfo)
230+
const memberInfoSize = C.size_t(C.sizeof_GoMemberInfo)
230231

231232
var typeInfoCache = make(map[reflect.Type]*C.GoTypeInfo)
232233

@@ -480,49 +481,3 @@ func methodQtSignature(method reflect.Method) (signature, result string) {
480481
}
481482
return
482483
}
483-
484-
func hashable(value interface{}) (hashable bool) {
485-
defer func() { recover() }()
486-
return value == value
487-
}
488-
489-
// unsafeString returns a Go string backed by C data.
490-
//
491-
// If the C data is deallocated or moved, the string will be
492-
// invalid and will crash the program if used. As such, the
493-
// resulting string must only be used inside the implementation
494-
// of the qml package and while the life time of the C data
495-
// is guaranteed.
496-
func unsafeString(data *C.char, size C.int) string {
497-
var s string
498-
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
499-
sh.Data = uintptr(unsafe.Pointer(data))
500-
sh.Len = int(size)
501-
return s
502-
}
503-
504-
// unsafeStringData returns a C string backed by Go data. The C
505-
// string is NOT null-terminated, so its length must be taken
506-
// into account.
507-
//
508-
// If the s Go string is garbage collected, the returned C data
509-
// will be invalid and will crash the program if used. As such,
510-
// the resulting data must only be used inside the implementation
511-
// of the qml package and while the life time of the Go string
512-
// is guaranteed.
513-
func unsafeStringData(s string) (*C.char, C.int) {
514-
return *(**C.char)(unsafe.Pointer(&s)), C.int(len(s))
515-
}
516-
517-
// unsafeBytesData returns a C string backed by Go data. The C
518-
// string is NOT null-terminated, so its length must be taken
519-
// into account.
520-
//
521-
// If the array backing the b Go slice is garbage collected, the
522-
// returned C data will be invalid and will crash the program if
523-
// used. As such, the resulting data must only be used inside the
524-
// implementation of the qml package and while the life time of
525-
// the Go array is guaranteed.
526-
func unsafeBytesData(b []byte) (*C.char, C.int) {
527-
return *(**C.char)(unsafe.Pointer(&b)), C.int(len(b))
528-
}

internal/util/util.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package util
2+
3+
import (
4+
"reflect"
5+
"unsafe"
6+
)
7+
8+
func Hashable(value interface{}) (hashable bool) {
9+
defer func() { recover() }()
10+
return value == value
11+
}
12+
13+
// unsafeString returns a Go string backed by C data.
14+
//
15+
// If the C data is deallocated or moved, the string will be
16+
// invalid and will crash the program if used. As such, the
17+
// resulting string must only be used inside the implementation
18+
// of the qml package and while the life time of the C data
19+
// is guaranteed.
20+
func UnsafeString(data unsafe.Pointer, size int) string {
21+
var s string
22+
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
23+
sh.Data = uintptr(data)
24+
sh.Len = size
25+
return s
26+
}
27+
28+
// unsafeStringData returns a C string backed by Go data. The C
29+
// string is NOT null-terminated, so its length must be taken
30+
// into account.
31+
//
32+
// If the s Go string is garbage collected, the returned C data
33+
// will be invalid and will crash the program if used. As such,
34+
// the resulting data must only be used inside the implementation
35+
// of the qml package and while the life time of the Go string
36+
// is guaranteed.
37+
func UnsafeStringData(s string) (unsafe.Pointer, int) {
38+
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
39+
return unsafe.Pointer(sh.Data), sh.Len
40+
}
41+
42+
// unsafeBytesData returns a C string backed by Go data. The C
43+
// string is NOT null-terminated, so its length must be taken
44+
// into account.
45+
//
46+
// If the array backing the b Go slice is garbage collected, the
47+
// returned C data will be invalid and will crash the program if
48+
// used. As such, the resulting data must only be used inside the
49+
// implementation of the qml package and while the life time of
50+
// the Go array is guaranteed.
51+
func UnsafeBytesData(b []byte) (unsafe.Pointer, int) {
52+
return *(*unsafe.Pointer)(unsafe.Pointer(&b)), len(b)
53+
}

log.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import (
99
"log"
1010
"path/filepath"
1111
"strings"
12+
"unsafe"
13+
14+
"github.com/limetext/qml-go/internal/util"
1215
)
1316

1417
// SetLogger sets the target for messages logged by the qml package,
@@ -97,7 +100,7 @@ func init() {
97100
//export hookLogHandler
98101
func hookLogHandler(cmsg *C.LogMessage) {
99102
// Workarund for QTBUG-35943
100-
text := unsafeString(cmsg.text, cmsg.textLen)
103+
text := util.UnsafeString(unsafe.Pointer(cmsg.text), int(cmsg.textLen))
101104
if strings.HasPrefix(text, `"Qt Warning: Compose file:`) {
102105
return
103106
}
@@ -139,8 +142,8 @@ func (m *logMessage) Line() int {
139142

140143
func (m *logMessage) String() string {
141144
m.assertValid()
142-
file := unsafeString(m.c.file, m.c.fileLen)
143-
text := unsafeString(m.c.text, m.c.textLen)
145+
file := util.UnsafeString(unsafe.Pointer(m.c.file), int(m.c.fileLen))
146+
text := util.UnsafeString(unsafe.Pointer(m.c.text), int(m.c.textLen))
144147
return fmt.Sprintf("%s:%d: %s", filepath.Base(file), m.c.line, text)
145148
}
146149

qml.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ import "C"
99
import (
1010
"errors"
1111
"unsafe"
12+
13+
"github.com/limetext/qml-go/internal/util"
1214
)
1315

1416
func AddLibraryPath(path string) {
15-
cpath, cpathLen := unsafeStringData(path)
17+
cpath, cpathLen := util.UnsafeStringData(path)
1618
RunMain(func() {
17-
C.coreAddLibraryPath(cpath, cpathLen)
19+
C.coreAddLibraryPath((*C.char)(cpath), C.int(cpathLen))
1820
})
1921
}
2022

qmlcommon.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ import (
1111
"image/color"
1212
"os"
1313
"reflect"
14+
"runtime/debug"
1415
"unsafe"
16+
17+
"github.com/limetext/qml-go/internal/util"
1518
)
1619

1720
// Common implements the common behavior of all QML objects.
@@ -274,11 +277,11 @@ func (obj *Common) Map(property string) *Map {
274277
// ObjectByName panics if the object is not found.
275278
func (obj *Common) ObjectByName(objectName string) Object {
276279
obj.assertInitialized()
277-
cname, cnamelen := unsafeStringData(objectName)
280+
cname, cnamelen := util.UnsafeStringData(objectName)
278281
var dvalue C.DataValue
279282
var object Object
280283
RunMain(func() {
281-
qname := C.newString(cname, cnamelen)
284+
qname := C.newString((*C.char)(cname), C.int(cnamelen))
282285
defer C.delString(qname)
283286
C.objectFindChild(obj.addr, qname, &dvalue)
284287
// unpackDataValue will also initialize the Go type, if necessary.
@@ -308,7 +311,7 @@ func (obj *Common) Call(method string, params ...interface{}) interface{} {
308311
if len(params) > len(dataValueArray) {
309312
panic("too many parameters")
310313
}
311-
cmethod, cmethodLen := unsafeStringData(method)
314+
cmethod, cmethodLen := util.UnsafeStringData(method)
312315
var result C.DataValue
313316
var cerr *C.error
314317
RunMain(func() {
@@ -328,7 +331,7 @@ func (obj *Common) Call(method string, params ...interface{}) interface{} {
328331
// destroyed while waiting to run on the main thread
329332
// TODO: What to do about this???
330333
}
331-
cerr = C.objectInvoke(obj.addr, cmethod, cmethodLen, &result, &dataValueArray[0], C.int(len(params)))
334+
cerr = C.objectInvoke(obj.addr, (*C.char)(cmethod), C.int(cmethodLen), &result, &dataValueArray[0], C.int(len(params)))
332335
})
333336
if cerr != nil {
334337
fmt.Fprintf(os.Stderr, "Common: %#v\n", obj)
@@ -429,10 +432,10 @@ func (obj *Common) On(signal string, function interface{}) {
429432
if funct.NumIn() > C.MaxParams {
430433
panic("function takes too many arguments")
431434
}
432-
csignal, csignallen := unsafeStringData(signal)
435+
csignal, csignallen := util.UnsafeStringData(signal)
433436
var cerr *C.error
434437
RunMain(func() {
435-
cerr = C.objectConnect(obj.addr, csignal, csignallen, obj.engine.addr, unsafe.Pointer(&function), C.int(funcv.Type().NumIn()))
438+
cerr = C.objectConnect(obj.addr, (*C.char)(csignal), C.int(csignallen), obj.engine.addr, unsafe.Pointer(&function), C.int(funcv.Type().NumIn()))
436439
if cerr == nil {
437440
connectedFunction[&function] = true
438441
stats.connectionsAlive(+1)

qmlcontext.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ package qml
55
// #include "capi.h"
66
//
77
import "C"
8+
import (
9+
"github.com/limetext/qml-go/internal/util"
10+
)
811

912
// Context represents a QML context that can hold variables visible
1013
// to logic running within it.
@@ -25,12 +28,12 @@ type Context struct {
2528
// not be garbage collected until the engine is destroyed, even if the
2629
// value is unused or changed.
2730
func (ctx *Context) SetVar(name string, value interface{}) {
28-
cname, cnamelen := unsafeStringData(name)
31+
cname, cnamelen := util.UnsafeStringData(name)
2932
RunMain(func() {
3033
var dvalue C.DataValue
3134
packDataValue(value, &dvalue, ctx.engine, cppOwner)
3235

33-
qname := C.newString(cname, cnamelen)
36+
qname := C.newString((*C.char)(cname), C.int(cnamelen))
3437
defer C.delString(qname)
3538

3639
C.contextSetProperty(ctx.addr, qname, &dvalue)
@@ -54,11 +57,11 @@ func (ctx *Context) SetVars(value interface{}) {
5457

5558
// Var returns the context variable with the given name.
5659
func (ctx *Context) Var(name string) interface{} {
57-
cname, cnamelen := unsafeStringData(name)
60+
cname, cnamelen := util.UnsafeStringData(name)
5861

5962
var dvalue C.DataValue
6063
RunMain(func() {
61-
qname := C.newString(cname, cnamelen)
64+
qname := C.newString((*C.char)(cname), C.int(cnamelen))
6265
defer C.delString(qname)
6366

6467
C.contextGetProperty(ctx.addr, qname, &dvalue)

0 commit comments

Comments
 (0)