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/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
- /* For 64-bit integer types, use a float. When JS and V8 adopt
65
- * bigger sized integer types, start using those instead. */
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<Number> (arg->v_int64);
83
+ return v8::BigInt::New (Isolate::GetCurrent(), arg->v_int64);
68
84
  case GI_TYPE_TAG_UINT64:
69
- return New<Number> (arg->v_uint64);
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** pointer = (void**)((size_t)data + i * item_size);
353
- memcpy(&value, pointer, item_size);
408
+ void* element_ptr = (void*)((size_t)data + i * item_size);
354
409
 
355
- if (length == -1 && isZero(value, item_type_info))
356
- break;
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
- Nan::Set(array, i, GIArgumentToV8(item_type_info, &value));
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
- memcpy(pointer, &arg, element_size);
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, key_arg.v_pointer, GIArgumentToHashPointer (&value_arg, value_type_info));
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
- g_array_free ((GArray*)data, TRUE);
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, Nan::To<int64_t> (value).ToChecked());
1765
+ g_value_set_long (gvalue, V8ToInt64 (value));
1457
1766
  } else if (G_VALUE_HOLDS_ULONG (gvalue)) {
1458
- g_value_set_ulong (gvalue, Nan::To<uint32_t> (value).ToChecked());
1767
+ g_value_set_ulong (gvalue, V8ToUint64 (value));
1459
1768
  } else if (G_VALUE_HOLDS_INT64 (gvalue)) {
1460
- g_value_set_int64 (gvalue, Nan::To<int64_t> (value).ToChecked());
1769
+ g_value_set_int64 (gvalue, V8ToInt64 (value));
1461
1770
  } else if (G_VALUE_HOLDS_UINT64 (gvalue)) {
1462
- g_value_set_uint64 (gvalue, Nan::To<uint32_t> (value).ToChecked());
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, Nan::To<int64_t> (value).ToChecked());
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
- return New<Number>(g_value_get_long (gvalue));
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 New<Number>(g_value_get_ulong (gvalue));
1847
+ return v8::BigInt::NewFromUnsigned(Isolate::GetCurrent(), g_value_get_ulong (gvalue));
1527
1848
  } else if (G_VALUE_HOLDS_INT64 (gvalue)) {
1528
- return New<Number>(g_value_get_int64 (gvalue));
1849
+ return v8::BigInt::New(Isolate::GetCurrent(), g_value_get_int64 (gvalue));
1529
1850
  } else if (G_VALUE_HOLDS_UINT64 (gvalue)) {
1530
- return New<Number>(g_value_get_uint64 (gvalue));
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
- return New<Number>(g_value_get_gtype (gvalue));
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
- ERROR("%s", G_VALUE_TYPE_NAME(gvalue));
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);