node-gtk 1.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -190
- package/bin/node-gtk.js +31 -0
- package/lib/bootstrap.js +22 -4
- package/lib/index.js +25 -0
- package/lib/loop.js +34 -0
- package/lib/overrides/GLib-2.0.js +12 -9
- package/lib/overrides/Gio-2.0.js +26 -0
- package/lib/overrides/Gtk-3.0.js +17 -12
- package/lib/overrides/Gtk-4.0.js +4 -2
- package/lib/register-class.js +1 -1
- package/package.json +9 -2
- package/scripts/build-test-fixtures.js +237 -0
- package/scripts/ci.sh +5 -3
- package/src/boxed.cc +33 -3
- package/src/boxed.h +13 -0
- package/src/callback.cc +12 -0
- package/src/callback.h +1 -0
- package/src/closure.cc +110 -2
- package/src/function.cc +68 -7
- package/src/function.h +1 -0
- package/src/gi.cc +72 -0
- package/src/gobject.cc +148 -27
- package/src/loop.cc +39 -3
- package/src/loop.h +2 -0
- package/src/type.cc +3 -2
- package/src/value.cc +369 -31
- package/src/value.h +22 -0
- package/tools/README.md +91 -0
- package/tools/generate-types.js +1045 -0
- package/lib/binding/node-v102-linux-x64/node_gtk.node +0 -0
- package/lib/binding/node-v108-linux-x64/node_gtk.node +0 -0
- package/lib/binding/node-v115-linux-x64/node_gtk.node +0 -0
- package/lib/binding/node-v127-linux-x64/node_gtk.node +0 -0
- package/lib/binding/node-v93-linux-x64/node_gtk.node +0 -0
package/src/value.cc
CHANGED
|
@@ -35,6 +35,21 @@ static void HashPointerToGIArgument (GIArgument *arg, GITypeInfo *type_info);
|
|
|
35
35
|
|
|
36
36
|
static bool IsUint8Array (GITypeInfo *type_info);
|
|
37
37
|
|
|
38
|
+
/* Accept both Number and BigInt for 64-bit / platform-dependent integer
|
|
39
|
+
* holders. Reading those types yields a BigInt (#323, #149), so a value
|
|
40
|
+
* round-tripped through JS arrives back here as a BigInt. */
|
|
41
|
+
static gint64 V8ToInt64 (Local<Value> value) {
|
|
42
|
+
if (value->IsBigInt())
|
|
43
|
+
return value.As<v8::BigInt>()->Int64Value();
|
|
44
|
+
return Nan::To<int64_t> (value).ToChecked();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static guint64 V8ToUint64 (Local<Value> value) {
|
|
48
|
+
if (value->IsBigInt())
|
|
49
|
+
return value.As<v8::BigInt>()->Uint64Value();
|
|
50
|
+
return (guint64) Nan::To<int64_t> (value).ToChecked();
|
|
51
|
+
}
|
|
52
|
+
|
|
38
53
|
|
|
39
54
|
Local<Value> GIArgumentToV8(GITypeInfo *type_info, GIArgument *arg, long length, ResourceOwnership ownership) {
|
|
40
55
|
GITypeTag type_tag = g_type_info_get_tag (type_info);
|
|
@@ -61,12 +76,13 @@ Local<Value> GIArgumentToV8(GITypeInfo *type_info, GIArgument *arg, long length,
|
|
|
61
76
|
case GI_TYPE_TAG_DOUBLE:
|
|
62
77
|
return New<Number> (arg->v_double);
|
|
63
78
|
|
|
64
|
-
/*
|
|
65
|
-
*
|
|
79
|
+
/* 64-bit integers are returned as BigInt so values above
|
|
80
|
+
* Number.MAX_SAFE_INTEGER keep full precision (#323, #149). The IN side
|
|
81
|
+
* accepts both Number and BigInt. */
|
|
66
82
|
case GI_TYPE_TAG_INT64:
|
|
67
|
-
return New
|
|
83
|
+
return v8::BigInt::New (Isolate::GetCurrent(), arg->v_int64);
|
|
68
84
|
case GI_TYPE_TAG_UINT64:
|
|
69
|
-
return
|
|
85
|
+
return v8::BigInt::NewFromUnsigned (Isolate::GetCurrent(), arg->v_uint64);
|
|
70
86
|
|
|
71
87
|
case GI_TYPE_TAG_GTYPE: /* c++: gsize */
|
|
72
88
|
return v8::BigInt::NewFromUnsigned(Isolate::GetCurrent(), arg->v_size);
|
|
@@ -212,6 +228,18 @@ Local<Value> GHashToV8 (GITypeInfo *type_info, GHashTable *hash_table) {
|
|
|
212
228
|
|
|
213
229
|
Local<Object> object = New<Object>();
|
|
214
230
|
|
|
231
|
+
// A NULL table marshals to an empty object. This notably happens for
|
|
232
|
+
// *_out_uninitialized functions, which return FALSE and leave their (out)
|
|
233
|
+
// pointer untouched (i.e. NULL, as the slot is zero-initialized). The
|
|
234
|
+
// GList/GSList/C-array converters already tolerate NULL; without this
|
|
235
|
+
// guard g_hash_table_iter_init asserts and the iterator is left
|
|
236
|
+
// uninitialized (#400).
|
|
237
|
+
if (hash_table == NULL) {
|
|
238
|
+
g_base_info_unref(key_info);
|
|
239
|
+
g_base_info_unref(value_info);
|
|
240
|
+
return object;
|
|
241
|
+
}
|
|
242
|
+
|
|
215
243
|
GHashTableIter iter;
|
|
216
244
|
GIArgument key_arg;
|
|
217
245
|
GIArgument value_arg;
|
|
@@ -287,6 +315,14 @@ static bool isZero(GIArgument &value, GITypeInfo *type_info) {
|
|
|
287
315
|
}
|
|
288
316
|
}
|
|
289
317
|
|
|
318
|
+
static bool IsZeroMemory (const void *ptr, gsize size) {
|
|
319
|
+
const guint8 *bytes = (const guint8 *) ptr;
|
|
320
|
+
for (gsize i = 0; i < size; i++)
|
|
321
|
+
if (bytes[i] != 0)
|
|
322
|
+
return false;
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
|
|
290
326
|
Local<Value> ArrayToV8 (GITypeInfo *type_info, void* data, long length) {
|
|
291
327
|
|
|
292
328
|
auto array = New<Array>();
|
|
@@ -299,6 +335,26 @@ Local<Value> ArrayToV8 (GITypeInfo *type_info, void* data, long length) {
|
|
|
299
335
|
auto item_size = GetTypeSize (item_type_info);
|
|
300
336
|
// auto item_transfer = transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer;
|
|
301
337
|
|
|
338
|
+
// Composite values (a struct/union/boxed passed by value, e.g. an array of
|
|
339
|
+
// GValue) are stored inline and may be larger than a GIArgument, so they
|
|
340
|
+
// must not be memcpy'd into one — that overflows the stack (#398). Detect
|
|
341
|
+
// them up front so the fill loop can read them in place instead.
|
|
342
|
+
bool element_is_value_struct = false;
|
|
343
|
+
bool element_is_gvalue = false;
|
|
344
|
+
if (g_type_info_get_tag (item_type_info) == GI_TYPE_TAG_INTERFACE
|
|
345
|
+
&& !g_type_info_is_pointer (item_type_info)) {
|
|
346
|
+
GIBaseInfo *element_interface = g_type_info_get_interface (item_type_info);
|
|
347
|
+
GIInfoType element_interface_type = g_base_info_get_type (element_interface);
|
|
348
|
+
if (element_interface_type == GI_INFO_TYPE_STRUCT
|
|
349
|
+
|| element_interface_type == GI_INFO_TYPE_BOXED
|
|
350
|
+
|| element_interface_type == GI_INFO_TYPE_UNION) {
|
|
351
|
+
element_is_value_struct = true;
|
|
352
|
+
element_is_gvalue =
|
|
353
|
+
g_registered_type_info_get_g_type (element_interface) == G_TYPE_VALUE;
|
|
354
|
+
}
|
|
355
|
+
g_base_info_unref (element_interface);
|
|
356
|
+
}
|
|
357
|
+
|
|
302
358
|
switch (array_type) {
|
|
303
359
|
case GI_ARRAY_TYPE_C:
|
|
304
360
|
{
|
|
@@ -349,13 +405,27 @@ Local<Value> ArrayToV8 (GITypeInfo *type_info, void* data, long length) {
|
|
|
349
405
|
if (length != -1 && i >= length)
|
|
350
406
|
break;
|
|
351
407
|
|
|
352
|
-
void
|
|
353
|
-
memcpy(&value, pointer, item_size);
|
|
408
|
+
void* element_ptr = (void*)((size_t)data + i * item_size);
|
|
354
409
|
|
|
355
|
-
if (
|
|
356
|
-
|
|
410
|
+
if (element_is_value_struct) {
|
|
411
|
+
// A zero-terminated array of values ends on an all-zero element.
|
|
412
|
+
if (length == -1 && IsZeroMemory(element_ptr, item_size))
|
|
413
|
+
break;
|
|
414
|
+
|
|
415
|
+
if (element_is_gvalue) {
|
|
416
|
+
Nan::Set(array, i, GValueToV8((GValue*) element_ptr));
|
|
417
|
+
} else {
|
|
418
|
+
value.v_pointer = element_ptr;
|
|
419
|
+
Nan::Set(array, i, GIArgumentToV8(item_type_info, &value));
|
|
420
|
+
}
|
|
421
|
+
} else {
|
|
422
|
+
memcpy(&value, element_ptr, item_size);
|
|
423
|
+
|
|
424
|
+
if (length == -1 && isZero(value, item_type_info))
|
|
425
|
+
break;
|
|
357
426
|
|
|
358
|
-
|
|
427
|
+
Nan::Set(array, i, GIArgumentToV8(item_type_info, &value));
|
|
428
|
+
}
|
|
359
429
|
}
|
|
360
430
|
|
|
361
431
|
|
|
@@ -450,6 +520,36 @@ GArray * V8ToGArray(GITypeInfo *type_info, Local<Value> value) {
|
|
|
450
520
|
return g_array;
|
|
451
521
|
}
|
|
452
522
|
|
|
523
|
+
GPtrArray * V8ToGPtrArray(GITypeInfo *type_info, Local<Value> value) {
|
|
524
|
+
if (!value->IsArray()) {
|
|
525
|
+
Nan::ThrowTypeError("Expected an array.");
|
|
526
|
+
return NULL;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
auto array = Local<Array>::Cast (TO_OBJECT (value));
|
|
530
|
+
int length = array->Length();
|
|
531
|
+
|
|
532
|
+
GITypeInfo *element_info = g_type_info_get_param_type (type_info, 0);
|
|
533
|
+
GPtrArray *ptr_array = g_ptr_array_sized_new (length);
|
|
534
|
+
|
|
535
|
+
for (int i = 0; i < length; i++) {
|
|
536
|
+
auto element = Nan::Get(array, i).ToLocalChecked();
|
|
537
|
+
GIArgument arg;
|
|
538
|
+
|
|
539
|
+
// A GPtrArray always stores its elements as pointers, so each element
|
|
540
|
+
// is converted to its pointer representation (e.g. g_strdup'd string,
|
|
541
|
+
// GObject/boxed pointer) and added directly.
|
|
542
|
+
if (V8ToGIArgument(element_info, &arg, element, false)) {
|
|
543
|
+
g_ptr_array_add (ptr_array, arg.v_pointer);
|
|
544
|
+
} else {
|
|
545
|
+
g_warning("V8ToGPtrArray: couldnt convert value at index %i", i);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
g_base_info_unref (element_info);
|
|
550
|
+
return ptr_array;
|
|
551
|
+
}
|
|
552
|
+
|
|
453
553
|
static void *V8ArrayToCArray(GITypeInfo *type_info, Local<Value> value) {
|
|
454
554
|
auto array = Local<Array>::Cast (TO_OBJECT (value));
|
|
455
555
|
int length = array->Length();
|
|
@@ -458,6 +558,22 @@ static void *V8ArrayToCArray(GITypeInfo *type_info, Local<Value> value) {
|
|
|
458
558
|
GITypeInfo* element_info = g_type_info_get_param_type (type_info, 0);
|
|
459
559
|
gsize element_size = GetTypeSize(element_info);
|
|
460
560
|
|
|
561
|
+
// When the element is a struct/union/boxed passed by *value* (not a
|
|
562
|
+
// pointer), the C array stores the struct contents inline, so each
|
|
563
|
+
// element's bytes must be copied from the wrapped instance rather than
|
|
564
|
+
// copying the GIArgument (which only holds a pointer to it).
|
|
565
|
+
bool element_is_struct_by_value = false;
|
|
566
|
+
if (!g_type_info_is_pointer(element_info)
|
|
567
|
+
&& g_type_info_get_tag(element_info) == GI_TYPE_TAG_INTERFACE) {
|
|
568
|
+
GIBaseInfo* interface_info = g_type_info_get_interface(element_info);
|
|
569
|
+
GIInfoType interface_type = g_base_info_get_type(interface_info);
|
|
570
|
+
element_is_struct_by_value =
|
|
571
|
+
interface_type == GI_INFO_TYPE_STRUCT
|
|
572
|
+
|| interface_type == GI_INFO_TYPE_BOXED
|
|
573
|
+
|| interface_type == GI_INFO_TYPE_UNION;
|
|
574
|
+
g_base_info_unref(interface_info);
|
|
575
|
+
}
|
|
576
|
+
|
|
461
577
|
void *result = g_malloc0(element_size * (length + (isZeroTerminated ? 1 : 0)));
|
|
462
578
|
|
|
463
579
|
for (int i = 0; i < length; i++) {
|
|
@@ -467,7 +583,10 @@ static void *V8ArrayToCArray(GITypeInfo *type_info, Local<Value> value) {
|
|
|
467
583
|
|
|
468
584
|
if (V8ToGIArgument(element_info, &arg, value, true)) {
|
|
469
585
|
void* pointer = (void*)((size_t)result + i * element_size);
|
|
470
|
-
|
|
586
|
+
if (element_is_struct_by_value && arg.v_pointer != NULL)
|
|
587
|
+
memcpy(pointer, arg.v_pointer, element_size);
|
|
588
|
+
else
|
|
589
|
+
memcpy(pointer, &arg, element_size);
|
|
471
590
|
} else {
|
|
472
591
|
WARN("couldnt convert value: %s", *Nan::Utf8String(TO_STRING (value)));
|
|
473
592
|
}
|
|
@@ -607,10 +726,15 @@ gpointer V8ToGHash (GITypeInfo *type_info, Local<Value> value) {
|
|
|
607
726
|
case GI_TYPE_TAG_UINT16:
|
|
608
727
|
case GI_TYPE_TAG_INT32:
|
|
609
728
|
case GI_TYPE_TAG_UINT32:
|
|
610
|
-
hash_func = g_int_hash;
|
|
611
|
-
equal_func = g_int_equal;
|
|
612
|
-
break;
|
|
613
729
|
case GI_TYPE_TAG_GTYPE:
|
|
730
|
+
// The key is stored directly in the pointer via
|
|
731
|
+
// GINT_TO_POINTER/GSIZE_TO_POINTER (see GIArgumentToHashPointer),
|
|
732
|
+
// matching the GObject-introspection convention. g_int_hash and
|
|
733
|
+
// g_int64_hash dereference their argument as a pointer-to-integer,
|
|
734
|
+
// so they crash on these packed values — use g_direct_* instead.
|
|
735
|
+
hash_func = g_direct_hash;
|
|
736
|
+
equal_func = g_direct_equal;
|
|
737
|
+
break;
|
|
614
738
|
case GI_TYPE_TAG_INT64:
|
|
615
739
|
case GI_TYPE_TAG_UINT64:
|
|
616
740
|
hash_func = g_int64_hash;
|
|
@@ -665,7 +789,9 @@ gpointer V8ToGHash (GITypeInfo *type_info, Local<Value> value) {
|
|
|
665
789
|
goto item_error;
|
|
666
790
|
}
|
|
667
791
|
|
|
668
|
-
g_hash_table_insert (hash_table,
|
|
792
|
+
g_hash_table_insert (hash_table,
|
|
793
|
+
GIArgumentToHashPointer (&key_arg, key_type_info),
|
|
794
|
+
GIArgumentToHashPointer (&value_arg, value_type_info));
|
|
669
795
|
|
|
670
796
|
continue;
|
|
671
797
|
|
|
@@ -818,6 +944,8 @@ bool V8ToGIArgument(GITypeInfo *type_info, GIArgument *arg, Local<Value> value,
|
|
|
818
944
|
arg->v_pointer = V8ToGArray(type_info, value);
|
|
819
945
|
break;
|
|
820
946
|
case GI_ARRAY_TYPE_PTR_ARRAY:
|
|
947
|
+
arg->v_pointer = V8ToGPtrArray(type_info, value);
|
|
948
|
+
break;
|
|
821
949
|
default:
|
|
822
950
|
printf("%s", Util::ArrayTypeToString(array_type));
|
|
823
951
|
g_assert_not_reached ();
|
|
@@ -1356,9 +1484,15 @@ void FreeGIArgumentArray(GITypeInfo *type_info, GIArgument *arg, GITransfer tran
|
|
|
1356
1484
|
}
|
|
1357
1485
|
case GI_ARRAY_TYPE_ARRAY:
|
|
1358
1486
|
case GI_ARRAY_TYPE_BYTE_ARRAY:
|
|
1487
|
+
{
|
|
1488
|
+
// Free the container struct, not `data`, which may have been
|
|
1489
|
+
// reassigned to the inner buffer in the free-elements block.
|
|
1490
|
+
g_array_free ((GArray*) arg->v_pointer, TRUE);
|
|
1491
|
+
break;
|
|
1492
|
+
}
|
|
1359
1493
|
case GI_ARRAY_TYPE_PTR_ARRAY:
|
|
1360
1494
|
{
|
|
1361
|
-
|
|
1495
|
+
g_ptr_array_free ((GPtrArray*) arg->v_pointer, TRUE);
|
|
1362
1496
|
break;
|
|
1363
1497
|
}
|
|
1364
1498
|
default:
|
|
@@ -1368,6 +1502,178 @@ void FreeGIArgumentArray(GITypeInfo *type_info, GIArgument *arg, GITransfer tran
|
|
|
1368
1502
|
}
|
|
1369
1503
|
}
|
|
1370
1504
|
|
|
1505
|
+
/*
|
|
1506
|
+
* Transfer-container IN cleanup for GList/GSList/GHashTable.
|
|
1507
|
+
*
|
|
1508
|
+
* With transfer-container, the callee takes ownership of (and frees) the
|
|
1509
|
+
* container structure but NOT its elements, which the caller still owns. By
|
|
1510
|
+
* the time node-gtk runs its post-call cleanup the container has already been
|
|
1511
|
+
* freed, so it can no longer be walked to reach the (owned) elements — doing
|
|
1512
|
+
* so corrupts the heap (#399). Instead we snapshot the element pointers into a
|
|
1513
|
+
* private structure *before* the call and free them from that snapshot after.
|
|
1514
|
+
*/
|
|
1515
|
+
|
|
1516
|
+
struct CapturedContainer {
|
|
1517
|
+
GList *list; // GLIST: copied node chain; GHASH: keys
|
|
1518
|
+
GSList *slist; // GSLIST: copied node chain
|
|
1519
|
+
GList *values; // GHASH: values
|
|
1520
|
+
};
|
|
1521
|
+
|
|
1522
|
+
bool IsTransferContainerInList (GITypeInfo *type_info, GITransfer transfer, GIDirection direction) {
|
|
1523
|
+
if (direction != GI_DIRECTION_IN || transfer != GI_TRANSFER_CONTAINER)
|
|
1524
|
+
return false;
|
|
1525
|
+
|
|
1526
|
+
switch (g_type_info_get_tag (type_info)) {
|
|
1527
|
+
case GI_TYPE_TAG_GLIST:
|
|
1528
|
+
case GI_TYPE_TAG_GSLIST:
|
|
1529
|
+
case GI_TYPE_TAG_GHASH:
|
|
1530
|
+
return true;
|
|
1531
|
+
default:
|
|
1532
|
+
return false;
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
gpointer CaptureTransferContainerElements (GITypeInfo *type_info, gpointer container) {
|
|
1537
|
+
if (container == NULL)
|
|
1538
|
+
return NULL;
|
|
1539
|
+
|
|
1540
|
+
CapturedContainer *captured = g_new0 (CapturedContainer, 1);
|
|
1541
|
+
|
|
1542
|
+
switch (g_type_info_get_tag (type_info)) {
|
|
1543
|
+
case GI_TYPE_TAG_GLIST:
|
|
1544
|
+
captured->list = g_list_copy ((GList *) container);
|
|
1545
|
+
break;
|
|
1546
|
+
case GI_TYPE_TAG_GSLIST:
|
|
1547
|
+
captured->slist = g_slist_copy ((GSList *) container);
|
|
1548
|
+
break;
|
|
1549
|
+
case GI_TYPE_TAG_GHASH:
|
|
1550
|
+
captured->list = g_hash_table_get_keys ((GHashTable *) container);
|
|
1551
|
+
captured->values = g_hash_table_get_values ((GHashTable *) container);
|
|
1552
|
+
break;
|
|
1553
|
+
default:
|
|
1554
|
+
break;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
return captured;
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
void FreeTransferContainerElements (GITypeInfo *type_info, gpointer captured_ptr) {
|
|
1561
|
+
if (captured_ptr == NULL)
|
|
1562
|
+
return;
|
|
1563
|
+
|
|
1564
|
+
CapturedContainer *captured = (CapturedContainer *) captured_ptr;
|
|
1565
|
+
GITypeTag type_tag = g_type_info_get_tag (type_info);
|
|
1566
|
+
GITypeInfo *key_info = g_type_info_get_param_type (type_info, 0);
|
|
1567
|
+
GIArgument element;
|
|
1568
|
+
|
|
1569
|
+
if (type_tag == GI_TYPE_TAG_GSLIST) {
|
|
1570
|
+
for (GSList *l = captured->slist; l != NULL; l = l->next) {
|
|
1571
|
+
element.v_pointer = l->data;
|
|
1572
|
+
FreeGIArgument (key_info, &element, GI_TRANSFER_EVERYTHING, GI_DIRECTION_OUT);
|
|
1573
|
+
}
|
|
1574
|
+
g_slist_free (captured->slist);
|
|
1575
|
+
} else {
|
|
1576
|
+
for (GList *l = captured->list; l != NULL; l = l->next) {
|
|
1577
|
+
element.v_pointer = l->data;
|
|
1578
|
+
FreeGIArgument (key_info, &element, GI_TRANSFER_EVERYTHING, GI_DIRECTION_OUT);
|
|
1579
|
+
}
|
|
1580
|
+
g_list_free (captured->list);
|
|
1581
|
+
}
|
|
1582
|
+
g_base_info_unref (key_info);
|
|
1583
|
+
|
|
1584
|
+
if (type_tag == GI_TYPE_TAG_GHASH) {
|
|
1585
|
+
GITypeInfo *value_info = g_type_info_get_param_type (type_info, 1);
|
|
1586
|
+
for (GList *l = captured->values; l != NULL; l = l->next) {
|
|
1587
|
+
element.v_pointer = l->data;
|
|
1588
|
+
FreeGIArgument (value_info, &element, GI_TRANSFER_EVERYTHING, GI_DIRECTION_OUT);
|
|
1589
|
+
}
|
|
1590
|
+
g_base_info_unref (value_info);
|
|
1591
|
+
g_list_free (captured->values);
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
g_free (captured);
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
// Returns the boxed GType of a struct/union/boxed interface type, or
|
|
1598
|
+
// G_TYPE_INVALID if `type_info` isn't a registered boxed type.
|
|
1599
|
+
static GType BoxedGTypeOf (GITypeInfo *type_info) {
|
|
1600
|
+
if (g_type_info_get_tag(type_info) != GI_TYPE_TAG_INTERFACE)
|
|
1601
|
+
return G_TYPE_INVALID;
|
|
1602
|
+
|
|
1603
|
+
GIBaseInfo *interface_info = g_type_info_get_interface(type_info);
|
|
1604
|
+
GIInfoType interface_type = g_base_info_get_type(interface_info);
|
|
1605
|
+
GType gtype = G_TYPE_INVALID;
|
|
1606
|
+
|
|
1607
|
+
if (interface_type == GI_INFO_TYPE_BOXED
|
|
1608
|
+
|| interface_type == GI_INFO_TYPE_STRUCT
|
|
1609
|
+
|| interface_type == GI_INFO_TYPE_UNION) {
|
|
1610
|
+
GType registered = g_registered_type_info_get_g_type(interface_info);
|
|
1611
|
+
if (G_TYPE_IS_BOXED(registered))
|
|
1612
|
+
gtype = registered;
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
g_base_info_unref(interface_info);
|
|
1616
|
+
return gtype;
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
void CopyBoxedForTransferFullIn (GITypeInfo *type_info, GIArgument *arg, long length) {
|
|
1620
|
+
if (arg->v_pointer == NULL)
|
|
1621
|
+
return;
|
|
1622
|
+
|
|
1623
|
+
GITypeTag type_tag = g_type_info_get_tag(type_info);
|
|
1624
|
+
|
|
1625
|
+
// A single boxed argument passed by value/pointer.
|
|
1626
|
+
if (type_tag == GI_TYPE_TAG_INTERFACE) {
|
|
1627
|
+
GType gtype = BoxedGTypeOf(type_info);
|
|
1628
|
+
if (gtype != G_TYPE_INVALID)
|
|
1629
|
+
arg->v_pointer = g_boxed_copy(gtype, arg->v_pointer);
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
// A C array of boxed pointers (e.g. GIMarshallingTestsBoxedStruct **).
|
|
1634
|
+
if (type_tag == GI_TYPE_TAG_ARRAY
|
|
1635
|
+
&& g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_C
|
|
1636
|
+
&& length >= 0) {
|
|
1637
|
+
GITypeInfo *element_info = g_type_info_get_param_type(type_info, 0);
|
|
1638
|
+
|
|
1639
|
+
if (g_type_info_is_pointer(element_info)) {
|
|
1640
|
+
GType gtype = BoxedGTypeOf(element_info);
|
|
1641
|
+
if (gtype != G_TYPE_INVALID) {
|
|
1642
|
+
gpointer *elements = (gpointer *) arg->v_pointer;
|
|
1643
|
+
for (long i = 0; i < length; i++)
|
|
1644
|
+
if (elements[i] != NULL)
|
|
1645
|
+
elements[i] = g_boxed_copy(gtype, elements[i]);
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
g_base_info_unref(element_info);
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
void RefObjectForTransferFullIn (GITypeInfo *type_info, GIArgument *arg) {
|
|
1654
|
+
if (arg->v_pointer == NULL)
|
|
1655
|
+
return;
|
|
1656
|
+
|
|
1657
|
+
if (g_type_info_get_tag(type_info) != GI_TYPE_TAG_INTERFACE)
|
|
1658
|
+
return;
|
|
1659
|
+
|
|
1660
|
+
GIBaseInfo *iface = g_type_info_get_interface(type_info);
|
|
1661
|
+
GIInfoType itype = g_base_info_get_type(iface);
|
|
1662
|
+
|
|
1663
|
+
// The callee owns one reference after the call (transfer-full). node-gtk
|
|
1664
|
+
// keeps only its toggle ref, so without this the callee would effectively
|
|
1665
|
+
// "own" the toggle ref: the refcount never accounts for the new owner, no
|
|
1666
|
+
// toggle-up fires, the wrapper stays weak, and once GC collects it the
|
|
1667
|
+
// destroy callback finalizes the object while the callee still uses it
|
|
1668
|
+
// (e.g. a controller passed to gtk_widget_add_controller). The G_IS_OBJECT
|
|
1669
|
+
// guard skips boxed/fundamental interface types (handled elsewhere).
|
|
1670
|
+
if ((itype == GI_INFO_TYPE_OBJECT || itype == GI_INFO_TYPE_INTERFACE)
|
|
1671
|
+
&& G_IS_OBJECT(arg->v_pointer))
|
|
1672
|
+
g_object_ref(arg->v_pointer);
|
|
1673
|
+
|
|
1674
|
+
g_base_info_unref(iface);
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1371
1677
|
|
|
1372
1678
|
/*
|
|
1373
1679
|
* GValue conversion functions
|
|
@@ -1389,19 +1695,19 @@ bool CanConvertV8ToGValue(GValue *gvalue, Local<Value> value) {
|
|
|
1389
1695
|
} else if (G_VALUE_HOLDS_UINT (gvalue)) {
|
|
1390
1696
|
return value->IsNumber();
|
|
1391
1697
|
} else if (G_VALUE_HOLDS_LONG (gvalue)) {
|
|
1392
|
-
return value->IsNumber();
|
|
1698
|
+
return value->IsNumber() || value->IsBigInt();
|
|
1393
1699
|
} else if (G_VALUE_HOLDS_ULONG (gvalue)) {
|
|
1394
|
-
return value->IsNumber();
|
|
1700
|
+
return value->IsNumber() || value->IsBigInt();
|
|
1395
1701
|
} else if (G_VALUE_HOLDS_INT64 (gvalue)) {
|
|
1396
|
-
return value->IsNumber();
|
|
1702
|
+
return value->IsNumber() || value->IsBigInt();
|
|
1397
1703
|
} else if (G_VALUE_HOLDS_UINT64 (gvalue)) {
|
|
1398
|
-
return value->IsNumber();
|
|
1704
|
+
return value->IsNumber() || value->IsBigInt();
|
|
1399
1705
|
} else if (G_VALUE_HOLDS_FLOAT (gvalue)) {
|
|
1400
1706
|
return value->IsNumber();
|
|
1401
1707
|
} else if (G_VALUE_HOLDS_DOUBLE (gvalue)) {
|
|
1402
1708
|
return value->IsNumber();
|
|
1403
1709
|
} else if (G_VALUE_HOLDS_GTYPE (gvalue)) {
|
|
1404
|
-
return value->IsNumber();
|
|
1710
|
+
return value->IsNumber() || value->IsBigInt();
|
|
1405
1711
|
} else if (G_VALUE_HOLDS_ENUM (gvalue)) {
|
|
1406
1712
|
return value->IsNumber();
|
|
1407
1713
|
} else if (G_VALUE_HOLDS_FLAGS (gvalue)) {
|
|
@@ -1410,6 +1716,9 @@ bool CanConvertV8ToGValue(GValue *gvalue, Local<Value> value) {
|
|
|
1410
1716
|
return value->IsString();
|
|
1411
1717
|
} else if (G_VALUE_HOLDS_OBJECT (gvalue)) {
|
|
1412
1718
|
return ValueIsInstanceOfGType(value, G_VALUE_TYPE (gvalue));
|
|
1719
|
+
} else if (G_VALUE_HOLDS (gvalue, G_TYPE_STRV)) {
|
|
1720
|
+
// GStrv is a boxed type, so this must come before G_VALUE_HOLDS_BOXED.
|
|
1721
|
+
return value->IsArray();
|
|
1413
1722
|
} else if (G_VALUE_HOLDS_BOXED (gvalue)) {
|
|
1414
1723
|
return ValueIsInstanceOfGType(value, G_VALUE_TYPE (gvalue));
|
|
1415
1724
|
} else if (G_VALUE_HOLDS_PARAM (gvalue)) {
|
|
@@ -1453,19 +1762,19 @@ bool V8ToGValue(GValue *gvalue, Local<Value> value, ResourceOwnership ownership)
|
|
|
1453
1762
|
} else if (G_VALUE_HOLDS_UINT (gvalue)) {
|
|
1454
1763
|
g_value_set_uint (gvalue, Nan::To<uint32_t> (value).ToChecked());
|
|
1455
1764
|
} else if (G_VALUE_HOLDS_LONG (gvalue)) {
|
|
1456
|
-
g_value_set_long (gvalue,
|
|
1765
|
+
g_value_set_long (gvalue, V8ToInt64 (value));
|
|
1457
1766
|
} else if (G_VALUE_HOLDS_ULONG (gvalue)) {
|
|
1458
|
-
g_value_set_ulong (gvalue,
|
|
1767
|
+
g_value_set_ulong (gvalue, V8ToUint64 (value));
|
|
1459
1768
|
} else if (G_VALUE_HOLDS_INT64 (gvalue)) {
|
|
1460
|
-
g_value_set_int64 (gvalue,
|
|
1769
|
+
g_value_set_int64 (gvalue, V8ToInt64 (value));
|
|
1461
1770
|
} else if (G_VALUE_HOLDS_UINT64 (gvalue)) {
|
|
1462
|
-
g_value_set_uint64 (gvalue,
|
|
1771
|
+
g_value_set_uint64 (gvalue, V8ToUint64 (value));
|
|
1463
1772
|
} else if (G_VALUE_HOLDS_FLOAT (gvalue)) {
|
|
1464
1773
|
g_value_set_float (gvalue, Nan::To<double> (value).ToChecked());
|
|
1465
1774
|
} else if (G_VALUE_HOLDS_DOUBLE (gvalue)) {
|
|
1466
1775
|
g_value_set_double (gvalue, Nan::To<double> (value).ToChecked());
|
|
1467
1776
|
} else if (G_VALUE_HOLDS_GTYPE (gvalue)) {
|
|
1468
|
-
g_value_set_gtype (gvalue,
|
|
1777
|
+
g_value_set_gtype (gvalue, V8ToUint64 (value));
|
|
1469
1778
|
} else if (G_VALUE_HOLDS_ENUM (gvalue)) {
|
|
1470
1779
|
g_value_set_enum (gvalue, Nan::To<int32_t> (value).ToChecked());
|
|
1471
1780
|
} else if (G_VALUE_HOLDS_FLAGS (gvalue)) {
|
|
@@ -1482,6 +1791,16 @@ bool V8ToGValue(GValue *gvalue, Local<Value> value, ResourceOwnership ownership)
|
|
|
1482
1791
|
return false;
|
|
1483
1792
|
}
|
|
1484
1793
|
g_value_set_object (gvalue, GObjectFromWrapper (value));
|
|
1794
|
+
} else if (G_VALUE_HOLDS (gvalue, G_TYPE_STRV)) {
|
|
1795
|
+
// GStrv is a boxed type, so this must come before G_VALUE_HOLDS_BOXED.
|
|
1796
|
+
Local<Array> array = Local<Array>::Cast(TO_OBJECT (value));
|
|
1797
|
+
int length = array->Length();
|
|
1798
|
+
char **strv = g_new0 (char *, length + 1);
|
|
1799
|
+
for (int i = 0; i < length; i++) {
|
|
1800
|
+
Nan::Utf8String element (Nan::Get(array, i).ToLocalChecked());
|
|
1801
|
+
strv[i] = g_strdup (*element);
|
|
1802
|
+
}
|
|
1803
|
+
g_value_take_boxed (gvalue, strv);
|
|
1485
1804
|
} else if (G_VALUE_HOLDS_BOXED (gvalue)) {
|
|
1486
1805
|
if (!ValueIsInstanceOfGType(value, G_VALUE_TYPE (gvalue))) {
|
|
1487
1806
|
Throw::CannotConvertGType("boxed", G_VALUE_TYPE (gvalue));
|
|
@@ -1521,19 +1840,23 @@ Local<Value> GValueToV8(const GValue *gvalue, ResourceOwnership ownership) {
|
|
|
1521
1840
|
} else if (G_VALUE_HOLDS_UINT (gvalue)) {
|
|
1522
1841
|
return New<v8::Uint32>(g_value_get_uint (gvalue));
|
|
1523
1842
|
} else if (G_VALUE_HOLDS_LONG (gvalue)) {
|
|
1524
|
-
|
|
1843
|
+
/* glong/gulong are platform-dependent (64-bit on LP64), so marshal as
|
|
1844
|
+
* BigInt for full precision and consistent behavior (#323, #149). */
|
|
1845
|
+
return v8::BigInt::New(Isolate::GetCurrent(), g_value_get_long (gvalue));
|
|
1525
1846
|
} else if (G_VALUE_HOLDS_ULONG (gvalue)) {
|
|
1526
|
-
return
|
|
1847
|
+
return v8::BigInt::NewFromUnsigned(Isolate::GetCurrent(), g_value_get_ulong (gvalue));
|
|
1527
1848
|
} else if (G_VALUE_HOLDS_INT64 (gvalue)) {
|
|
1528
|
-
return New
|
|
1849
|
+
return v8::BigInt::New(Isolate::GetCurrent(), g_value_get_int64 (gvalue));
|
|
1529
1850
|
} else if (G_VALUE_HOLDS_UINT64 (gvalue)) {
|
|
1530
|
-
return
|
|
1851
|
+
return v8::BigInt::NewFromUnsigned(Isolate::GetCurrent(), g_value_get_uint64 (gvalue));
|
|
1531
1852
|
} else if (G_VALUE_HOLDS_FLOAT (gvalue)) {
|
|
1532
1853
|
return New<Number>(g_value_get_float (gvalue));
|
|
1533
1854
|
} else if (G_VALUE_HOLDS_DOUBLE (gvalue)) {
|
|
1534
1855
|
return New<Number>(g_value_get_double (gvalue));
|
|
1535
1856
|
} else if (G_VALUE_HOLDS_GTYPE (gvalue)) {
|
|
1536
|
-
|
|
1857
|
+
// A GType is a gsize; marshal as BigInt to match the GIArgument path
|
|
1858
|
+
// and gi.getGType() (#323, #149).
|
|
1859
|
+
return v8::BigInt::NewFromUnsigned(Isolate::GetCurrent(), g_value_get_gtype (gvalue));
|
|
1537
1860
|
} else if (G_VALUE_HOLDS_ENUM (gvalue)) {
|
|
1538
1861
|
return New<Integer>(g_value_get_enum (gvalue));
|
|
1539
1862
|
} else if (G_VALUE_HOLDS_FLAGS (gvalue)) {
|
|
@@ -1548,6 +1871,15 @@ Local<Value> GValueToV8(const GValue *gvalue, ResourceOwnership ownership) {
|
|
|
1548
1871
|
return Nan::EmptyString();
|
|
1549
1872
|
} else if (G_VALUE_HOLDS_OBJECT (gvalue)) {
|
|
1550
1873
|
return WrapperFromGObject (G_OBJECT (g_value_get_object (gvalue)));
|
|
1874
|
+
} else if (G_VALUE_HOLDS (gvalue, G_TYPE_STRV)) {
|
|
1875
|
+
// GStrv is a boxed type, so this must come before G_VALUE_HOLDS_BOXED.
|
|
1876
|
+
char **strv = (char **) g_value_get_boxed (gvalue);
|
|
1877
|
+
Local<Array> array = Nan::New<Array>();
|
|
1878
|
+
if (strv != NULL) {
|
|
1879
|
+
for (guint i = 0; strv[i] != NULL; i++)
|
|
1880
|
+
Nan::Set(array, i, New<String>(strv[i]).ToLocalChecked());
|
|
1881
|
+
}
|
|
1882
|
+
return array;
|
|
1551
1883
|
} else if (G_VALUE_HOLDS_BOXED (gvalue)) {
|
|
1552
1884
|
GType gtype = G_VALUE_TYPE (gvalue);
|
|
1553
1885
|
GIBaseInfo *info = g_irepository_find_by_gtype(NULL, gtype);
|
|
@@ -1566,7 +1898,13 @@ Local<Value> GValueToV8(const GValue *gvalue, ResourceOwnership ownership) {
|
|
|
1566
1898
|
} else if (G_VALUE_HOLDS_VARIANT (gvalue)) {
|
|
1567
1899
|
ERROR("Unsuported type: variant");
|
|
1568
1900
|
} else {
|
|
1569
|
-
|
|
1901
|
+
// Don't abort the whole process on a GValue type we can't convert
|
|
1902
|
+
// (e.g. GStreamer's GstValueArray / GstValueList, #389). Warn and
|
|
1903
|
+
// return null so the caller stays alive.
|
|
1904
|
+
g_warning("GValueToV8: unhandled GValue type '%s'; returning null. "
|
|
1905
|
+
"Please report this at https://github.com/romgrk/node-gtk/issues",
|
|
1906
|
+
G_VALUE_TYPE_NAME (gvalue));
|
|
1907
|
+
return Nan::Null();
|
|
1570
1908
|
}
|
|
1571
1909
|
}
|
|
1572
1910
|
|
package/src/value.h
CHANGED
|
@@ -30,6 +30,28 @@ bool V8ToGIArgument (GITypeInfo *type_info, GIArgument *argument, Local<
|
|
|
30
30
|
bool V8ToOutGIArgument(GITypeInfo *type_info, GIArgument *arg, Local<Value> value, bool may_be_null);
|
|
31
31
|
void FreeGIArgument (GITypeInfo *type_info, GIArgument *argument, GITransfer transfer = GI_TRANSFER_EVERYTHING, GIDirection direction = GI_DIRECTION_OUT);
|
|
32
32
|
void FreeGIArgumentArray (GITypeInfo *type_info, GIArgument *arg, GITransfer transfer = GI_TRANSFER_EVERYTHING, GIDirection direction = GI_DIRECTION_OUT, long length = -1);
|
|
33
|
+
|
|
34
|
+
// For a transfer-container IN GList/GSList/GHashTable, the callee frees the
|
|
35
|
+
// container structure itself, so node-gtk cannot walk it afterwards to free
|
|
36
|
+
// the (caller-owned) elements. These capture the element pointers *before*
|
|
37
|
+
// the call so they can be freed *after* it. See #399.
|
|
38
|
+
bool IsTransferContainerInList (GITypeInfo *type_info, GITransfer transfer, GIDirection direction);
|
|
39
|
+
gpointer CaptureTransferContainerElements (GITypeInfo *type_info, gpointer container);
|
|
40
|
+
void FreeTransferContainerElements (GITypeInfo *type_info, gpointer captured);
|
|
41
|
+
|
|
42
|
+
// For a transfer-full IN boxed argument (or a C array of boxed pointers) the
|
|
43
|
+
// callee frees the passed memory, but the JS wrapper still owns its own copy,
|
|
44
|
+
// which would then be double-freed when the wrapper is finalized. Replace each
|
|
45
|
+
// such pointer with a g_boxed_copy so only the copy is handed to the callee.
|
|
46
|
+
// See #409.
|
|
47
|
+
void CopyBoxedForTransferFullIn (GITypeInfo *type_info, GIArgument *arg, long length);
|
|
48
|
+
|
|
49
|
+
// For a transfer-full IN GObject argument the callee takes ownership of one
|
|
50
|
+
// reference. node-gtk only holds a toggle ref on the wrapper, so add the
|
|
51
|
+
// reference the callee expects — otherwise the object is finalized out from
|
|
52
|
+
// under the callee once the wrapper is GC'd. See #439.
|
|
53
|
+
void RefObjectForTransferFullIn (GITypeInfo *type_info, GIArgument *arg);
|
|
54
|
+
|
|
33
55
|
bool CanConvertV8ToGIArgument (GITypeInfo *type_info, Local<Value> value, bool may_be_null);
|
|
34
56
|
|
|
35
57
|
bool V8ToGValue(GValue *gvalue, Local<Value> value, ResourceOwnership ownership = kNone);
|