node-gtk 0.14.0 → 2.0.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,8 +35,23 @@ 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
- Local<Value> GIArgumentToV8(GITypeInfo *type_info, GIArgument *arg, long length, bool mustCopy) {
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);
41
56
 
42
57
  switch (type_tag) {
@@ -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);
@@ -128,7 +144,7 @@ Local<Value> GIArgumentToV8(GITypeInfo *type_info, GIArgument *arg, long length,
128
144
  case GI_INFO_TYPE_BOXED:
129
145
  case GI_INFO_TYPE_STRUCT:
130
146
  case GI_INFO_TYPE_UNION:
131
- value = WrapperFromBoxed (interface_info, arg->v_pointer, mustCopy);
147
+ value = WrapperFromBoxed (interface_info, arg->v_pointer, ownership);
132
148
  break;
133
149
  case GI_INFO_TYPE_ENUM:
134
150
  value = New<Number>(arg->v_int);
@@ -162,7 +178,7 @@ Local<Value> GIArgumentToV8(GITypeInfo *type_info, GIArgument *arg, long length,
162
178
  return GHashToV8(type_info, (GHashTable *)arg->v_pointer);
163
179
 
164
180
  case GI_TYPE_TAG_ERROR:
165
- return GErrorToV8(type_info, (GError *)arg->v_pointer);
181
+ return GErrorToV8(type_info, (GError *)arg->v_pointer, ownership);
166
182
 
167
183
  default:
168
184
  ERROR("Unhandled tag type: %s", g_type_tag_to_string(type_tag));
@@ -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;
@@ -232,9 +260,9 @@ Local<Value> GHashToV8 (GITypeInfo *type_info, GHashTable *hash_table) {
232
260
  return object;
233
261
  }
234
262
 
235
- Local<Value> GErrorToV8 (GITypeInfo *type_info, GError *err) {
236
- auto err_info = g_irepository_find_by_name(NULL, "GLib", "Error");
237
- auto obj = WrapperFromBoxed (err_info, err, true);
263
+ Local<Value> GErrorToV8 (GITypeInfo *type_info, GError *err, ResourceOwnership ownership) {
264
+ auto err_info = g_irepository_find_by_gtype(NULL, g_error_get_type());
265
+ auto obj = WrapperFromBoxed (err_info, err, ownership);
238
266
  return obj;
239
267
  }
240
268
 
@@ -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;
357
414
 
358
- Nan::Set(array, i, GIArgumentToV8(item_type_info, &value));
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;
426
+
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
 
@@ -682,7 +808,7 @@ item_error:
682
808
  return hash_table;
683
809
  }
684
810
 
685
- bool V8ToGIArgument(GIBaseInfo *gi_info, GIArgument *arg, Local<Value> value) {
811
+ bool V8ToGIArgumentInterface(GIBaseInfo *gi_info, GIArgument *arg, Local<Value> value) {
686
812
  GIInfoType type = g_base_info_get_type (gi_info);
687
813
 
688
814
  memset(arg, 0, sizeof(GIArgument));
@@ -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 ();
@@ -828,7 +956,7 @@ bool V8ToGIArgument(GITypeInfo *type_info, GIArgument *arg, Local<Value> value,
828
956
  case GI_TYPE_TAG_INTERFACE:
829
957
  {
830
958
  GIBaseInfo *interface_info = g_type_info_get_interface (type_info);
831
- V8ToGIArgument (interface_info, arg, value);
959
+ V8ToGIArgumentInterface (interface_info, arg, value);
832
960
  g_base_info_unref(interface_info);
833
961
  }
834
962
  break;
@@ -1241,7 +1369,7 @@ void FreeGIArgument(GITypeInfo *type_info, GIArgument *arg, GITransfer transfer,
1241
1369
 
1242
1370
  case GI_TYPE_TAG_ERROR:
1243
1371
  {
1244
- g_error_free((GError *)arg->v_pointer);
1372
+ // no g_error_free as the ptr is taken directly from box
1245
1373
  break;
1246
1374
  }
1247
1375
 
@@ -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,154 @@ 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
+
1371
1653
 
1372
1654
  /*
1373
1655
  * GValue conversion functions
@@ -1389,19 +1671,19 @@ bool CanConvertV8ToGValue(GValue *gvalue, Local<Value> value) {
1389
1671
  } else if (G_VALUE_HOLDS_UINT (gvalue)) {
1390
1672
  return value->IsNumber();
1391
1673
  } else if (G_VALUE_HOLDS_LONG (gvalue)) {
1392
- return value->IsNumber();
1674
+ return value->IsNumber() || value->IsBigInt();
1393
1675
  } else if (G_VALUE_HOLDS_ULONG (gvalue)) {
1394
- return value->IsNumber();
1676
+ return value->IsNumber() || value->IsBigInt();
1395
1677
  } else if (G_VALUE_HOLDS_INT64 (gvalue)) {
1396
- return value->IsNumber();
1678
+ return value->IsNumber() || value->IsBigInt();
1397
1679
  } else if (G_VALUE_HOLDS_UINT64 (gvalue)) {
1398
- return value->IsNumber();
1680
+ return value->IsNumber() || value->IsBigInt();
1399
1681
  } else if (G_VALUE_HOLDS_FLOAT (gvalue)) {
1400
1682
  return value->IsNumber();
1401
1683
  } else if (G_VALUE_HOLDS_DOUBLE (gvalue)) {
1402
1684
  return value->IsNumber();
1403
1685
  } else if (G_VALUE_HOLDS_GTYPE (gvalue)) {
1404
- return value->IsNumber();
1686
+ return value->IsNumber() || value->IsBigInt();
1405
1687
  } else if (G_VALUE_HOLDS_ENUM (gvalue)) {
1406
1688
  return value->IsNumber();
1407
1689
  } else if (G_VALUE_HOLDS_FLAGS (gvalue)) {
@@ -1410,6 +1692,9 @@ bool CanConvertV8ToGValue(GValue *gvalue, Local<Value> value) {
1410
1692
  return value->IsString();
1411
1693
  } else if (G_VALUE_HOLDS_OBJECT (gvalue)) {
1412
1694
  return ValueIsInstanceOfGType(value, G_VALUE_TYPE (gvalue));
1695
+ } else if (G_VALUE_HOLDS (gvalue, G_TYPE_STRV)) {
1696
+ // GStrv is a boxed type, so this must come before G_VALUE_HOLDS_BOXED.
1697
+ return value->IsArray();
1413
1698
  } else if (G_VALUE_HOLDS_BOXED (gvalue)) {
1414
1699
  return ValueIsInstanceOfGType(value, G_VALUE_TYPE (gvalue));
1415
1700
  } else if (G_VALUE_HOLDS_PARAM (gvalue)) {
@@ -1424,7 +1709,7 @@ bool CanConvertV8ToGValue(GValue *gvalue, Local<Value> value) {
1424
1709
  G_VALUE_TYPE_NAME (gvalue));
1425
1710
  }
1426
1711
 
1427
- bool V8ToGValue(GValue *gvalue, Local<Value> value, bool mustCopy) {
1712
+ bool V8ToGValue(GValue *gvalue, Local<Value> value, ResourceOwnership ownership) {
1428
1713
  if (!CanConvertV8ToGValue(gvalue, value)) {
1429
1714
  auto maybeDetailString = Nan::ToDetailString(value);
1430
1715
  Nan::Utf8String utf8String(
@@ -1453,19 +1738,19 @@ bool V8ToGValue(GValue *gvalue, Local<Value> value, bool mustCopy) {
1453
1738
  } else if (G_VALUE_HOLDS_UINT (gvalue)) {
1454
1739
  g_value_set_uint (gvalue, Nan::To<uint32_t> (value).ToChecked());
1455
1740
  } else if (G_VALUE_HOLDS_LONG (gvalue)) {
1456
- g_value_set_long (gvalue, Nan::To<int64_t> (value).ToChecked());
1741
+ g_value_set_long (gvalue, V8ToInt64 (value));
1457
1742
  } else if (G_VALUE_HOLDS_ULONG (gvalue)) {
1458
- g_value_set_ulong (gvalue, Nan::To<uint32_t> (value).ToChecked());
1743
+ g_value_set_ulong (gvalue, V8ToUint64 (value));
1459
1744
  } else if (G_VALUE_HOLDS_INT64 (gvalue)) {
1460
- g_value_set_int64 (gvalue, Nan::To<int64_t> (value).ToChecked());
1745
+ g_value_set_int64 (gvalue, V8ToInt64 (value));
1461
1746
  } else if (G_VALUE_HOLDS_UINT64 (gvalue)) {
1462
- g_value_set_uint64 (gvalue, Nan::To<uint32_t> (value).ToChecked());
1747
+ g_value_set_uint64 (gvalue, V8ToUint64 (value));
1463
1748
  } else if (G_VALUE_HOLDS_FLOAT (gvalue)) {
1464
1749
  g_value_set_float (gvalue, Nan::To<double> (value).ToChecked());
1465
1750
  } else if (G_VALUE_HOLDS_DOUBLE (gvalue)) {
1466
1751
  g_value_set_double (gvalue, Nan::To<double> (value).ToChecked());
1467
1752
  } else if (G_VALUE_HOLDS_GTYPE (gvalue)) {
1468
- g_value_set_gtype (gvalue, Nan::To<int64_t> (value).ToChecked());
1753
+ g_value_set_gtype (gvalue, V8ToUint64 (value));
1469
1754
  } else if (G_VALUE_HOLDS_ENUM (gvalue)) {
1470
1755
  g_value_set_enum (gvalue, Nan::To<int32_t> (value).ToChecked());
1471
1756
  } else if (G_VALUE_HOLDS_FLAGS (gvalue)) {
@@ -1482,12 +1767,22 @@ bool V8ToGValue(GValue *gvalue, Local<Value> value, bool mustCopy) {
1482
1767
  return false;
1483
1768
  }
1484
1769
  g_value_set_object (gvalue, GObjectFromWrapper (value));
1770
+ } else if (G_VALUE_HOLDS (gvalue, G_TYPE_STRV)) {
1771
+ // GStrv is a boxed type, so this must come before G_VALUE_HOLDS_BOXED.
1772
+ Local<Array> array = Local<Array>::Cast(TO_OBJECT (value));
1773
+ int length = array->Length();
1774
+ char **strv = g_new0 (char *, length + 1);
1775
+ for (int i = 0; i < length; i++) {
1776
+ Nan::Utf8String element (Nan::Get(array, i).ToLocalChecked());
1777
+ strv[i] = g_strdup (*element);
1778
+ }
1779
+ g_value_take_boxed (gvalue, strv);
1485
1780
  } else if (G_VALUE_HOLDS_BOXED (gvalue)) {
1486
1781
  if (!ValueIsInstanceOfGType(value, G_VALUE_TYPE (gvalue))) {
1487
1782
  Throw::CannotConvertGType("boxed", G_VALUE_TYPE (gvalue));
1488
1783
  return false;
1489
1784
  }
1490
- if (mustCopy)
1785
+ if (ownership == kCopy)
1491
1786
  g_value_set_boxed (gvalue, PointerFromWrapper(value));
1492
1787
  else
1493
1788
  g_value_set_static_boxed (gvalue, PointerFromWrapper(value));
@@ -1508,7 +1803,7 @@ bool V8ToGValue(GValue *gvalue, Local<Value> value, bool mustCopy) {
1508
1803
  return true;
1509
1804
  }
1510
1805
 
1511
- Local<Value> GValueToV8(const GValue *gvalue, bool mustCopy) {
1806
+ Local<Value> GValueToV8(const GValue *gvalue, ResourceOwnership ownership) {
1512
1807
  // by-value types
1513
1808
  if (G_VALUE_HOLDS_BOOLEAN (gvalue)) {
1514
1809
  return New<v8::Boolean>(g_value_get_boolean (gvalue));
@@ -1521,19 +1816,23 @@ Local<Value> GValueToV8(const GValue *gvalue, bool mustCopy) {
1521
1816
  } else if (G_VALUE_HOLDS_UINT (gvalue)) {
1522
1817
  return New<v8::Uint32>(g_value_get_uint (gvalue));
1523
1818
  } else if (G_VALUE_HOLDS_LONG (gvalue)) {
1524
- return New<Number>(g_value_get_long (gvalue));
1819
+ /* glong/gulong are platform-dependent (64-bit on LP64), so marshal as
1820
+ * BigInt for full precision and consistent behavior (#323, #149). */
1821
+ return v8::BigInt::New(Isolate::GetCurrent(), g_value_get_long (gvalue));
1525
1822
  } else if (G_VALUE_HOLDS_ULONG (gvalue)) {
1526
- return New<Number>(g_value_get_ulong (gvalue));
1823
+ return v8::BigInt::NewFromUnsigned(Isolate::GetCurrent(), g_value_get_ulong (gvalue));
1527
1824
  } else if (G_VALUE_HOLDS_INT64 (gvalue)) {
1528
- return New<Number>(g_value_get_int64 (gvalue));
1825
+ return v8::BigInt::New(Isolate::GetCurrent(), g_value_get_int64 (gvalue));
1529
1826
  } else if (G_VALUE_HOLDS_UINT64 (gvalue)) {
1530
- return New<Number>(g_value_get_uint64 (gvalue));
1827
+ return v8::BigInt::NewFromUnsigned(Isolate::GetCurrent(), g_value_get_uint64 (gvalue));
1531
1828
  } else if (G_VALUE_HOLDS_FLOAT (gvalue)) {
1532
1829
  return New<Number>(g_value_get_float (gvalue));
1533
1830
  } else if (G_VALUE_HOLDS_DOUBLE (gvalue)) {
1534
1831
  return New<Number>(g_value_get_double (gvalue));
1535
1832
  } else if (G_VALUE_HOLDS_GTYPE (gvalue)) {
1536
- return New<Number>(g_value_get_gtype (gvalue));
1833
+ // A GType is a gsize; marshal as BigInt to match the GIArgument path
1834
+ // and gi.getGType() (#323, #149).
1835
+ return v8::BigInt::NewFromUnsigned(Isolate::GetCurrent(), g_value_get_gtype (gvalue));
1537
1836
  } else if (G_VALUE_HOLDS_ENUM (gvalue)) {
1538
1837
  return New<Integer>(g_value_get_enum (gvalue));
1539
1838
  } else if (G_VALUE_HOLDS_FLAGS (gvalue)) {
@@ -1548,6 +1847,15 @@ Local<Value> GValueToV8(const GValue *gvalue, bool mustCopy) {
1548
1847
  return Nan::EmptyString();
1549
1848
  } else if (G_VALUE_HOLDS_OBJECT (gvalue)) {
1550
1849
  return WrapperFromGObject (G_OBJECT (g_value_get_object (gvalue)));
1850
+ } else if (G_VALUE_HOLDS (gvalue, G_TYPE_STRV)) {
1851
+ // GStrv is a boxed type, so this must come before G_VALUE_HOLDS_BOXED.
1852
+ char **strv = (char **) g_value_get_boxed (gvalue);
1853
+ Local<Array> array = Nan::New<Array>();
1854
+ if (strv != NULL) {
1855
+ for (guint i = 0; strv[i] != NULL; i++)
1856
+ Nan::Set(array, i, New<String>(strv[i]).ToLocalChecked());
1857
+ }
1858
+ return array;
1551
1859
  } else if (G_VALUE_HOLDS_BOXED (gvalue)) {
1552
1860
  GType gtype = G_VALUE_TYPE (gvalue);
1553
1861
  GIBaseInfo *info = g_irepository_find_by_gtype(NULL, gtype);
@@ -1555,7 +1863,7 @@ Local<Value> GValueToV8(const GValue *gvalue, bool mustCopy) {
1555
1863
  Throw::InvalidGType(gtype);
1556
1864
  return Nan::Null(); // FIXME(return a MaybeLocal instead?)
1557
1865
  }
1558
- Local<Value> obj = WrapperFromBoxed(info, g_value_get_boxed(gvalue), mustCopy);
1866
+ Local<Value> obj = WrapperFromBoxed(info, g_value_get_boxed(gvalue), ownership);
1559
1867
  g_base_info_unref(info);
1560
1868
  return obj;
1561
1869
  } else if (G_VALUE_HOLDS_PARAM (gvalue)) {
@@ -1566,7 +1874,13 @@ Local<Value> GValueToV8(const GValue *gvalue, bool mustCopy) {
1566
1874
  } else if (G_VALUE_HOLDS_VARIANT (gvalue)) {
1567
1875
  ERROR("Unsuported type: variant");
1568
1876
  } else {
1569
- ERROR("%s", G_VALUE_TYPE_NAME(gvalue));
1877
+ // Don't abort the whole process on a GValue type we can't convert
1878
+ // (e.g. GStreamer's GstValueArray / GstValueList, #389). Warn and
1879
+ // return null so the caller stays alive.
1880
+ g_warning("GValueToV8: unhandled GValue type '%s'; returning null. "
1881
+ "Please report this at https://github.com/romgrk/node-gtk/issues",
1882
+ G_VALUE_TYPE_NAME (gvalue));
1883
+ return Nan::Null();
1570
1884
  }
1571
1885
  }
1572
1886