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/debug.cc CHANGED
@@ -10,6 +10,7 @@
10
10
  #include "type.h"
11
11
 
12
12
  #include <cstdio>
13
+ #include <cinttypes>
13
14
 
14
15
  namespace GNodeJS {
15
16
 
@@ -245,16 +246,16 @@ void print_gtype (GType type) {
245
246
  void print_type (GType type) {
246
247
  GType t = type;
247
248
  while(!G_TYPE_IS_FUNDAMENTAL(type) && t != 0) {
248
- printf("\t\x1b[92m -> %s \t (%lu)\x1b[0m\n", g_type_name(t), t);
249
+ printf("\t\x1b[92m -> %s \t (%" PRIuPTR ")\x1b[0m\n", g_type_name(t), (uintptr_t)t);
249
250
  t = g_type_parent(t);
250
251
  }
251
252
  }
252
253
 
253
254
  void print_klass (void * klass) {
254
255
  printf("\x1b[38;5;202m");
255
- printf("klass: %s (type: %lu )",
256
+ printf("klass: %s (type: %" PRIuPTR " )",
256
257
  G_OBJECT_CLASS_NAME (klass),
257
- G_OBJECT_CLASS_TYPE (klass) );
258
+ (uintptr_t)G_OBJECT_CLASS_TYPE (klass) );
258
259
  printf("\x1b[0m\n");
259
260
  }
260
261
 
package/src/function.cc CHANGED
@@ -55,7 +55,9 @@ static void* AllocateArgument (GIBaseInfo *arg_info) {
55
55
 
56
56
  GIBaseInfo* base_info = g_type_info_get_interface (&arg_type);
57
57
  size_t size = Boxed::GetSize (base_info);
58
- void* pointer = g_malloc0(size);
58
+ GType gtype = g_registered_type_info_get_g_type (base_info);
59
+ // Match g_boxed_free's allocator for registered boxed types (#290, #213).
60
+ void* pointer = AllocateBoxed(gtype, size);
59
61
 
60
62
  g_base_info_unref(base_info);
61
63
  return pointer;
@@ -67,6 +69,35 @@ static bool IsMethod (GIBaseInfo *info) {
67
69
  (flags & GI_FUNCTION_IS_CONSTRUCTOR) == 0);
68
70
  }
69
71
 
72
+ static bool EndsWith (const char *str, const char *suffix) {
73
+ size_t str_len = strlen (str);
74
+ size_t suffix_len = strlen (suffix);
75
+ return str_len >= suffix_len && strcmp (str + str_len - suffix_len, suffix) == 0;
76
+ }
77
+
78
+ /* True for an instance method that deallocates the instance itself — a *_free
79
+ * or *_unref on a boxed/struct/union. node-gtk's wrapper owns the memory and
80
+ * frees it on GC, so after such a call the wrapper must disown it or GC will
81
+ * double-free (#429). Restricted to boxed-like containers: GObject lifetime is
82
+ * handled separately, and gtk_widget_destroy() etc. don't free the wrapper. */
83
+ static bool FreesInstance (GIFunctionInfo *info) {
84
+ if (!IsMethod (info))
85
+ return false;
86
+
87
+ const char *symbol = g_function_info_get_symbol (info);
88
+ if (symbol == NULL || !(EndsWith (symbol, "_free") || EndsWith (symbol, "_unref")))
89
+ return false;
90
+
91
+ GIBaseInfo *container = g_base_info_get_container (info);
92
+ if (container == NULL)
93
+ return false;
94
+
95
+ GIInfoType type = g_base_info_get_type (container);
96
+ return type == GI_INFO_TYPE_STRUCT
97
+ || type == GI_INFO_TYPE_UNION
98
+ || type == GI_INFO_TYPE_BOXED;
99
+ }
100
+
70
101
  static bool ShouldSkipReturn(GIBaseInfo *info, GITypeInfo *return_type) {
71
102
  return g_type_info_get_tag(return_type) == GI_TYPE_TAG_VOID
72
103
  || g_callable_info_skip_return(info) == TRUE;
@@ -134,6 +165,7 @@ bool FunctionInfo::Init() {
134
165
 
135
166
  is_method = IsMethod(info);
136
167
  can_throw = g_callable_info_can_throw_gerror (info);
168
+ frees_instance = FreesInstance(info);
137
169
 
138
170
  n_callable_args = g_callable_info_get_n_args (info);
139
171
  n_total_args = n_callable_args;
@@ -208,14 +240,14 @@ bool FunctionInfo::Init() {
208
240
  if (closure_i >= 0 && closure_i < n_callable_args)
209
241
  call_parameters[closure_i].type = ParameterType::kSKIP;
210
242
 
211
- if (destroy_i < i) {
243
+ if (destroy_i >= 0 && destroy_i < i) {
212
244
  if (IsDirectionIn(call_parameters[destroy_i].direction))
213
245
  n_in_args--;
214
246
  if (IsDirectionOut(call_parameters[destroy_i].direction))
215
247
  n_out_args--;
216
248
  }
217
249
 
218
- if (closure_i < i) {
250
+ if (closure_i >= 0 && closure_i < i) {
219
251
  if (IsDirectionIn(call_parameters[closure_i].direction))
220
252
  n_in_args--;
221
253
  if (IsDirectionOut(call_parameters[closure_i].direction))
@@ -335,7 +367,7 @@ Local<Value> FunctionCall (
335
367
 
336
368
  if (func->is_method) {
337
369
  GIBaseInfo *container = g_base_info_get_container (gi_info);
338
- V8ToGIArgument(container, &total_arg_values[0], info.This());
370
+ V8ToGIArgumentInterface(container, &total_arg_values[0], info.This());
339
371
  callable_arg_values = &total_arg_values[1];
340
372
  } else {
341
373
  callable_arg_values = &total_arg_values[0];
@@ -388,16 +420,16 @@ Local<Value> FunctionCall (
388
420
  }
389
421
  else if (param.type == ParameterType::kCALLBACK) {
390
422
  Callback *callback;
391
- ffi_closure *closure;
423
+ gpointer callable; /* executable trampoline address (see Callback) */
392
424
 
393
425
  if (info[in_arg]->IsNullOrUndefined()) {
394
- closure = nullptr;
426
+ callable = nullptr;
395
427
  callback = nullptr;
396
428
  } else {
397
429
  GICallableInfo *callback_info = g_type_info_get_interface (&type_info);
398
430
  GIScopeType scope_type = g_arg_info_get_scope(&arg_info);
399
431
  callback = new Callback(info[in_arg].As<Function>(), callback_info, scope_type);
400
- closure = callback->closure;
432
+ callable = callback->native_address;
401
433
  g_base_info_unref (callback_info);
402
434
  }
403
435
 
@@ -414,7 +446,7 @@ Local<Value> FunctionCall (
414
446
  callable_arg_values[closure_i].v_pointer = callback;
415
447
  }
416
448
 
417
- callable_arg_values[i].v_pointer = closure;
449
+ callable_arg_values[i].v_pointer = callable;
418
450
  func->call_parameters[i].data.v_pointer = callback;
419
451
  }
420
452
 
@@ -440,6 +472,22 @@ Local<Value> FunctionCall (
440
472
  param.data.v_pointer = callable_arg_values[i].v_pointer;
441
473
  callable_arg_values[i].v_pointer = &param.data;
442
474
  }
475
+ // For a transfer-container IN GList/GSList/GHashTable the callee
476
+ // frees the container; snapshot the (caller-owned) element
477
+ // pointers now so they can be freed after the call (#399).
478
+ else if (IsTransferContainerInList(&type_info,
479
+ g_arg_info_get_ownership_transfer(&arg_info), direction)) {
480
+ param.data = {};
481
+ param.data.v_pointer = CaptureTransferContainerElements(
482
+ &type_info, callable_arg_values[i].v_pointer);
483
+ }
484
+ // For a transfer-full IN boxed (or array of boxed) the callee
485
+ // frees the memory; hand it a copy so the JS wrapper's own
486
+ // memory isn't double-freed when it's finalized (#409).
487
+ else if (direction == GI_DIRECTION_IN
488
+ && g_arg_info_get_ownership_transfer(&arg_info) == GI_TRANSFER_EVERYTHING) {
489
+ CopyBoxedForTransferFullIn(&type_info, &callable_arg_values[i], param.length);
490
+ }
443
491
  }
444
492
 
445
493
  in_arg++;
@@ -488,24 +536,29 @@ Local<Value> FunctionCall (
488
536
  Nan::ThrowError(error_stack->message);
489
537
  g_error_free(error_stack);
490
538
  }
539
+
540
+ /*
541
+ * Fifth, free the return value and arguments
542
+ */
543
+
544
+ if (!use_return_value)
545
+ FreeGIArgument(&return_type, &return_value_stack, return_transfer);
546
+
491
547
  } else if (!use_return_value) {
492
- jsReturnValue = func->GetReturnValue (
548
+
549
+ // Value transferred to jsReturnValue
550
+ jsReturnValue = func->JsReturnValue (
493
551
  info.This(),
494
552
  &return_type,
495
- use_return_value ? return_value : &return_value_stack,
496
- callable_arg_values);
553
+ &return_value_stack,
554
+ callable_arg_values,
555
+ return_transfer);
497
556
  } else {
557
+
558
+ // Value returned in return_value
498
559
  jsReturnValue = Nan::Undefined();
499
560
  }
500
561
 
501
-
502
- /*
503
- * Fifth, free the return value and arguments
504
- */
505
-
506
- if (!use_return_value)
507
- FreeGIArgument(&return_type, &return_value_stack, return_transfer);
508
-
509
562
  for (int i = 0; i < func->n_callable_args; i++) {
510
563
  GIArgInfo arg_info = {};
511
564
  GITypeInfo arg_type;
@@ -533,6 +586,11 @@ Local<Value> FunctionCall (
533
586
  delete callback;
534
587
  }
535
588
  }
589
+ else if (IsTransferContainerInList (&arg_type, transfer, direction)) {
590
+ // The callee already freed the container structure; free only the
591
+ // captured (caller-owned) elements, never the freed container (#399).
592
+ FreeTransferContainerElements (&arg_type, param.data.v_pointer);
593
+ }
536
594
  else {
537
595
  if (direction == GI_DIRECTION_INOUT || (direction == GI_DIRECTION_OUT && !g_arg_info_is_caller_allocates (&arg_info)))
538
596
  FreeGIArgument (&arg_type, (GIArgument*)arg_value.v_pointer, transfer, direction);
@@ -541,6 +599,11 @@ Local<Value> FunctionCall (
541
599
  }
542
600
  }
543
601
 
602
+ // If this method freed the instance, drop node-gtk's ownership of it so the
603
+ // GC finalizer won't free it a second time (#429).
604
+ if (func->frees_instance && !didThrow)
605
+ DisownBoxed (info.This());
606
+
544
607
  #ifndef __linux__
545
608
  delete[] total_arg_values;
546
609
  #endif
@@ -553,11 +616,12 @@ Local<Value> FunctionCall (
553
616
  * Creates the JS return value from the C arguments list
554
617
  * @returns the JS return value
555
618
  */
556
- Local<Value> FunctionInfo::GetReturnValue (
619
+ Local<Value> FunctionInfo::JsReturnValue (
557
620
  Local<Value> self,
558
621
  GITypeInfo* return_type,
559
622
  GIArgument* return_value,
560
- GIArgument* callable_arg_values) {
623
+ GIArgument* callable_arg_values,
624
+ GITransfer return_transfer) {
561
625
 
562
626
  Local<Value> jsReturnValue;
563
627
  int jsReturnIndex = 0;
@@ -591,7 +655,10 @@ Local<Value> FunctionInfo::GetReturnValue (
591
655
  // When a method returns the instance itself, skip the conversion and just return the
592
656
  // existent wrapper
593
657
  bool isReturningSelf = is_method && PointerFromWrapper(self) == return_value->v_pointer;
594
- ADD_RETURN (isReturningSelf ? self : GIArgumentToV8 (return_type, return_value, length))
658
+ ADD_RETURN (isReturningSelf ? self :
659
+ GIArgumentToV8 (
660
+ return_type, return_value, length,
661
+ return_transfer == GI_TRANSFER_EVERYTHING ? kTransfer : kNone))
595
662
  }
596
663
 
597
664
  for (int i = 0; i < n_callable_args; i++) {
@@ -630,13 +697,15 @@ Local<Value> FunctionInfo::GetReturnValue (
630
697
  ADD_RETURN (result)
631
698
 
632
699
  } else if (param.type == ParameterType::kNORMAL) {
700
+ GITransfer transfer = g_arg_info_get_ownership_transfer(&arg_info);
701
+ ResourceOwnership ownership = transfer == GI_TRANSFER_EVERYTHING ? kTransfer : kNone;
633
702
 
634
703
  if (IsPointerType(&arg_type) && g_arg_info_is_caller_allocates(&arg_info)) {
635
704
  void *pointer = &arg_value.v_pointer;
636
- ADD_RETURN (GIArgumentToV8(&arg_type, (GIArgument*) pointer))
705
+ ADD_RETURN (GIArgumentToV8(&arg_type, (GIArgument*) pointer, -1, ownership))
637
706
  }
638
707
  else {
639
- ADD_RETURN (GIArgumentToV8(&arg_type, (GIArgument*) arg_value.v_pointer))
708
+ ADD_RETURN (GIArgumentToV8(&arg_type, (GIArgument*) arg_value.v_pointer, -1, ownership))
640
709
  }
641
710
  }
642
711
  }
@@ -647,19 +716,6 @@ Local<Value> FunctionInfo::GetReturnValue (
647
716
  return jsReturnValue;
648
717
  }
649
718
 
650
- /**
651
- * Frees the C return value
652
- * @param return_value the return value pointer
653
- */
654
- void FunctionInfo::FreeReturnValue (GIArgument *return_value) {
655
- GITypeInfo return_type;
656
- g_callable_info_load_return_type(info, &return_type);
657
- GITransfer return_transfer = g_callable_info_get_caller_owns(info);
658
-
659
- FreeGIArgument(&return_type, return_value, return_transfer);
660
- }
661
-
662
-
663
719
 
664
720
  Local<Function> MakeFunction(GIBaseInfo *info) {
665
721
  FunctionInfo *func = new FunctionInfo(info);
@@ -673,8 +729,8 @@ Local<Function> MakeFunction(GIBaseInfo *info) {
673
729
  auto fn = Nan::GetFunction (tpl).ToLocalChecked();
674
730
  fn->SetName(name);
675
731
 
676
- Persistent<FunctionTemplate> persistent(Isolate::GetCurrent(), tpl);
677
- persistent.SetWeak(func, FunctionDestroyed, WeakCallbackType::kParameter);
732
+ func->persistent = new Nan::Persistent<FunctionTemplate>(tpl);
733
+ func->persistent->SetWeak(func, FunctionDestroyed, WeakCallbackType::kParameter);
678
734
 
679
735
  return fn;
680
736
  }
@@ -691,8 +747,9 @@ void FunctionInvoker(const Nan::FunctionCallbackInfo<Value> &info) {
691
747
  Callback::AsyncFree();
692
748
  }
693
749
 
694
- void FunctionDestroyed(const v8::WeakCallbackInfo<FunctionInfo> &data) {
750
+ void FunctionDestroyed(const Nan::WeakCallbackInfo<FunctionInfo> &data) {
695
751
  FunctionInfo *func = data.GetParameter ();
752
+ delete func->persistent;
696
753
  delete func;
697
754
  }
698
755
 
package/src/function.h CHANGED
@@ -33,6 +33,7 @@ struct FunctionInfo {
33
33
 
34
34
  bool is_method;
35
35
  bool can_throw;
36
+ bool frees_instance; /* a *_free/*_unref method that deallocates the instance (#429) */
36
37
 
37
38
  int n_callable_args;
38
39
  int n_total_args;
@@ -40,14 +41,14 @@ struct FunctionInfo {
40
41
  int n_in_args;
41
42
 
42
43
  Parameter* call_parameters;
44
+ Nan::Persistent<FunctionTemplate> *persistent;
43
45
 
44
46
  FunctionInfo(GIBaseInfo* info);
45
47
  ~FunctionInfo();
46
48
 
47
49
  bool Init();
48
50
  bool TypeCheck (const Nan::FunctionCallbackInfo<Value> &info);
49
- Local<Value> GetReturnValue (Local<Value> self, GITypeInfo* return_type, GIArgument* return_value, GIArgument* callable_arg_values);
50
- void FreeReturnValue (GIArgument *return_value);
51
+ Local<Value> JsReturnValue (Local<Value> self, GITypeInfo* return_type, GIArgument* return_value, GIArgument* callable_arg_values, GITransfer return_transfer);
51
52
  };
52
53
 
53
54
  bool IsDestroyNotify (GIBaseInfo *info);
@@ -55,7 +56,7 @@ bool IsDestroyNotify (GIBaseInfo *info);
55
56
  Local<Value> FunctionCall (FunctionInfo *func, const Nan::FunctionCallbackInfo<Value> &info, GIArgument *return_value = NULL, GError **error = NULL);
56
57
 
57
58
  void FunctionInvoker (const Nan::FunctionCallbackInfo<Value> &info);
58
- void FunctionDestroyed (const v8::WeakCallbackInfo<FunctionInfo> &data);
59
+ void FunctionDestroyed (const Nan::WeakCallbackInfo<FunctionInfo> &data);
59
60
 
60
61
  Local<Function> MakeFunction (GIBaseInfo *base_info);
61
62
 
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) {
@@ -283,9 +337,22 @@ NAN_METHOD(StructFieldGetter) {
283
337
  }
284
338
 
285
339
  GIArgument value;
286
- bool mustCopy = true;
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
 
@@ -300,13 +367,13 @@ NAN_METHOD(StructFieldGetter) {
300
367
  // auto offset = g_field_info_get_offset(*fieldInfo);
301
368
  // auto fieldPtr = G_STRUCT_MEMBER_P(boxed, offset);
302
369
  // value.v_pointer = fieldPtr;
303
- // mustCopy = false;
370
+ // ownership = kNone;
304
371
 
305
372
  Nan::ThrowError("Converting non-primitive fields is not allowed");
306
373
  return;
307
374
  }
308
375
 
309
- RETURN(GNodeJS::GIArgumentToV8(*typeInfo, &value, -1, mustCopy));
376
+ RETURN(GNodeJS::GIArgumentToV8(*typeInfo, &value, -1, ownership));
310
377
  }
311
378
 
312
379
  NAN_METHOD(StartLoop) {