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/README.md +80 -189
- package/bin/node-gtk.js +31 -0
- package/lib/binding/node-v127-linux-x64/node_gtk.node +0 -0
- package/lib/binding/node-v131-linux-x64/node_gtk.node +0 -0
- package/lib/binding/node-v137-linux-x64/node_gtk.node +0 -0
- package/lib/binding/node-v147-linux-x64/node_gtk.node +0 -0
- package/lib/bootstrap.js +22 -4
- package/lib/index.js +25 -0
- 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 +93 -2
- package/src/function.cc +65 -7
- package/src/function.h +1 -0
- package/src/gi.cc +67 -0
- package/src/gobject.cc +9 -4
- package/src/type.cc +3 -2
- package/src/value.cc +345 -31
- package/src/value.h +16 -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-v93-linux-x64/node_gtk.node +0 -0
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
|
-
|
|
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(
|
|
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 (
|
|
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
|
-
/*
|
|
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;
|
|
357
414
|
|
|
358
|
-
|
|
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
|
-
|
|
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,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,
|
|
1741
|
+
g_value_set_long (gvalue, V8ToInt64 (value));
|
|
1457
1742
|
} else if (G_VALUE_HOLDS_ULONG (gvalue)) {
|
|
1458
|
-
g_value_set_ulong (gvalue,
|
|
1743
|
+
g_value_set_ulong (gvalue, V8ToUint64 (value));
|
|
1459
1744
|
} else if (G_VALUE_HOLDS_INT64 (gvalue)) {
|
|
1460
|
-
g_value_set_int64 (gvalue,
|
|
1745
|
+
g_value_set_int64 (gvalue, V8ToInt64 (value));
|
|
1461
1746
|
} else if (G_VALUE_HOLDS_UINT64 (gvalue)) {
|
|
1462
|
-
g_value_set_uint64 (gvalue,
|
|
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,
|
|
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
|
-
|
|
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
|
|
1823
|
+
return v8::BigInt::NewFromUnsigned(Isolate::GetCurrent(), g_value_get_ulong (gvalue));
|
|
1527
1824
|
} else if (G_VALUE_HOLDS_INT64 (gvalue)) {
|
|
1528
|
-
return New
|
|
1825
|
+
return v8::BigInt::New(Isolate::GetCurrent(), g_value_get_int64 (gvalue));
|
|
1529
1826
|
} else if (G_VALUE_HOLDS_UINT64 (gvalue)) {
|
|
1530
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|