Skip to content

Commit a159017

Browse files
committed
Merge pull request #2 from ricochet1k/safer
Safer
2 parents 271f860 + 424704a commit a159017

File tree

6 files changed

+226
-38
lines changed

6 files changed

+226
-38
lines changed

bridge.go

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ import (
2323
)
2424

2525
var (
26-
guiFunc = make(chan func())
27-
guiDone = make(chan struct{})
28-
guiLock = 0
29-
guiMainRef uintptr
30-
guiPaintRef uintptr
31-
guiIdleRun int32
26+
guiFunc = make(chan func())
27+
guiDone = make(chan struct{})
28+
guiLock = 0
29+
guiMainRef uintptr
30+
guiPaintRef uintptr
31+
guiIdleRun int32
3232

3333
initialized int32
3434
)
@@ -187,6 +187,7 @@ func hookIdleTimer() {
187187
return
188188
}
189189
}
190+
// fmt.Fprintf(os.Stderr, "hookIdleTimer: %v\n", runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name())
190191
f()
191192
guiDone <- struct{}{}
192193
atomic.AddInt32(&guiIdleRun, -1)
@@ -230,10 +231,17 @@ func wrapGoValue(engine *Engine, gvalue interface{}, owner valueOwner) (cvalue u
230231

231232
painting := cdata.Ref() == atomic.LoadUintptr(&guiPaintRef)
232233

234+
hashableGvalue := gvalue
235+
if gvaluev.Kind() == reflect.Slice {
236+
hashableGvalue = *(*reflect.SliceHeader)(unsafe.Pointer(gvaluev.Pointer()))
237+
} else if !hashable(gvalue) {
238+
panic(fmt.Sprintf("gvalue not hashable: %v %v", gvaluev.Type(), gvaluev.Kind()))
239+
}
240+
233241
// Cannot reuse a jsOwner because the QML runtime may choose to destroy
234242
// the value _after_ we hand it a new reference to the same value.
235243
// See issue #68 for details.
236-
prev, ok := engine.values[gvalue]
244+
prev, ok := engine.values[hashableGvalue]
237245
if ok && (prev.owner == cppOwner || painting) {
238246
return prev.cvalue
239247
}
@@ -257,7 +265,7 @@ func wrapGoValue(engine *Engine, gvalue interface{}, owner valueOwner) (cvalue u
257265
fold.next = prev
258266
prev.prev = fold
259267
}
260-
engine.values[gvalue] = fold
268+
engine.values[hashableGvalue] = fold
261269

262270
//fmt.Printf("[DEBUG] value alive (wrapped): cvalue=%x gvalue=%x/%#v\n", fold.cvalue, addrOf(fold.gvalue), fold.gvalue)
263271
stats.valuesAlive(+1)
@@ -356,7 +364,6 @@ func deref(value reflect.Value) reflect.Value {
356364
}
357365
return value
358366
}
359-
panic("cannot happen")
360367
}
361368

362369
//export hookGoValueReadField
@@ -367,7 +374,18 @@ func hookGoValueReadField(enginep, foldp unsafe.Pointer, reflectIndex, getIndex,
367374
if getIndex >= 0 {
368375
field = reflect.ValueOf(fold.gvalue).Method(int(getIndex)).Call(nil)[0]
369376
} else {
370-
field = deref(reflect.ValueOf(fold.gvalue)).Field(int(reflectIndex))
377+
val := deref(reflect.ValueOf(fold.gvalue))
378+
// defer func() {
379+
// if r := recover(); r != nil {
380+
// fmt.Fprintf(os.Stderr, "panic in hookGoValueReadField %v %v\n", val, reflectIndex)
381+
// }
382+
// }()
383+
if !val.IsValid() {
384+
// panic(fmt.Sprintf("invalid value in hookGoValueReadField %#v\n", fold))
385+
resultdv.dataType = C.DTInvalid
386+
return
387+
}
388+
field = val.Field(int(reflectIndex))
371389
}
372390
field = deref(field)
373391

@@ -503,7 +521,11 @@ func hookGoValueCallMethod(enginep, foldp unsafe.Pointer, reflectIndex C.int, ar
503521
for i := 0; i < numIn; i++ {
504522
paramdv := (*C.DataValue)(unsafe.Pointer(uintptr(unsafe.Pointer(args)) + (uintptr(i)+1)*dataValueSize))
505523
param := reflect.ValueOf(unpackDataValue(paramdv, fold.engine))
506-
if argt := methodt.In(i); param.Type() != argt {
524+
argt := methodt.In(i)
525+
if !param.IsValid() {
526+
fmt.Printf("Warning: %s called with zero parameter\n", methodName)
527+
param = reflect.Zero(argt)
528+
} else if param.Type() != argt {
507529
param, err = convertParam(methodName, i, param, argt)
508530
if err != nil {
509531
panic(err.Error())
@@ -562,7 +584,7 @@ func hookGoValuePaint(enginep, foldp unsafe.Pointer, reflectIndex C.intptr_t) {
562584
return
563585
}
564586

565-
painter := &Painter{engine: fold.engine, obj: &Common{fold.cvalue, fold.engine}}
587+
painter := &Painter{engine: fold.engine, obj: CommonOf(fold.cvalue, fold.engine)}
566588
v := reflect.ValueOf(fold.gvalue)
567589
method := v.Method(int(reflectIndex))
568590
method.Call([]reflect.Value{reflect.ValueOf(painter)})
@@ -617,7 +639,7 @@ func _initGoType(fold *valueFold, schedulePaint bool) {
617639
return
618640
}
619641
// TODO Would be good to preserve identity on the Go side. See unpackDataValue as well.
620-
obj := &Common{engine: fold.engine, addr: fold.cvalue}
642+
obj := CommonOf(fold.cvalue, fold.engine)
621643
fold.init.Call([]reflect.Value{reflect.ValueOf(fold.gvalue), reflect.ValueOf(obj)})
622644
fold.init = reflect.Value{}
623645
if schedulePaint {

cpp/capi.cpp

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,58 @@ void engineSetOwnershipJS(QQmlEngine_ *engine, QObject_ *object)
113113
qengine->setObjectOwnership(qobject, QQmlEngine::JavaScriptOwnership);
114114
}
115115

116+
void engineClearImportPaths(QQmlEngine_ *engine)
117+
{
118+
QQmlEngine *qengine = reinterpret_cast<QQmlEngine *>(engine);
119+
120+
QStringList empty;
121+
122+
qengine->setImportPathList(empty);
123+
}
124+
125+
void engineAddImportPath(QQmlEngine_ *engine, const char *path, int pathLen)
126+
{
127+
QQmlEngine *qengine = reinterpret_cast<QQmlEngine *>(engine);
128+
129+
QByteArray qimportPath(path, pathLen);
130+
QString qsimportPath = QString::fromUtf8(qimportPath);
131+
132+
qengine->addImportPath(qsimportPath);
133+
}
134+
135+
void engineClearPluginPaths(QQmlEngine_ *engine)
136+
{
137+
QQmlEngine *qengine = reinterpret_cast<QQmlEngine *>(engine);
138+
139+
QStringList empty;
140+
141+
qengine->setPluginPathList(empty);
142+
}
143+
144+
void engineAddPluginPath(QQmlEngine_ *engine, const char *path, int pathLen)
145+
{
146+
QQmlEngine *qengine = reinterpret_cast<QQmlEngine *>(engine);
147+
148+
QByteArray qpluginPath(path, pathLen);
149+
QString qspluginPath = QString::fromUtf8(qpluginPath);
150+
151+
qengine->addPluginPath(qspluginPath);
152+
}
153+
154+
void engineClearComponentCache(QQmlEngine_ *engine)
155+
{
156+
QQmlEngine *qengine = reinterpret_cast<QQmlEngine *>(engine);
157+
qengine->clearComponentCache();
158+
}
159+
160+
void coreAddLibraryPath(const char *path, int pathLen)
161+
{
162+
QByteArray qlibraryPath(path, pathLen);
163+
QString qslibraryPath = QString::fromUtf8(qlibraryPath);
164+
165+
QCoreApplication::addLibraryPath(qslibraryPath);
166+
}
167+
116168
QQmlComponent_ *newComponent(QQmlEngine_ *engine, QObject_ *parent)
117169
{
118170
QQmlEngine *qengine = reinterpret_cast<QQmlEngine *>(engine);
@@ -362,7 +414,7 @@ const char *objectTypeName(QObject_ *object)
362414
int objectGetProperty(QObject_ *object, const char *name, DataValue *result)
363415
{
364416
QObject *qobject = reinterpret_cast<QObject *>(object);
365-
417+
366418
QVariant var = qobject->property(name);
367419
packDataValue(&var, result);
368420

@@ -437,6 +489,10 @@ error *objectInvoke(QObject_ *object, const char *method, int methodLen, DataVal
437489
panicf("fix the parameter dispatching");
438490
}
439491

492+
if (qobject == 0) {
493+
return errorf("method called on null object: %s", method);
494+
}
495+
440496
const QMetaObject *metaObject = qobject->metaObject();
441497
// Walk backwards so descendants have priority.
442498
for (int i = metaObject->methodCount()-1; i >= 0; i--) {
@@ -452,7 +508,7 @@ error *objectInvoke(QObject_ *object, const char *method, int methodLen, DataVal
452508

453509
bool ok;
454510
if (metaMethod.returnType() == QMetaType::Void) {
455-
ok = metaMethod.invoke(qobject, Qt::DirectConnection,
511+
ok = metaMethod.invoke(qobject, Qt::DirectConnection,
456512
arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6], arg[7], arg[8], arg[9]);
457513
} else {
458514
ok = metaMethod.invoke(qobject, Qt::DirectConnection, Q_RETURN_ARG(QVariant, result),
@@ -475,7 +531,7 @@ void objectFindChild(QObject_ *object, QString_ *name, DataValue *resultdv)
475531
{
476532
QObject *qobject = reinterpret_cast<QObject *>(object);
477533
QString *qname = reinterpret_cast<QString *>(name);
478-
534+
479535
QVariant var;
480536
QObject *result = qobject->findChild<QObject *>(*qname);
481537
if (result) {
@@ -743,6 +799,15 @@ void packDataValue(QVariant_ *var, DataValue *value)
743799
*(DataValue**)(value->data) = dvlist;
744800
}
745801
break;
802+
case QMetaType::User:
803+
{
804+
static const int qjstype = QVariant::fromValue(QJSValue()).userType();
805+
if (qvar->userType() == qjstype) {
806+
auto var = qvar->value<QJSValue>().toVariant();
807+
packDataValue(&var, value);
808+
}
809+
}
810+
break;
746811
default:
747812
if (qvar->type() == (int)QMetaType::QObjectStar || qvar->canConvert<QObject *>()) {
748813
QObject *qobject = qvar->value<QObject *>();

cpp/capi.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ void engineSetOwnershipCPP(QQmlEngine_ *engine, QObject_ *object);
122122
void engineSetOwnershipJS(QQmlEngine_ *engine, QObject_ *object);
123123
void engineSetContextForObject(QQmlEngine_ *engine, QObject_ *object);
124124
void engineAddImageProvider(QQmlEngine_ *engine, QString_ *providerId, void *imageFunc);
125+
void engineClearImportPaths(QQmlEngine_ *engine);
126+
void engineAddImportPath(QQmlEngine_ *engine, const char *path, int pathLen);
127+
void engineClearPluginPaths(QQmlEngine_ *engine);
128+
void engineAddPluginPath(QQmlEngine_ *engine, const char *path, int pathLen);
129+
void engineClearComponentCache(QQmlEngine_ *engine);
130+
void coreAddLibraryPath(const char *path, int pathLen);
125131

126132
void contextGetProperty(QQmlContext_ *context, QString_ *name, DataValue *value);
127133
void contextSetProperty(QQmlContext_ *context, QString_ *name, DataValue *value);

datatype.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,7 @@ func unpackDataValue(dvalue *C.DataValue, engine *Engine) interface{} {
162162
return nil
163163
case C.DTObject:
164164
// TODO Would be good to preserve identity on the Go side. See initGoType as well.
165-
obj := &Common{
166-
engine: engine,
167-
addr: *(*unsafe.Pointer)(datap),
168-
}
165+
obj := CommonOf(*(*unsafe.Pointer)(datap), engine)
169166
if len(converters) > 0 {
170167
// TODO Embed the type name in DataValue to drop these calls.
171168
typeName := obj.TypeName()

0 commit comments

Comments
 (0)