Skip to content

Commit

Permalink
support for go 1.12 fix
Browse files Browse the repository at this point in the history
  • Loading branch information
nanu-c committed May 5, 2019
1 parent cd05c39 commit 8460421
Show file tree
Hide file tree
Showing 30 changed files with 23 additions and 2,398 deletions.
2 changes: 0 additions & 2 deletions all.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
#include "cpp/capi.cpp"
#include "cpp/govalue.cpp"
#include "cpp/govaluetype.cpp"
#include "cpp/goitemmodel.cpp"
#include "cpp/goitemmodel_api.cpp"
#include "cpp/idletimer.cpp"
#include "cpp/connector.cpp"

Expand Down
161 changes: 23 additions & 138 deletions bridge.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package qml

// #cgo CPPFLAGS: -I./cpp
// #cgo CXXFLAGS: -std=c++0x -Wall -fno-strict-aliasing
// #cgo CXXFLAGS: -std=c++0x -pedantic-errors -Wall -fno-strict-aliasing
// #cgo LDFLAGS: -lstdc++
// #cgo pkg-config: Qt5Core Qt5Widgets Qt5Quick
//
Expand All @@ -16,20 +16,12 @@ import (
"os"
"reflect"
"runtime"
"runtime/debug"
"sync/atomic"
"unsafe"

"github.com/nanu-c/qml-go/cdata"
"github.com/nanu-c/qml-go/qpainter"
"github.com/nanu-c/qml-go/tools/util"
)

type mainThreadFunc struct {
f func()
done chan struct{}
}

var (
guiFunc = make(chan func())
guiDone = make(chan struct{})
Expand All @@ -54,45 +46,19 @@ func init() {
// The Run function must necessarily be called from the same goroutine as
// the main function or the application may fail when running on Mac OS.
func Run(f func() error) error {
return RunArgs(os.Args, f)
}

// RunArgs runs the main QML event loop, runs f, and then terminates the
// event loop once f returns.
//
// Most functions from the qml package block until RunArgs is called.
//
// The RunArgs function must necessarily be called from the same goroutine as
// the main function or the application may fail when running on Mac OS.
func RunArgs(args []string, f func() error) error {
if cdata.Ref() != guiMainRef {
panic("RunArgs must be called on the initial goroutine so apps are portable to Mac OS")
panic("Run must be called on the initial goroutine so apps are portable to Mac OS")
}
if !atomic.CompareAndSwapInt32(&initialized, 0, 1) {
panic("qml.RunArgs called more than once")
}

// so, technically we should be freeing the C.CString below, but newGuiApplication
// can really only be called once, and the args are not going to be large
argc := len(args)
argv := make([]*C.char, argc+1)
for i := 0; i < argc; i++ {
argv[i] = C.CString(args[i])
panic("qml.Run called more than once")
}
argv[argc] = nil

// argvP := unsafe.Pointer(&argv[0])

C.newGuiApplication(C.int(argc), &argv[0])
C.newGuiApplication()
C.idleTimerInit((*C.int32_t)(&guiIdleRun))
done := make(chan error, 1)
go func() {
RunMain(func() {}) // Block until the event loop is running.
done <- f()
C.applicationExit()
// this is here to keep the argv slice alive while the application is running
// TODO: change this to something without side effects? runtime.KeepAlive?
fmt.Sprintln(argc, argv)
}()
C.applicationExec()
return <-done
Expand All @@ -112,31 +78,14 @@ func RunMain(f func()) {

// Tell Qt we're waiting for the idle hook to be called.
if atomic.AddInt32(&guiIdleRun, 1) == 1 {
// C.idleTimerStart()
}

var doneChan chan struct{}

select {
case doneChan = <-doneChanBuffer:
// yay
default:
doneChan = make(chan struct{}, 1)
C.idleTimerStart()
}

// Send f to be executed by the idle hook in the main GUI thread.
guiFunc <- mainThreadFunc{f: f, done: doneChan}
C.idleTimerStart()
guiFunc <- f

// Wait until f is done executing.
<-doneChan

select {
case doneChanBuffer <- doneChan:
return
default:
close(doneChan)
}
<-guiDone
}

// Lock freezes all QML activity by blocking the main event loop.
Expand Down Expand Up @@ -227,13 +176,7 @@ func Changed(value, fieldAddr interface{}) {
//
//export hookIdleTimer
func hookIdleTimer() {
ref := cdata.Ref()
if ref != guiMainRef && ref != atomic.LoadUintptr(&guiPaintRef) {
// Not within the GUI or render threads!
fmt.Println("AAARG! hookIdleTimer didn't run on the main thread!")
// return
}
var f mainThreadFunc
var f func()
for {
select {
case f = <-guiFunc:
Expand All @@ -244,46 +187,12 @@ func hookIdleTimer() {
return
}
}
// fmt.Fprintf(os.Stderr, "hookIdleTimer: %v\n", runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name())
// fmt.Fprintf(os.Stderr, "hookIdleTimer: %v\n", reflect.ValueOf(f))
func() {
defer func() {
if r := recover(); r != nil {
fmt.Printf("Panic in RunMain func: %v\n", r)
debug.PrintStack()
}
}()

f.f()
}()

f.done <- struct{}{}
f()
guiDone <- struct{}{}
atomic.AddInt32(&guiIdleRun, -1)
}
}

var valueFoldRefCounter C.GoValueRef = 1
var valueFoldMap = make(map[C.GoValueRef]*valueFold)

func getFoldRef(fold *valueFold) C.GoValueRef {
if fold.ref != 0 {
return fold.ref
}

ref := (C.GoValueRef)(atomic.AddUintptr((*uintptr)(unsafe.Pointer(&valueFoldRefCounter)), 1))
fold.ref = ref
valueFoldMap[ref] = fold
return ref
}

func foldFromRef(ref C.GoValueRef) *valueFold {
return valueFoldMap[ref]
}

func clearFoldRef(ref C.GoValueRef) {
delete(valueFoldMap, ref)
}

type valueFold struct {
engine *Engine
gvalue interface{}
Expand All @@ -292,7 +201,6 @@ type valueFold struct {
prev *valueFold
next *valueFold
owner valueOwner
ref C.GoValueRef
}

// cgoFolds holds all fold values that get reference to on the cgo space.
Expand Down Expand Up @@ -338,7 +246,7 @@ const (
func wrapGoValue(engine *Engine, gvalue interface{}, owner valueOwner) (cvalue unsafe.Pointer) {
gvaluev := reflect.ValueOf(gvalue)
gvaluek := gvaluev.Kind()
if gvaluek == reflect.Struct && !util.Hashable(gvalue) {
if gvaluek == reflect.Struct && !hashable(gvalue) {
name := gvaluev.Type().Name()
if name != "" {
name = " (" + name + ")"
Expand All @@ -351,17 +259,10 @@ func wrapGoValue(engine *Engine, gvalue interface{}, owner valueOwner) (cvalue u

painting := cdata.Ref() == atomic.LoadInt64(&guiPaintRef)

hashableGvalue := gvalue
if gvaluev.Kind() == reflect.Slice {
hashableGvalue = *(*reflect.SliceHeader)(unsafe.Pointer(gvaluev.Pointer()))
} else if !util.Hashable(gvalue) {
panic(fmt.Sprintf("gvalue not hashable: %v %v", gvaluev.Type(), gvaluev.Kind()))
}

// Cannot reuse a jsOwner because the QML runtime may choose to destroy
// the value _after_ we hand it a new reference to the same value.
// See issue #68 for details.
prev, ok := engine.values[hashableGvalue]
prev, ok := engine.values[gvalue]
if ok && (prev.owner == cppOwner || painting) {
return prev.cvalue
}
Expand All @@ -385,7 +286,7 @@ func wrapGoValue(engine *Engine, gvalue interface{}, owner valueOwner) (cvalue u
fold.next = prev
prev.prev = fold
}
engine.values[hashableGvalue] = fold
engine.values[gvalue] = fold

//fmt.Printf("[DEBUG] value alive (wrapped): cvalue=%x gvalue=%x/%#v\n", fold.cvalue, addrOf(fold.gvalue), fold.gvalue)
stats.valuesAlive(+1)
Expand Down Expand Up @@ -451,9 +352,9 @@ func hookGoValueDestroyed(enginep unsafe.Pointer, foldr C.GoRef) {
if len(typeNew) == before {
panic("destroying value without an associated engine; who created the value?")
}
} else if engines[engine.savedAddr] == nil {
} else if engines[engine.addr] == nil {
// Must never do that. The engine holds memory references that C++ depends on.
panic(fmt.Sprintf("engine %p was released from global list while its values were still alive", engine.savedAddr))
panic(fmt.Sprintf("engine %p was released from global list while its values were still alive", engine.addr))
} else {
switch {
case fold.prev != nil:
Expand All @@ -475,7 +376,7 @@ func hookGoValueDestroyed(enginep unsafe.Pointer, foldr C.GoRef) {
panic("destroying value that knows about the engine, but the engine doesn't know about the value; who cleared the engine?")
}
if engine.destroyed && len(engine.values) == 0 {
delete(engines, engine.savedAddr)
delete(engines, engine.addr)
}
}
fold.destroyRef()
Expand All @@ -493,6 +394,7 @@ func deref(value reflect.Value) reflect.Value {
}
return value
}
panic("cannot happen")
}

//export hookGoValueReadField
Expand All @@ -503,18 +405,7 @@ func hookGoValueReadField(enginep unsafe.Pointer, foldr C.GoRef, reflectIndex, g
if getIndex >= 0 {
field = reflect.ValueOf(fold.gvalue).Method(int(getIndex)).Call(nil)[0]
} else {
val := deref(reflect.ValueOf(fold.gvalue))
// defer func() {
// if r := recover(); r != nil {
// fmt.Fprintf(os.Stderr, "panic in hookGoValueReadField %v %v\n", val, reflectIndex)
// }
// }()
if !val.IsValid() {
// panic(fmt.Sprintf("invalid value in hookGoValueReadField %#v\n", fold))
resultdv.dataType = C.DTInvalid
return
}
field = val.Field(int(reflectIndex))
field = deref(reflect.ValueOf(fold.gvalue)).Field(int(reflectIndex))
}
field = deref(field)

Expand All @@ -531,7 +422,7 @@ func hookGoValueReadField(enginep unsafe.Pointer, foldr C.GoRef, reflectIndex, g
if fieldk == reflect.Slice || fieldk == reflect.Struct && field.Type() != typeRGBA {
if field.CanAddr() {
field = field.Addr()
} else if !util.Hashable(field.Interface()) {
} else if !hashable(field.Interface()) {
t := reflect.ValueOf(fold.gvalue).Type()
for t.Kind() == reflect.Ptr {
t = t.Elem()
Expand Down Expand Up @@ -650,11 +541,7 @@ func hookGoValueCallMethod(enginep unsafe.Pointer, foldr C.GoRef, reflectIndex C
for i := 0; i < numIn; i++ {
paramdv := (*C.DataValue)(unsafe.Pointer(uintptr(unsafe.Pointer(args)) + (uintptr(i)+1)*dataValueSize))
param := reflect.ValueOf(unpackDataValue(paramdv, fold.engine))
argt := methodt.In(i)
if !param.IsValid() {
fmt.Printf("Warning: %s called with zero parameter\n", methodName)
param = reflect.Zero(argt)
} else if param.Type() != argt {
if argt := methodt.In(i); param.Type() != argt {
param, err = convertParam(methodName, i, param, argt)
if err != nil {
panic(err.Error())
Expand Down Expand Up @@ -713,12 +600,10 @@ func hookGoValuePaint(enginep unsafe.Pointer, foldr C.GoRef, reflectIndex C.intp
return
}

obj := CommonOf(fold.cvalue, fold.engine)
painter := qpainter.FromPtr(qpainterptr)

painter := &Painter{engine: fold.engine, obj: &Common{fold.cvalue, fold.engine}}
v := reflect.ValueOf(fold.gvalue)
method := v.Method(int(reflectIndex))
method.Call([]reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(painter)})
method.Call([]reflect.Value{reflect.ValueOf(painter)})
}

func ensureEngine(enginep unsafe.Pointer, foldr C.GoRef) *valueFold {
Expand Down Expand Up @@ -770,7 +655,7 @@ func _initGoType(fold *valueFold, schedulePaint bool) {
return
}
// TODO Would be good to preserve identity on the Go side. See unpackDataValue as well.
obj := CommonOf(fold.cvalue, fold.engine)
obj := &Common{engine: fold.engine, addr: fold.cvalue}
fold.init.Call([]reflect.Value{reflect.ValueOf(fold.gvalue), reflect.ValueOf(obj)})
fold.init = reflect.Value{}
if schedulePaint {
Expand Down
18 changes: 0 additions & 18 deletions cdata/cdata12.c

This file was deleted.

17 changes: 0 additions & 17 deletions cdata/cdata15_386.s

This file was deleted.

17 changes: 0 additions & 17 deletions cdata/cdata15_amd64.s

This file was deleted.

Loading

0 comments on commit 8460421

Please sign in to comment.