[beast: 9/47] V8BSE: add on() to base objects to connect signal handlers



commit 0f6d9bf2053d63018da425c40b3c6da7219c52f4
Author: Tim Janik <timj gnu org>
Date:   Sun Jun 4 23:21:09 2017 +0200

    V8BSE: add on() to base objects to connect signal handlers
    
    The "on" method is implemented in Javascript and installed on all
    base object types. For this, V8stub::jsinit() is called after V8stub
    initialization, which in turn calls a JS function with all exports
    passed as argument. Using Bse.server, a simple signal test is as
    follows:
    
      Bse.server.on ('user-message', u => { console.log (u.text1) });
      um = Bse.UserMessage(); um.utype = 1; um.text1="A User Message!";
      Bse.server.send_user_message (um);
    
    Signed-off-by: Tim Janik <timj gnu org>

 ebeast/v8bse/V8Stub.py     |   45 ++++++++++++++++++++++++++++++++++++++++++-
 ebeast/v8bse/nodemodule.cc |    5 +++-
 2 files changed, 47 insertions(+), 3 deletions(-)
---
diff --git a/ebeast/v8bse/V8Stub.py b/ebeast/v8bse/V8Stub.py
index 098a249..2901b38 100644
--- a/ebeast/v8bse/V8Stub.py
+++ b/ebeast/v8bse/V8Stub.py
@@ -133,6 +133,7 @@ class Generator:
     s += 'typedef v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object>> 
V8ppCopyablePersistentObject;\n'
     # collect impl types
     namespaces = []
+    base_objects = []
     types = []
     for tp in implementation_types:
       if tp.isimpl:
@@ -185,7 +186,8 @@ class Generator:
       s += '  %-40s %s;\n' % (v8ppclass_type (tp), v8ppclass (tp))
     s += '  v8pp::module                             module_;\n'
     s += 'public:\n'
-    s += '  V8stub (v8::Isolate *const __v8isolate);\n'
+    s += '  explicit    V8stub (v8::Isolate *const __v8isolate);\n'
+    s += '  void        jsinit (v8::Local<v8::Context> context, v8::Local<v8::Object> exports) const;\n'
     s += '};\n'
     # V8stub ctor - begin
     s += '\nV8stub::V8stub (v8::Isolate *const __v8isolate) :\n'
@@ -195,6 +197,7 @@ class Generator:
       s += '  %s (__v8isolate),\n' % v8ppclass (tp)
     s += '  module_ (__v8isolate)\n'
     s += '{\n'
+    s += '  v8::HandleScope __v8scope (__v8isolate);\n'
     # Wrapper registration
     for tp in v8pp_class_types:
       cn = colon_typename (tp)
@@ -212,6 +215,7 @@ class Generator:
           b += '    .inherit<%s>()\n' % colon_typename (tb)
         if len (bases (tp)) == 0:
           b += '    .inherit<%s>()\n' % 'Aida::RemoteHandle'
+          base_objects += [ tp.name ]
       # Class ctor
       if tp.storage == Decls.SEQUENCE or tp.storage == Decls.RECORD:
         b += '    .ctor()\n'
@@ -254,9 +258,10 @@ class Generator:
             g += '      (void) __v8result;\n' # avoid 'unused' warnings
           g += '    };\n'
           g += '    __bsehandle->sig_%s() += sighandler;\n' % sg.name
-          g += '    __v8args.GetReturnValue().Set (v8::Null (__v8isolate));\n' # FIXME: False
+          g += '    __v8args.GetReturnValue().Set (v8::Null (__v8isolate));\n' # TODO: return handle for 
disconnect?
           g += '  };\n'
           b += '    .set ("__on_%s", onsig_%d)\n' % (sg.name, self.idcounter)
+          # TODO: provide a method to disconnect signal handlers, i.e. off()
           self.idcounter += 1
       # output generated signal handlers
       if g:
@@ -272,8 +277,44 @@ class Generator:
       s += '  module_.set ("%s", %s);\n' % (tp.name, v8ppclass (tp))
     # V8stub ctor - end
     s += '}\n'
+    # jsinit - begin
+    s += '\nvoid\n'
+    s += 'V8stub::jsinit (v8::Local<v8::Context> context, v8::Local<v8::Object> exports) const\n'
+    s += '{\n'
+    jsinit_def = '{ base_objects: [ %s ] }' % ', '.join ('exports.' + e for e in base_objects)
+    j = re.sub (r'@jsinit_def@', jsinit_def, jsinit)
+    j = re.sub (r'(["\\])', r'\\\1', j)
+    j = re.sub (r'\n', r'\\n"\n  "', j)
+    s += '  v8::Isolate *const isolate = context->GetIsolate();\n'
+    s += '  v8::HandleScope __v8scope (isolate);\n'
+    s += '  v8::ScriptOrigin org = v8::ScriptOrigin (v8pp::to_v8 (isolate, __FILE__),\n'
+    s += '                                           v8::Integer::New (isolate, __LINE__));\n'
+    s += '  const char *const script = "%s";\n' % j
+    s += '  v8::Local<v8::String> code = v8pp::to_v8 (isolate, script);\n'
+    s += '  v8::Local<v8::Value> bcode = v8::Script::Compile (context, code, 
&org).ToLocalChecked()->Run();\n'
+    s += '  v8::Local<v8::Function> fun = v8::Local<v8::Function>::Cast (bcode);\n'
+    s += '  assert (!fun.IsEmpty());\n'
+    s += '  v8::Local<v8::Value> args[] = { exports };\n'
+    s += '  fun->Call (context, context->Global(), sizeof (args) / sizeof (args[0]), args);\n'
+    # jsinit - end
+    s += '}\n'
     return s
 
+# Javacript code to be executed at v8stub setup
+jsinit = r"""
+(function (exports) {
+  const jsinit = @jsinit_def@;
+  jsinit.base_objects.forEach (obj => {
+    obj.prototype.on = function (signal, handler) {
+      const connector = this['__on_' + signal.replace (/[^a-z0-9]/gi, '_')];
+      if (connector instanceof Function)
+        return connector.call (this, handler);
+      throw new ReferenceError (signal + " is not a signal");
+    };
+  });
+})
+"""
+
 def generate (namespace_list, **args):
   import sys, tempfile, os
   config = {}
diff --git a/ebeast/v8bse/nodemodule.cc b/ebeast/v8bse/nodemodule.cc
index 4185b6b..0656d0e 100644
--- a/ebeast/v8bse/nodemodule.cc
+++ b/ebeast/v8bse/nodemodule.cc
@@ -273,7 +273,7 @@ v8bse_register_module (v8::Local<v8::Object> exports)
   };
   bse_client_connection->notify_callback (bsenotfycb);
 
-  // register v8stub
+  // register v8stub C++ bindings
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
   bse_v8stub = new V8stub (isolate);
   v8::Local<v8::Object> module_instance = bse_v8stub->module_.new_instance();
@@ -286,6 +286,9 @@ v8bse_register_module (v8::Local<v8::Object> exports)
   module_instance->DefineOwnProperty (context, v8pp::to_v8 (isolate, "server"),
                                       v8_server, v8::PropertyAttribute (v8::ReadOnly | v8::DontDelete));
 
+  // execute v8stub javascript initialization
+  bse_v8stub->jsinit (context, exports);
+
   // debugging aids:
   if (0)
     Bse::printerr ("gdb %s %u -ex 'catch catch' -ex 'catch throw'\n", Bse::string_split 
(program_invocation_name, " ", 1)[0], Bse::ThisThread::process_pid());


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