node-gtk 1.0.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/gi.cc CHANGED
@@ -208,6 +208,49 @@ NAN_METHOD(ObjectPropertySetter) {
208
208
  RETURN(success.ToLocalChecked());
209
209
  }
210
210
 
211
+ // All members of a C union live at offset 0. Some GLib versions (the
212
+ // regression fixed in 2.86.1 — glib#3745) emit a bogus 0xffff offset for union
213
+ // field members in the compiled typelib, so g_field_info_get_field /
214
+ // g_field_info_set_field would read/write far out of bounds and crash. Detect
215
+ // union fields so they can be accessed directly at offset 0 instead (#376).
216
+ static bool FieldIsInUnion (GIFieldInfo *field) {
217
+ GIBaseInfo *container = g_base_info_get_container (field);
218
+ return container != NULL
219
+ && g_base_info_get_type (container) == GI_INFO_TYPE_UNION;
220
+ }
221
+
222
+ // Whether a field type is a simple (non-pointer) C value that can be read or
223
+ // written by copying its bytes — mirrors what g_field_info_get/set_field
224
+ // accept. Pointer-backed types (strings, nested structs, arrays, ...) return
225
+ // false and are rejected, as before.
226
+ static bool IsSimpleFieldType (GITypeInfo *type_info) {
227
+ switch (g_type_info_get_tag (type_info)) {
228
+ case GI_TYPE_TAG_BOOLEAN:
229
+ case GI_TYPE_TAG_INT8:
230
+ case GI_TYPE_TAG_UINT8:
231
+ case GI_TYPE_TAG_INT16:
232
+ case GI_TYPE_TAG_UINT16:
233
+ case GI_TYPE_TAG_INT32:
234
+ case GI_TYPE_TAG_UINT32:
235
+ case GI_TYPE_TAG_INT64:
236
+ case GI_TYPE_TAG_UINT64:
237
+ case GI_TYPE_TAG_FLOAT:
238
+ case GI_TYPE_TAG_DOUBLE:
239
+ case GI_TYPE_TAG_UNICHAR:
240
+ case GI_TYPE_TAG_GTYPE:
241
+ return true;
242
+ case GI_TYPE_TAG_INTERFACE: {
243
+ GIBaseInfo *iface = g_type_info_get_interface (type_info);
244
+ GIInfoType itype = g_base_info_get_type (iface);
245
+ bool simple = (itype == GI_INFO_TYPE_ENUM || itype == GI_INFO_TYPE_FLAGS);
246
+ g_base_info_unref (iface);
247
+ return simple;
248
+ }
249
+ default:
250
+ return false;
251
+ }
252
+ }
253
+
211
254
  NAN_METHOD(StructFieldSetter) {
212
255
  Local<Object> boxedWrapper = info[0].As<Object>();
213
256
  Local<Object> fieldInfo = info[1].As<Object>();
@@ -231,6 +274,17 @@ NAN_METHOD(StructFieldSetter) {
231
274
 
232
275
  RETURN (Nan::Undefined());
233
276
 
277
+ } else if (FieldIsInUnion(field)) {
278
+
279
+ // Union members are at offset 0; write directly to avoid the bogus
280
+ // introspected offset (glib#3745, #376).
281
+ if (IsSimpleFieldType(field_type)) {
282
+ memcpy(boxed, &arg, GNodeJS::GetTypeSize(field_type));
283
+ } else {
284
+ Nan::ThrowError("Unable to set field (complex types not allowed)");
285
+ RETURN (Nan::Undefined());
286
+ }
287
+
234
288
  } else {
235
289
 
236
290
  if (g_field_info_set_field(field, boxed, &arg) == FALSE) {
@@ -286,6 +340,19 @@ NAN_METHOD(StructFieldGetter) {
286
340
  GNodeJS::ResourceOwnership ownership = GNodeJS::kCopy;
287
341
  BaseInfo typeInfo = g_field_info_get_type(*fieldInfo);
288
342
 
343
+ if (FieldIsInUnion(*fieldInfo)) {
344
+ // Union members are at offset 0; read directly to avoid the bogus
345
+ // introspected offset (glib#3745, #376).
346
+ if (!IsSimpleFieldType(*typeInfo)) {
347
+ Nan::ThrowError("Converting non-primitive fields is not allowed");
348
+ return;
349
+ }
350
+ memset(&value, 0, sizeof(value));
351
+ memcpy(&value, boxed, GNodeJS::GetTypeSize(*typeInfo));
352
+ RETURN(GNodeJS::GIArgumentToV8(*typeInfo, &value, -1, ownership));
353
+ return;
354
+ }
355
+
289
356
  if (!g_field_info_get_field(*fieldInfo, boxed, &value)) {
290
357
  /* If g_field_info_get_field() failed, this is a non-primitive type */
291
358
 
package/src/gobject.cc CHANGED
@@ -59,12 +59,18 @@ static GObject* CreateGObjectFromObject(GType gtype, Local<Value> object) {
59
59
 
60
60
  for (int i = 0; i < n_properties; i++) {
61
61
  Local<String> name = TO_STRING (Nan::Get(properties, i).ToLocalChecked());
62
- const char *name_string = g_strdup (*Nan::Utf8String(name));
62
+ // Accept camelCase property names (e.g. iconName) in addition to
63
+ // dashed/underscored ones; GObject canonicalizes '_' to '-' but not
64
+ // camelCase, so convert here (#320). The original spelling is kept so
65
+ // an unknown name is reported as the user wrote it.
66
+ Nan::Utf8String name_original (name);
67
+ char *name_string = Util::ToDashed (*name_original);
63
68
  Local<Value> value = Nan::Get(property_hash, name).ToLocalChecked();
64
69
 
65
70
  auto value_spec = g_object_class_find_property (G_OBJECT_CLASS (klass), name_string);
66
71
  if (value_spec == NULL) {
67
- Throw::InvalidPropertyName(name_string);
72
+ Throw::InvalidPropertyName(*name_original);
73
+ g_free(name_string);
68
74
  goto out;
69
75
  }
70
76
 
@@ -333,8 +339,7 @@ static void StoreVFunc(GType gtype, Callback *callback) {
333
339
  static void DestroyVFuncs(GType gtype) {
334
340
  /* Destroy vfunc list, if any */
335
341
  GSList *list = (GSList *) g_type_get_qdata (gtype, GNodeJS::vfuncs_quark());
336
- GSList *item = list;
337
- while ((item = g_slist_next (item)) != NULL) {
342
+ for (GSList *item = list; item != NULL; item = item->next) {
338
343
  auto callback = (Callback *) item->data;
339
344
  delete callback;
340
345
  }
package/src/type.cc CHANGED
@@ -15,11 +15,12 @@ char *GetInfoName (GIBaseInfo* info) {
15
15
 
16
16
  char* name = g_strdup (info_name);
17
17
 
18
- GIBaseInfo *parent;
19
- while ((parent = g_base_info_get_container (info)) != NULL) {
18
+ GIBaseInfo *parent = g_base_info_get_container (info);
19
+ while (parent != NULL) {
20
20
  char *new_name = g_strconcat (g_base_info_get_name(parent), ".", name, NULL);
21
21
  g_free (name);
22
22
  name = new_name;
23
+ parent = g_base_info_get_container (parent);
23
24
  }
24
25
 
25
26
  char *new_name = g_strconcat (g_base_info_get_namespace(info), ".", name, NULL);
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;
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
 
@@ -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,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)) {
@@ -1453,19 +1738,19 @@ bool V8ToGValue(GValue *gvalue, Local<Value> value, ResourceOwnership ownership)
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,6 +1767,16 @@ bool V8ToGValue(GValue *gvalue, Local<Value> value, ResourceOwnership ownership)
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));
@@ -1521,19 +1816,23 @@ Local<Value> GValueToV8(const GValue *gvalue, ResourceOwnership ownership) {
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, ResourceOwnership ownership) {
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);
@@ -1566,7 +1874,13 @@ Local<Value> GValueToV8(const GValue *gvalue, ResourceOwnership ownership) {
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