[beast/ebeast: 14/24] V8: specialise v8pp::convert<> for all Aida::RemoteHandle derived types
- From: Tim Janik <timj src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [beast/ebeast: 14/24] V8: specialise v8pp::convert<> for all Aida::RemoteHandle derived types
- Date: Tue, 21 Feb 2017 23:22:18 +0000 (UTC)
commit ca8f4bc6f4bc25131b466b4b2b293f790cd6dbf9
Author: Tim Janik <timj gnu org>
Date: Mon Feb 20 15:01:39 2017 +0100
V8: specialise v8pp::convert<> for all Aida::RemoteHandle derived types
Signed-off-by: Tim Janik <timj gnu org>
ebeast/v8bse/V8Stub.py | 20 +++++++-
ebeast/v8bse/nodemodule.cc | 108 +++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 124 insertions(+), 4 deletions(-)
---
diff --git a/ebeast/v8bse/V8Stub.py b/ebeast/v8bse/V8Stub.py
index 265a113..d71aa45 100644
--- a/ebeast/v8bse/V8Stub.py
+++ b/ebeast/v8bse/V8Stub.py
@@ -95,10 +95,19 @@ class Generator:
continue
if tp.storage in (Decls.SEQUENCE, Decls.RECORD, Decls.INTERFACE):
v8pp_class_types += [ tp ]
- # C++ Classes
- s += '\n// C++ Classes\n'
+ # C++ class type aliases for v8pp::class_
+ s += '\n// v8pp::class_ aliases\n'
for tp in v8pp_class_types:
s += 'typedef %-40s %s;\n' % ('v8pp::class_<%s>' % colon_typename (tp), v8ppclass_type (tp))
+ # C++ class specialisations for v8pp::convert
+ s += '\n// v8pp::convert<> specialisations\n'
+ s += 'namespace v8pp {\n'
+ for tp in v8pp_class_types:
+ if tp.storage == Decls.INTERFACE:
+ cn = colon_typename (tp)
+ s += 'template<> struct convert%-40s : convert_AidaRemoteHandle<%s> {};\n' % ('<%s>' % cn, cn)
+ s += 'template<> struct convert%-40s : convert_AidaRemoteHandle<%s*> {};\n' % ('<%s*>' % cn, cn)
+ s += '} // v8pp\n'
# V8stub - main binding stub
s += '\n// Main binding stub\n'
s += 'struct V8stub final {\n'
@@ -116,6 +125,13 @@ class Generator:
s += ' %s (isolate),\n' % v8ppclass (tp)
s += ' module_ (isolate)\n'
s += '{\n'
+ # Wrapper registration
+ for tp in v8pp_class_types:
+ cn = colon_typename (tp)
+ if tp.storage == Decls.INTERFACE:
+ s += ' aida_remote_handle_wrapper_map (Aida::TypeHash '
+ s += '(%s), ' % class_digest (tp)
+ s += 'aida_remote_handle_wrapper_impl<%s>);\n' % cn
# Class bindings
for tp in v8pp_class_types:
cn = colon_typename (tp)
diff --git a/ebeast/v8bse/nodemodule.cc b/ebeast/v8bse/nodemodule.cc
index dd9c3ab..2a9bb90 100644
--- a/ebeast/v8bse/nodemodule.cc
+++ b/ebeast/v8bse/nodemodule.cc
@@ -1,8 +1,112 @@
// Licensed GNU LGPL v2.1 or later: http://www.gnu.org/licenses/lgpl.html
-#include "../bse.hh"
-#include "v8bse.cc"
+#include "../../bse/bse.hh"
#include <node.h>
#include <uv.h>
+#include <v8pp/class.hpp>
+#include <v8pp/convert.hpp>
+
+namespace Aida = Rapicorn::Aida;
+
+// == RemoteHandle Wrapping ==
+/* NOTE: A RemoteHandle is a smart pointer to a complex C++ object (possibly behind a thread boundary),
+ * so multiple RemoteHandle objects can point to the same C++ object. There are a couple ways that a
+ * RemoteHandle can be mapped onto Javascript:
+ * 1) A v8::Object contains a RemoteHandle, i.e. every function that returns a new RemoteHandle also
+ * returns a new Object in JS, even if they all point to the same C++ impl. In this case it is
+ * desirable that the JS Object cannot gain new properties (i.e. isSealed() === true).
+ * A major downside here is that two JS Objects that point to the same C++ impl are not === equal.
+ * 2) All RemoteHandles that point to the same C++ impl are mapped onto a single JS Object of the
+ * appropriate down-cast type. This correctly provides === equality and new properties added to
+ * a JS Object are preseved whenever another RemoteHandles is mapped into a JS Object that points
+ * to the same C++ impl.
+ * Here, an extra map must be maintained to achieve the (n RemoteHandle) => (1 JS Object) mapping
+ * by storing and looking up the orbid_ that defines the object identity each RemoteHandle points to.
+ * The downside here is resource lockup. Once created, a JS Object must keep its RemoteHandle around
+ * which forces the C++ impl to stay alive. And the v8::Persistent holding the JS Object map entry
+ * must not be weak to prevent GC cycles from "forgetting" the properties stored on the JS Object.
+ * 3) A viable middle ground might be using the map from (2) so a JS Object provides correct equality
+ * via ===, but the JS Objects are sealed as in (1) so we can use SetWeak persistents for the map
+ * to avoid resource leaks.
+ */
+
+/// Function to wrap a RemoteHandle derived type via v8pp::class_<>::import_external().
+typedef v8::Local<v8::Object> (*AidaRemoteHandleWrapper) (v8::Isolate *const, Rapicorn::Aida::RemoteHandle);
+
+/// Default implementation to wrap a RemoteHandle derived type via v8pp::class_<>::import_external().
+template<class Native> static v8::Local<v8::Object>
+aida_remote_handle_wrapper_impl (v8::Isolate *const isolate, Aida::RemoteHandle rhandle)
+{
+ Native target = Native::down_cast (rhandle);
+ if (target != NULL)
+ return v8pp::class_<Native>::import_external (isolate, new Native (target));
+ return v8::Local<v8::Object>();
+}
+
+/// Map Aida type ids to the corresponding AidaRemoteHandleWrapper.
+static AidaRemoteHandleWrapper
+aida_remote_handle_wrapper_map (const Aida::TypeHash &thash, AidaRemoteHandleWrapper newfunc)
+{
+ static std::map<Aida::TypeHash, AidaRemoteHandleWrapper> wmap;
+ if (!newfunc)
+ return wmap[thash];
+ wmap[thash] = newfunc;
+ return NULL;
+}
+
+/// Create (or find) the corresponding down_cast() JS Object for a RemoteHandle.
+static v8::Local<v8::Object>
+aida_remote_handle_wrap_native (v8::Isolate *const isolate, Aida::RemoteHandle const &rhandle)
+{
+ v8::EscapableHandleScope scope (isolate);
+ if (NULL != rhandle)
+ {
+ Aida::TypeHashList thl = rhandle.__aida_typelist__();
+ for (const auto &th : thl)
+ {
+ AidaRemoteHandleWrapper wrapper = aida_remote_handle_wrapper_map (th, AidaRemoteHandleWrapper
(NULL));
+ if (wrapper)
+ return scope.Escape (wrapper (isolate, rhandle));
+ }
+ }
+ return scope.Escape (v8::Local<v8::Object>());
+}
+
+/// Retrieve the native RemoteHandle from a JS Object.
+template<class NativeClass> static NativeClass&
+aida_remote_handle_unwrap_native (v8::Isolate *const isolate, v8::Local<v8::Value> value)
+{
+ v8::HandleScope scope (isolate);
+ NativeClass *nobject = NULL;
+ if (!value.IsEmpty() && value->IsObject())
+ nobject = v8pp::class_<NativeClass>::unwrap_object (isolate, value);
+ if (!nobject)
+ throw std::runtime_error ("failed to unwrap C++ Aida::RemoteHandle");
+ return *nobject;
+}
+
+/// Helper to specialize v8pp::convert<> for all RemoteHandle types.
+template<class DerivedHandle>
+struct convert_AidaRemoteHandle
+{
+ using N = DerivedHandle; // native type, derived from Aida::RemoteHandle
+ using J = v8::Local<v8::Object>; // Javascript type
+ static bool is_valid (v8::Isolate *const isolate, v8::Local<v8::Value> v) { return !v.IsEmpty() &&
v->IsObject(); }
+ static N& from_v8 (v8::Isolate *const isolate, v8::Local<v8::Value> v) { return
aida_remote_handle_unwrap_native<N> (isolate, v); }
+ static J to_v8 (v8::Isolate *const isolate, const N &rhandle) { return
aida_remote_handle_wrap_native (isolate, rhandle); }
+};
+
+/// Helper for convert_AidaRemoteHandle pointer types.
+template<class DerivedHandle>
+struct convert_AidaRemoteHandle<DerivedHandle*>
+{
+ using N = DerivedHandle; // native type, derived from Aida::RemoteHandle
+ using J = v8::Local<v8::Object>; // Javascript type
+ static bool is_valid (v8::Isolate *const isolate, v8::Local<v8::Value> v) { return
v8pp::convert<N>::is_valid (isolate, v); }
+ static N* from_v8 (v8::Isolate *const isolate, v8::Local<v8::Value> v) { return
&v8pp::convert<N>::from_v8 (isolate, v); }
+ static J to_v8 (v8::Isolate *const isolate, const N *n) { return !n ? J() :
v8pp::convert<N>::to_v8 (isolate, *n); }
+};
+
+#include "v8bse.cc"
// v8pp binding for Bse
static V8stub *bse_v8stub = NULL;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]