cutego/qml/utils-qml.go
Rafał Zawadzki d7d974c3b9 Big merge
2023-01-23 15:12:32 +01:00

693 lines
21 KiB
Go

package qml
import (
"encoding/json"
"math"
"reflect"
"runtime"
"strconv"
"strings"
"sync"
"unsafe"
"github.com/bluszcz/cutego"
"github.com/bluszcz/cutego/core"
)
var (
Z_newEngineHelper func(p core.QObject_ITF) *core.QObject
finalizerMap = make(map[unsafe.Pointer][]*core.QObject)
finalizerMapMutex sync.Mutex
//needed only for interop --->
ReturnPointersAsStrings bool
SupportsSyncCallsIntoRemote = true
syncCallIntoRemoteChan = make(chan string, 0)
syncCallIntoRemoteReturnChan = make(chan string, 0)
fromJsToRefReturnsOnChan bool
fromJsToRefReturnChan = make(chan []reflect.Value, 0)
Z_wrapperFunctionReturnChan = make(chan *QJSValue, 0)
//needed only for interop <---
)
type ptr_itf interface {
Pointer() unsafe.Pointer
}
func Z_initEngine(engine QJSEngine_ITF) {
ptr := engine.QJSEngine_PTR()
ptr.QObject.ConnectDestroyed(func(ptr *core.QObject) {
finalizerMapMutex.Lock()
pointers := finalizerMap[ptr.Pointer()]
delete(finalizerMap, ptr.Pointer())
finalizerMapMutex.Unlock()
for _, o := range pointers {
o.DestroyQObject()
}
runtime.GC()
})
if !ptr.GlobalObject().Property("___engineHelper").IsUndefined() || Z_newEngineHelper == nil {
return
}
ptr.GlobalObject().SetProperty("nil", NewQJSValue(QJSValue__UndefinedValue))
helper := Z_newEngineHelper(engine)
ptr.GlobalObject().SetProperty("___engineHelper", ptr.NewQObject(helper))
ptr.GlobalObject().SetProperty("___engineHelperPointer", ptr.NewGoType(helper.Pointer()))
ptr.GlobalObject().SetProperty("___factory_single_func", ptr.Evaluate(`
(function(cn, fn) {
return function() {
var callArgs = [___engineHelperPointer, this.___pointer, cn, fn];
for (var i = 0; i < arguments.length; i++) { callArgs.push(arguments[i]) }
return ___engineHelper.wrapperFunction(callArgs);
};
});
`, "", 0))
ptr.GlobalObject().SetProperty("___factory_batch_funcs", ptr.Evaluate(`
(function(that, cn, fns) {
fns.forEach(function(fn) {
that[fn] = ___factory_single_func(cn, fn);
})
});
`, "", 0))
ptr.GlobalObject().SetProperty("___factory_batch_global_funcs", ptr.Evaluate(`
(function(that, pfx, fns) {
fns.forEach(function(fn) {
that[fn] = ___factory_single_func(pfx+"."+fn, "");
})
});
`, "", 0))
ptr.GlobalObject().SetProperty("___factory_batch_global_enums", ptr.Evaluate(`
(function(that, ens) {
ens.forEach(function(en) {
that[en[0]] = en[1];
})
});
`, "", 0))
ptr.GlobalObject().SetProperty("___connectDestroyed", ptr.Evaluate(`
(function(that) {
that.ConnectDestroyed(function(){ that.___pointer = nil })
});
`, "", 0))
prefE := make(map[string][][]interface{})
qt.EnumMapMutex.Lock()
for en, ev := range qt.EnumMap {
pfx := strings.Split(en, ".")
prefE[pfx[0]] = append(prefE[pfx[0]], []interface{}{pfx[1], ev})
}
qt.EnumMapMutex.Unlock()
for pfx, ens := range prefE {
var jsv *QJSValue
if m := ptr.GlobalObject().Property(pfx); m.IsUndefined() {
jsv = ptr.NewObject()
ptr.GlobalObject().SetProperty(pfx, jsv)
} else {
jsv = m
}
inp := ptr.NewGoType(ens)
ptr.GlobalObject().Property("___factory_batch_global_enums").Call([]*QJSValue{jsv, inp})
inp.DestroyQJSValue()
}
pref := make(map[string][]string)
qt.FuncMapMutex.Lock()
for fn := range qt.FuncMap {
pfx := strings.Split(fn, ".")
if len(pfx) == 2 { //TODO: re-register 3rdparty functions?
pref[pfx[0]] = append(pref[pfx[0]], pfx[1])
}
}
qt.FuncMapMutex.Unlock()
for pfx, fns := range pref {
var jsv *QJSValue
if m := ptr.GlobalObject().Property(pfx); m.IsUndefined() {
jsv = ptr.NewObject()
ptr.GlobalObject().SetProperty(pfx, jsv)
} else {
jsv = m
}
inp := ptr.NewGoType(fns)
ptr.GlobalObject().Property("___factory_batch_global_funcs").Call([]*QJSValue{jsv, NewQJSValue8(pfx), inp})
inp.DestroyQJSValue()
}
}
func Z_wrapperFunction(jsvals *QJSValue) *QJSValue {
var m reflect.Value
cn, fn := jsvals.Property2(2).ToString(), jsvals.Property2(3).ToString()
if fn == "" {
v, _ := qt.GetFuncMap(cn)
m = reflect.ValueOf(v)
} else {
var rt reflect.Type
if t, ok := qt.GetItfMap(cn + "_ITF"); ok {
rt = reflect.TypeOf(t)
} else {
t, _ = qt.GetItfMap(cn)
rt = reflect.TypeOf(t)
}
//TODO: use ptr_itf instead ?
//TODO: use *FromPointer instead
//TODO: make SetPointer work as backup, for classes that don't support *FromPointer -> needs manual registration like for *FromPointer
o := reflect.New(rt)
o.MethodByName("SetPointer").Call([]reflect.Value{reflect.ValueOf(unsafe.Pointer(uintptr(jsvals.Property2(1).ToVariant().ToULongLong(nil))))})
m = o.MethodByName(fn)
}
engine := QJSEngine_qjsEngine(core.NewQObjectFromPointer(unsafe.Pointer(uintptr(jsvals.Property2(0).ToVariant().ToULongLong(nil)))))
if fn == "NewGoType" || (fn == "NewQVariant1" && cn == "core.QVariant") {
input := make([]interface{}, jsvals.Property("length").ToInt()-4)
for i := 0; i < len(input); i++ {
in := jsvals.Property2(uint(4 + i))
switch in.ToVariant().Type() {
case core.QVariant__List:
var o []interface{}
engine.ToGoType(in, &o)
input[i] = o
case core.QVariant__Map:
var o map[string]interface{}
engine.ToGoType(in, &o)
input[i] = o
default:
input[i] = in.ToVariant().ToInterface()
}
}
if fn == "NewQVariant1" {
return engine.NewGoType(core.NewQVariant1(input[0]))
}
if cn != "qml.QJSEngine" {
refInput := make([]reflect.Value, len(input))
for i, v := range input {
if jsv := jsvals.Property2(uint(4 + i)); jsv.IsCallable() {
refInput[i] = reflect.ValueOf(func(i interface{}) interface{} { //TODO: remote needs to provide infos about the expected arguments (needed for the callbacks for Qml created in the remote)
return jsv.Call([]*QJSValue{engine.NewGoType(i)}).ToVariant().ToInterface()
})
} else {
refInput[i] = reflect.ValueOf(v)
}
}
return engine.NewGoType(m.Call(refInput)[0].Interface())
}
return engine.NewGoType(engine.NewGoType(input...))
} else {
input := make([]reflect.Value, m.Type().NumIn())
for i := 0; i < len(input); i++ {
input[i] = engine.fromJsToRef(m.Type().In(i), jsvals.Property2(uint(4+i)))
}
ret := m.Call(input)
//needed only for interop --->
if fromJsToRefReturnsOnChan {
fromJsToRefReturnsOnChan = false
go func() {
ret = <-fromJsToRefReturnChan
if len(ret) == 0 {
Z_wrapperFunctionReturnChan <- NewQJSValue(QJSValue__UndefinedValue)
return
}
rret := engine.NewGoType(ret[0].Interface())
if reflect.TypeOf(ret[0].Interface()).Implements(reflect.TypeOf((*core.QObject_ITF)(nil)).Elem()) { //TODO: check for destroyed signal instead, or simply override the destructor instead ?
if qt.ExistsSignal(ret[0].Interface().(ptr_itf).Pointer(), "destroyed") {
engine.GlobalObject().Property("___connectDestroyed").Call([]*QJSValue{rret}) //TODO: connect destroyed/destructor from go instead ?
}
}
Z_wrapperFunctionReturnChan <- rret
}()
return NewQJSValue(QJSValue__UndefinedValue)
}
//needed only for interop <---
if len(ret) == 0 {
return NewQJSValue(QJSValue__UndefinedValue)
}
rret := engine.NewGoType(ret[0].Interface())
if reflect.TypeOf(ret[0].Interface()).Implements(reflect.TypeOf((*core.QObject_ITF)(nil)).Elem()) { //TODO: check for destroyed signal instead, or simply override the destructor instead ?
if qt.ExistsSignal(ret[0].Interface().(ptr_itf).Pointer(), "destroyed") {
engine.GlobalObject().Property("___connectDestroyed").Call([]*QJSValue{rret}) //TODO: connect destroyed/destructor from go instead ?
}
}
return rret
}
}
func (ptr *QJSEngine) ToGoType(jsval *QJSValue, dst interface{}) {
out := ptr.fromJsToRef(reflect.TypeOf(dst), jsval)
if out.Type().Kind() == reflect.Ptr {
out = out.Elem()
}
reflect.ValueOf(dst).Elem().Set(out)
}
func (ptr *QJSEngine) fromJsToRef(tofi reflect.Type, jsval *QJSValue) reflect.Value {
tofiO := tofi
if tofi.Kind() == reflect.Ptr {
tofi = tofi.Elem()
}
t, ok := qt.GetItfMap(tofi.String() + "_ITF")
if ok {
tofi = reflect.TypeOf(t)
} else {
t, ok = qt.GetItfMap(tofi.String())
if ok {
tofi = reflect.TypeOf(t)
}
}
switch {
case tofi.Kind() == reflect.UnsafePointer:
return reflect.ValueOf(unsafe.Pointer(uintptr(jsval.ToVariant().ToULongLong(nil))))
case tofi.Kind() == reflect.Uintptr:
return reflect.ValueOf(uintptr(jsval.ToVariant().ToULongLong(nil)))
case tofi.Kind() == reflect.Func:
return reflect.MakeFunc(tofi, func(args []reflect.Value) []reflect.Value {
input := make([]*QJSValue, len(args))
for i, arg := range args {
input[i] = ptr.NewGoType(arg.Interface())
}
ret := jsval.Call(input)
//the default path to return on this function
if !(ret.IsObject() && ret.Property("___earlyReturn").ToString() == "true") {
if tofi.NumOut() != 0 {
return []reflect.Value{ptr.fromJsToRef(tofi.Out(0), ret)}
}
return nil
}
//TODO: some functions can't return on a chan such as core.QAbstractListModel::RowCount (when called i.e. by the core.QAbstractListModel::Index method); warn about these methods here if the remote lanuage is not supporting "SupportsSyncCallsIntoRemote" ?
//needed only for interop --->
fromJsToRefReturnsOnChan = true
syncCallIntoRemoteChan <- string(ret.Property("___data").ToString())
go func() {
var o interface{}
json.Unmarshal([]byte(<-syncCallIntoRemoteReturnChan), &o)
if tofi.NumOut() != 0 {
fromJsToRefReturnChan <- []reflect.Value{ptr.fromJsToRef(tofi.Out(0), ptr.ToScriptValue(core.NewQVariant1(o)))}
} else {
fromJsToRefReturnChan <- nil
}
}()
if tofi.NumOut() != 0 {
return []reflect.Value{reflect.Zero(tofi.Out(0))} //TODO: (SupportsSyncCallsIntoRemote related); possible to trigger panic's with something like []reflect.Value{reflect.ValueOf("___earlyReturn")}
}
return nil
//needed only for interop <---
})
//TODO: merge into (*core.QVariant).ToGoType ? >>>
case tofiO.ConvertibleTo(reflect.TypeOf(int8(0))):
switch tofi.Kind() {
case reflect.Float32, reflect.Float64:
return reflect.ValueOf(jsval.ToVariant().ToDouble(nil)).Convert(tofi)
}
return reflect.ValueOf(jsval.ToVariant().ToLongLong(nil)).Convert(tofi)
//<<<
case tofi.Kind() == reflect.Slice, tofi.Kind() == reflect.Array:
var rv reflect.Value
if ls := jsval.Property("length").ToInt(); tofi.Kind() == reflect.Slice {
rv = reflect.MakeSlice(tofi, ls, ls)
} else {
rv = reflect.New(tofi).Elem()
}
for is := 0; is < rv.Len(); is++ {
rv.Index(is).Set(ptr.fromJsToRef(tofi.Elem(), jsval.Property2(uint(is))))
}
return rv
case tofi.Kind() == reflect.Map:
rv := reflect.MakeMapWithSize(tofi, jsval.Property("length").ToInt())
for k, _ := range jsval.ToVariant().ToMap() { //TODO: get keys from js function instead ?
rv.SetMapIndex(reflect.ValueOf(k).Convert(tofi.Key()), ptr.fromJsToRef(tofi.Elem(), jsval.Property(k)))
}
return rv
case ok:
if tofi.String() == "qml.QJSValue" {
if className := jsval.Property("___className").ToString(); className != "qml.QJSValue" {
t, ok := qt.GetItfMap(className + "_ITF")
if ok {
tofi = reflect.TypeOf(t)
} else {
t, ok = qt.GetItfMap(className)
if ok {
tofi = reflect.TypeOf(t)
}
}
if ok {
rv := reflect.New(tofi)
rv.MethodByName("SetPointer").Call([]reflect.Value{reflect.ValueOf(unsafe.Pointer(uintptr(jsval.Property("___pointer").ToVariant().ToULongLong(nil))))})
return reflect.ValueOf(ptr.NewGoType(rv.Interface()))
}
}
}
if tofi.String() == "core.QVariant" && jsval.IsUndefined() {
return reflect.ValueOf(core.NewQVariant())
}
//TODO: use ptr_itf instead ?
//TODO: use *FromPointer instead
//TODO: make SetPointer work as backup, for classes that don't support *FromPointer -> needs manual registration like for *FromPointer
rv := reflect.New(tofi)
rv.MethodByName("SetPointer").Call([]reflect.Value{reflect.ValueOf(unsafe.Pointer(uintptr(jsval.Property("___pointer").ToVariant().ToULongLong(nil))))})
return rv
case tofi.Kind() == reflect.Struct && !tofiO.Implements(reflect.TypeOf((*ptr_itf)(nil)).Elem()): //TODO: support pure go types (i.e. types that don't support ptr_itf as well)
rv := reflect.New(tofi).Elem()
for k, _ := range jsval.ToVariant().ToMap() { //TODO: get keys from js function instead ?
val := jsval.Property(k)
realName := k
toInt := false
for id := 0; id < rv.NumField(); id++ {
if tag, ok := rv.Type().Field(id).Tag.Lookup("json"); ok {
switch {
case strings.Count(tag, ",") == 0:
if k == tag {
realName = rv.Type().Field(id).Name
}
default:
tags := strings.Split(tag, ",")
if k == tags[0] || (len(tags[0]) == 0 && k == rv.Type().Field(id).Name) {
realName = rv.Type().Field(id).Name
if tags[1] == "string" {
toInt = true
}
}
}
}
}
field := rv.FieldByName(realName)
if !field.IsValid() {
continue
}
if toInt {
field.Set(reflect.ValueOf(val.ToVariant().ToLongLong(nil)).Convert(field.Type())) //TODO: pure go type conversion
} else {
field.Set(ptr.fromJsToRef(field.Type(), val))
}
}
if tofiO.Kind() == reflect.Ptr {
return rv.Addr()
}
return rv
case jsval.IsCallable():
return reflect.ValueOf(jsval)
case jsval.IsUndefined(), jsval.IsNull():
return reflect.Zero(tofi)
}
ii := reflect.New(tofi)
jsval.ToVariant().ToGoType(ii.Interface()) //TODO: does ToGoType support QObject or ptr_itf types inside maps/slices/structs ?
if tofiO.Kind() == reflect.Ptr {
return reflect.ValueOf(ii.Interface())
}
return reflect.ValueOf(ii.Elem().Interface())
}
//TODO: NewQJSValue1
func (ptr *QJSEngine) NewJSType(property *QJSValue, name string, i *QJSValue) { //TODO: support for js functions
ptr.newGoType(property, name, i)
}
func (ptr *QJSEngine) NewGoType(i ...interface{}) *QJSValue {
return ptr.newGoType(nil, "", i...)
}
func (ptr *QJSEngine) newGoType(property *QJSValue, name string, i ...interface{}) *QJSValue {
rv := reflect.ValueOf(i[0])
if len(i) > 1 {
rv = reflect.ValueOf(i[1])
}
//TODO: merged into core.NewQVariant1; can probably be removed after testing >>>
switch rv.Kind() {
case reflect.UnsafePointer:
return ptr.ToScriptValue(core.NewQVariant1(uint64(uintptr(rv.Interface().(unsafe.Pointer)))))
case reflect.Uintptr:
return ptr.ToScriptValue(core.NewQVariant1(uint64(rv.Interface().(uintptr))))
}
if rv.Type().ConvertibleTo(reflect.TypeOf(int8(0))) {
if rv.Kind() == reflect.Int64 {
return ptr.ToScriptValue(core.NewQVariant1(rv.Convert(reflect.TypeOf(int64(0))).Interface()))
}
return ptr.ToScriptValue(core.NewQVariant1(rv.Interface()))
}
//<<<
switch rv.Kind() {
case reflect.Func:
var fn string
if len(i) > 1 {
fn = i[0].(string)
} else {
fn = runtime.FuncForPC(rv.Pointer()).Name()
if strings.Contains(fn, "/") {
fns := strings.Split(fn, "/")
fn = fns[len(fns)-1]
}
}
if _, ok := qt.GetFuncMap(fn); !ok {
qt.SetFuncMap(fn, rv.Interface())
}
return ptr.makeFuncWrapper(fn)
case reflect.Slice, reflect.Array:
jsv := ptr.NewArray(uint(rv.Len()))
for i := 0; i < rv.Len(); i++ {
inp := ptr.NewGoType(rv.Index(i).Interface())
jsv.SetProperty2(uint(i), inp)
inp.DestroyQJSValue()
}
return jsv
case reflect.Map:
jsv := ptr.NewObject()
for _, k := range rv.MapKeys() {
if key := core.NewQVariant1(k.Interface()); key.CanConvert(int(core.QMetaType__QString)) { //TODO: replace with pure go (int -> string) conversion
jsv.SetProperty(key.ToString(), ptr.NewGoType(rv.MapIndex(k).Interface()))
}
}
return jsv
case reflect.Struct:
jsv := ptr.NewObject()
for id := 0; id < rv.NumField(); id++ {
if key := core.NewQVariant1(rv.Type().Field(id).Name); key.CanConvert(int(core.QMetaType__QString)) { //TODO: replace with pure go (int -> string) conversion
field := rv.Field(id)
if !field.CanInterface() {
continue
}
if tag, ok := rv.Type().Field(id).Tag.Lookup("json"); ok {
switch {
case (strings.HasSuffix(tag, ",omitempty") && isZero(field)) || tag == "-":
case strings.Count(tag, ",") == 0:
jsv.SetProperty(tag, ptr.NewGoType(field.Interface()))
default:
tags := strings.Split(tag, ",")
name := rv.Type().Field(id).Name
if len(tags[0]) != 0 {
name = tags[0]
}
v := ptr.NewGoType(field.Interface())
if tags[1] == "string" {
v = ptr.NewGoType(v.ToString()) //TODO: pure go type conversion
}
jsv.SetProperty(name, v)
}
} else {
jsv.SetProperty(rv.Type().Field(id).Name, ptr.NewGoType(field.Interface()))
}
}
}
return jsv
}
var jsv *QJSValue
if reflect.TypeOf(i[0]).Implements(reflect.TypeOf((*ptr_itf)(nil)).Elem()) {
jsv = ptr.NewObject()
} else {
jsv = ptr.ToScriptValue(core.NewQVariant1(i[0]))
}
if rv.Type().String() == "*qml.QJSValue" {
className := rv.Interface().(*QJSValue).Property("___className").ToString()
if className != "qml.QJSValue" {
var tofi reflect.Type
t, ok := qt.GetItfMap(className + "_ITF")
if ok {
tofi = reflect.TypeOf(t)
} else {
t, ok = qt.GetItfMap(className)
if ok {
tofi = reflect.TypeOf(t)
}
}
if ok {
rvn := reflect.New(tofi)
rvn.MethodByName("SetPointer").Call([]reflect.Value{reflect.ValueOf(unsafe.Pointer(uintptr(rv.Interface().(*QJSValue).Property("___pointer").ToVariant().ToULongLong(nil))))})
ptr.makeObjectWrapper(rvn.Interface(), jsv)
}
}
}
if property == nil {
if reflect.TypeOf(i[0]).Implements(reflect.TypeOf((*ptr_itf)(nil)).Elem()) {
ptr.makeObjectWrapper(i[0], jsv)
}
} else {
property.SetProperty(name, jsv)
}
return jsv
}
func isZero(v reflect.Value) bool {
switch v.Kind() {
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return math.Float64bits(v.Float()) == 0
case reflect.Complex64, reflect.Complex128:
c := v.Complex()
return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0
case reflect.Array:
for i := 0; i < v.Len(); i++ {
if !isZero(v.Index(i)) {
return false
}
}
return true
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
return v.IsNil()
case reflect.String:
return v.Len() == 0
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
if !isZero(v.Field(i)) {
return false
}
}
return true
default:
// This should never happens, but will act as a safeguard for
// later, as a default value doesn't makes sense here.
panic(&reflect.ValueError{"reflect.Value.IsZero", v.Kind()})
}
}
func (ptr *QJSEngine) makeFuncWrapper(fn string) *QJSValue {
retV := ptr.GlobalObject().Property("___factory_single_func").Call([]*QJSValue{NewQJSValue8(fn), NewQJSValue8("")})
//only needed for generic binding >>>
retV.SetProperty("callable", ptr.NewGoType(true))
retV.SetProperty("callableLocal", ptr.NewGoType(true))
retV.SetProperty("callableName", ptr.NewGoType(fn))
//<<<
//TODO: allow creation of funcs in arbitrary module depth
if strings.Count(fn, ".") == 0 {
ptr.GlobalObject().SetProperty(fn, retV)
} else {
var jsv *QJSValue
if m := ptr.GlobalObject().Property(strings.Split(fn, ".")[0]); m.IsUndefined() {
jsv = ptr.NewObject()
ptr.GlobalObject().SetProperty(strings.Split(fn, ".")[0], jsv)
} else {
jsv = m
}
jsv.SetProperty(strings.Split(fn, ".")[1], retV)
}
return retV
}
func (ptr *QJSEngine) makeObjectWrapper(in interface{}, jsv *QJSValue) {
if ReturnPointersAsStrings {
jsv.SetProperty("___pointer", ptr.ToScriptValue(core.NewQVariant1(strconv.FormatUint(uint64(uintptr(in.(ptr_itf).Pointer())), 10)))) //TODO: can be shortened once NewQVariant1 supports unsafe.Pointer
} else {
jsv.SetProperty("___pointer", ptr.ToScriptValue(core.NewQVariant1(uint64(uintptr(in.(ptr_itf).Pointer()))))) //TODO: can be shortened once NewQVariant1 supports unsafe.Pointer
}
rv := reflect.ValueOf(in)
className := rv.Type().Elem().String()
jsv.SetProperty("___className", NewQJSValue8(className))
_, ok1 := qt.GetItfMap(className + "_ITF")
if _, ok2 := qt.GetItfMap(className); !(ok1 || ok2) {
qt.SetItfMap(className, rv.Elem().Interface())
}
/* TODO:
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
*/
fns := make([]string, rv.NumMethod())
for i := 0; i < rv.NumMethod(); i++ {
fns[i] = rv.Type().Method(i).Name
}
ptr.GlobalObject().Property("___factory_batch_funcs").Call([]*QJSValue{jsv, NewQJSValue8(className), ptr.ToScriptValue(core.NewQVariant1(fns))})
if qt.HasFinalizer(in) {
buoy := core.NewQObject(nil)
buoy.ConnectDestroyed(func(*core.QObject) { runtime.KeepAlive(in) })
jsv.SetProperty("___buoy", ptr.NewQObject(buoy))
finalizerMapMutex.Lock()
finalizerMap[ptr.Pointer()] = append(finalizerMap[ptr.Pointer()], buoy)
finalizerMapMutex.Unlock()
}
}