[gnode] Add trampolines / closures



commit 93c19c7cef1f8869c35cf35484e511a0b0417c51
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Fri Nov 27 16:15:56 2015 -0800

    Add trampolines / closures
    
    Trying out various C++ features here, so it's not coherent at all..

 src/function.cc |  106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/function.h  |    2 +
 src/gobject.cc  |   20 ++++++++++
 src/value.cc    |   21 +++++++++++
 src/value.h     |    1 +
 5 files changed, 150 insertions(+), 0 deletions(-)
---
diff --git a/src/function.cc b/src/function.cc
index 09d588c..e76bc4e 100644
--- a/src/function.cc
+++ b/src/function.cc
@@ -149,4 +149,110 @@ Handle<Function> MakeFunction(GIBaseInfo *info) {
     return fn;
 }
 
+class TrampolineInfo {
+ private:
+    ffi_cif cif;
+    ffi_closure *closure;
+    v8::Persistent<v8::Function> func;
+    GICallableInfo *info;
+    GIScopeType scope_type;
+
+ public:
+    TrampolineInfo(v8::Handle<v8::Function>  function,
+                   GICallableInfo           *info,
+                   GIScopeType               scope_type);
+
+    void Dispose();
+    static void Call(ffi_cif *cif, void *result, void **args, void *data);
+    void *GetClosure();
+};
+
+void TrampolineInfo::Dispose() {
+    func.Dispose ();
+    g_base_info_unref (info);
+    g_callable_info_free_closure (info, closure);
+};
+
+void TrampolineInfo::Call(ffi_cif *cif,
+                          void *result,
+                          void **args,
+                          void *data) {
+    TrampolineInfo *trampoline = (TrampolineInfo *) data;
+
+    int argc = g_callable_info_get_n_args (trampoline->info);
+    Handle<Value> argv[argc];
+
+    for (int i = 0; i < argc; i++) {
+        GIArgInfo arg_info;
+        g_callable_info_load_arg (trampoline->info, i, &arg_info);
+        GITypeInfo type_info;
+        g_arg_info_load_type (&arg_info, &type_info);
+        argv[i] = GIArgumentToV8 (&type_info, (GIArgument *) &args[i]);
+    }
+
+    Handle<Function> func = trampoline->func;
+    /* Provide a bogus "this" function. Any interested callers should
+     * bind their callbacks to what they're intersted in... */
+    Handle<Object> this_obj = func;
+    Handle<Value> return_value = func->Call (this_obj, argc, argv);
+    GITypeInfo type_info;
+    g_callable_info_load_return_type (trampoline->info, &type_info);
+    V8ToGIArgument (&type_info, (GIArgument *) &result, return_value,
+                    g_callable_info_may_return_null (trampoline->info));
+}
+
+TrampolineInfo::TrampolineInfo(Handle<Function>  function,
+                               GICallableInfo   *info,
+                               GIScopeType       scope_type) {
+    this->closure = g_callable_info_prepare_closure (info, &cif, Call, this);
+    this->func = Persistent<Function>::New (function);
+    this->info = g_base_info_ref (info);
+    this->scope_type = scope_type;
+}
+
+struct Closure {
+    GClosure base_;
+    Persistent<Function> func;
+
+    static void Marshal(GClosure *closure,
+                        GValue   *g_return_value,
+                        uint argc, const GValue *g_argv,
+                        gpointer  invocation_hint,
+                        gpointer  marshal_data);
+
+    static void Invalidated(gpointer data, GClosure *closure);
+};
+
+void Closure::Marshal(GClosure *base,
+                      GValue   *g_return_value,
+                      uint argc, const GValue *g_argv,
+                      gpointer  invocation_hint,
+                      gpointer  marshal_data) {
+    Closure *closure = (Closure *) base;
+    Handle<Function> func = closure->func;
+
+    Handle<Value> argv[argc];
+
+    for (uint i = 0; i < argc; i++)
+        argv[i] = GValueToV8 (&g_argv[i]);
+
+    Handle<Object> this_obj = func;
+    Handle<Value> return_value = func->Call (this_obj, argc, argv);
+    V8ToGValue (g_return_value, return_value);
+}
+
+void Closure::Invalidated(gpointer data, GClosure *base) {
+    Closure *closure = (Closure *) base;
+    closure->func.Dispose();
+}
+
+GClosure *MakeClosure(Handle<Function> function) {
+    Closure *closure = (Closure *) g_closure_new_simple (sizeof (*closure), NULL);
+    closure->func = Persistent<Function>::New (function);
+    GClosure *gclosure = &closure->base_;
+    g_closure_set_marshal (gclosure, Closure::Marshal);
+    g_closure_add_invalidate_notifier (gclosure, NULL, Closure::Invalidated);
+    return gclosure;
+}
+
 };
diff --git a/src/function.h b/src/function.h
index fea7903..fc9bced 100644
--- a/src/function.h
+++ b/src/function.h
@@ -24,9 +24,11 @@
 
 #include <node.h>
 #include <girepository.h>
+#include <girffi.h>
 
 namespace GNodeJS {
 
 v8::Handle<v8::Function> MakeFunction(GIBaseInfo *base_info);
+GClosure *MakeClosure(v8::Handle<v8::Function> function);
 
 };
diff --git a/src/gobject.cc b/src/gobject.cc
index 68bf554..f54b7ed 100644
--- a/src/gobject.cc
+++ b/src/gobject.cc
@@ -176,9 +176,29 @@ static void DefinePrototypeMethods(Handle<ObjectTemplate> prototype, GIBaseInfo
     }
 }
 
+static Handle<Value> SignalConnectInternal(const Arguments &args, bool after) {
+    HandleScope scope;
+    GObject *gobject = GObjectFromWrapper(args.This ());
+
+    String::Utf8Value signal_name (args[0]->ToString ());
+    Handle<Function> callback = Local<Function>::Cast (args[1]->ToObject ());
+    GClosure *gclosure = MakeClosure (callback);
+
+    ulong handler_id = g_signal_connect_closure (gobject, *signal_name, gclosure, after);
+    return scope.Close (Integer::NewFromUnsigned (handler_id));
+}
+
+static Handle<Value> SignalConnect(const Arguments &args) {
+    return SignalConnectInternal (args, false);
+}
+
 static Handle<FunctionTemplate> GetBaseClassTemplate() {
     Local<FunctionTemplate> tpl = FunctionTemplate::New ();
     tpl->InstanceTemplate ()->SetInternalFieldCount (1);
+
+    Handle<ObjectTemplate> proto = tpl->PrototypeTemplate ();
+    proto->Set (String::NewSymbol ("connect"), FunctionTemplate::New (SignalConnect)->GetFunction ());
+
     return tpl;
 }
 
diff --git a/src/value.cc b/src/value.cc
index b1bd5d2..16129e5 100644
--- a/src/value.cc
+++ b/src/value.cc
@@ -254,4 +254,25 @@ void V8ToGValue(GValue *gvalue, Handle<Value> value) {
     }
 }
 
+Handle<Value> GValueToV8(const GValue *gvalue) {
+    if (G_VALUE_HOLDS_BOOLEAN (gvalue)) {
+        if (g_value_get_boolean (gvalue))
+            return True ();
+        else
+            return False ();
+    } else if (G_VALUE_HOLDS_INT (gvalue)) {
+        return Integer::New (g_value_get_int (gvalue));
+    } else if (G_VALUE_HOLDS_UINT (gvalue)) {
+        return Integer::NewFromUnsigned (g_value_get_uint (gvalue));
+    } else if (G_VALUE_HOLDS_FLOAT (gvalue)) {
+        return Number::New (g_value_get_float (gvalue));
+    } else if (G_VALUE_HOLDS_DOUBLE (gvalue)) {
+        return Number::New (g_value_get_double (gvalue));
+    } else if (G_VALUE_HOLDS_STRING (gvalue)) {
+        return String::New (g_value_get_string (gvalue));
+    } else {
+        g_assert_not_reached ();
+    }
+}
+
 };
diff --git a/src/value.h b/src/value.h
index 48dcbb3..6fcd6cd 100644
--- a/src/value.h
+++ b/src/value.h
@@ -32,5 +32,6 @@ void V8ToGIArgument(GITypeInfo *type_info, GIArgument *argument, v8::Handle<v8::
 void FreeGIArgument(GITypeInfo *type_info, GIArgument *argument);
 
 void V8ToGValue(GValue *gvalue, v8::Handle<v8::Value> value);
+v8::Handle<v8::Value> GValueToV8(const GValue *gvalue);
 
 };


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]