v8toolkit  0.0.1
Utility library for embedding V8 Javascript engine in a c++ program
v8_class_wrapper.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <type_traits>
7 #include <algorithm>
8 
9 #include <iostream>
10 #include <vector>
11 #include <utility>
12 #include <assert.h>
13 
14 // vector_map compiles a LOT faster than std::map as the number of wrapped classes increases
15 #define USE_EASTL_FOR_INTERNALS
16 
17 #ifdef USE_EASTL_FOR_INTERNALS
18 #include <EASTL/vector_map.h>
19 template<class... ArgTs>
20 using MapT=eastl::vector_map<ArgTs...>;
21 #else
22 #include <map>
23 template<class... ArgTs>
24 using MapT=std::map<ArgTs...>;
25 #endif
26 
27 #include "wrapped_class_base.h"
28 #include "v8toolkit.h"
29 #include "casts.hpp"
30 
31 // allow _v8 suffix for making v8::String objects
32 using namespace v8toolkit::literals;
33 
34 // this enables certain functionality only if bidirectional.h has been included
35 #ifdef V8TOOLKIT_BIDIRECTIONAL_ENABLED
36 #define V8_CLASS_WRAPPER_HAS_BIDIRECTIONAL_SUPPORT
37 #endif
38 
39 namespace v8toolkit {
40 
41 
42 //#define V8_CLASS_WRAPPER_DEBUG
43 
44 #ifdef V8_CLASS_WRAPPER_DEBUG
45 #define V8TOOLKIT_DEBUG(format_string, ...) \
46  fprintf(stderr, format_string, ##__VA_ARGS__);
47 #else
48 #define V8TOOLKIT_DEBUG(format_string, ...)
49 #endif
50 /**
51 * Design Questions:
52 * - When a c++ object returns a new object represted by one of its members, should it
53 * return the same javascript object each time as well?
54 * class Thing {
55 * OtherClass other_class;
56 * OtherClass & get_other_class(){return this->other_class;} <== should this return the same javascript object on each call for the same Thing object
57 * }
58 * - that's currently what the existing_wrapped_objects map is for, but if a new object
59 * of the same time is created at the same address as an old one, the old javascript
60 * object will be returned.
61 * - how would you track if the c++ object source object for an object went away?
62 * - how would you actually GC the old object when containing object went away?
63 * - Maybe allow some type of optional class customization to help give hints to V8ClassWrapper to have better behavior
64 *
65 */
66 
67 
68 /***
69 * set of classes for determining what to do do the underlying c++
70 * object when the javascript object is garbage collected
71 */
73 {
74  virtual void operator()(v8::Isolate * isolate, const void * object) const = 0;
75 
76  // wehther this destructor is actually destructive to memory it is given. Does it "own" the memory or not.
77  virtual bool destructive() const = 0;
78 
79  virtual std::string name() const = 0;
80 };
81 
82 
83 /**
84 * Helper to delete a C++ object when the corresponding javascript object is garbage collected
85 */
86 template<class T>
89 // std::cerr << fmt::format("creating destructor behavior delete at {}", (void*)this) << std::endl;
90  }
91  void operator()(v8::Isolate * isolate, const void * void_object) const override {
92  T* object = (T*)void_object;
93  V8TOOLKIT_DEBUG("Deleting underlying C++ object at %p during V8 garbage collection\n", void_object);
94  delete object;
95  isolate->AdjustAmountOfExternalAllocatedMemory(-static_cast<int64_t>(sizeof(T)));
96  }
97  bool destructive() const override {
98 // std::cerr << fmt::format("_Delete::destructive") << std::endl;
99  return true;
100  }
101  virtual std::string name() const override {
102  return fmt::format("_delete");
103  }
104 
105 };
106 
107 /**
108 * Helper to not do anything to the underlying C++ object when the corresponding javascript object
109 * is garbage collected
110 */
113 // std::cerr << fmt::format("creating DestructorBehavior_LeageAlone at {}", (void*)this) << std::endl;
114  }
115 
116  void operator()(v8::Isolate * isolate, const void * void_object) const override {
117  V8TOOLKIT_DEBUG("Not deleting underlying C++ object %p during V8 garbage collection\n", void_object);
118  }
119  bool destructive() const override {
120 // std::cerr << fmt::format("_Delete::destructive") << std::endl;
121  return false;
122  }
123  virtual std::string name() const override {
124 
125  return fmt::format("_leavealone");
126  }
127 };
128 
129 
130 // Helper struct which can determine if an embedded CPP object is compatible with
131 // type T as well as casting an object of type T to its most derived type
132 template<class T>
134 protected:
135  v8::Isolate * isolate;
136 
137  public:
138  TypeCheckerBase(v8::Isolate * isolate) : isolate(isolate) {}
139  virtual ~TypeCheckerBase(){}
140 
141  // returns nullptr if AnyBase cannot be converted to a compatible type
142  virtual T * check(AnyBase *, bool first_call = true
143  ) const = 0;
144 };
145 
146 template<class T>
148 protected:
149  v8::Isolate * isolate;
150 
151 public:
152  WrapAsMostDerivedBase(v8::Isolate * isolate) : isolate(isolate) {}
153  virtual ~WrapAsMostDerivedBase() = default;
154 
155  virtual v8::Local<v8::Object> operator()(T * cpp_object, DestructorBehavior & destructor_behavior) const = 0;
156 };
157 
158 // type to find the most derived type of an object and return a wrapped JavaScript object of that type
159 template<class, class, class = void>
161 
162 template<class T>
164  WrapAsMostDerived(v8::Isolate * isolate) : WrapAsMostDerivedBase<T>(isolate) {}
165  virtual v8::Local<v8::Object> operator()(T * cpp_object, DestructorBehavior & destructor_behavior) const override;
166 };
167 
168 
169 // specialization for when there is no const conflict
170 template<class T, class Head, class... Tail>
171 struct WrapAsMostDerived<T, TypeList<Head, Tail...>, std::enable_if_t<!std::is_const<T>::value || std::is_const<Head>::value>> : public WrapAsMostDerived<T, TypeList<Tail...>> {
172  using SUPER = WrapAsMostDerived<T, TypeList<Tail...>>;
173  WrapAsMostDerived(v8::Isolate * isolate) : SUPER(isolate) {}
174  virtual v8::Local<v8::Object> operator()(T * cpp_object, DestructorBehavior & destructor_behavior) const override;
175 };
176 
177 // specializaiton for when we have something const (T) and want something non-const (Head)
178 template<class T, class Head, class... Tail>
179 struct WrapAsMostDerived<T, TypeList<Head, Tail...>, std::enable_if_t<std::is_const<T>::value && !std::is_const<Head>::value>> : public WrapAsMostDerived<T, TypeList<Tail...>> {
180  using SUPER = WrapAsMostDerived<T, TypeList<Tail...>>;
181  WrapAsMostDerived(v8::Isolate * isolate) : SUPER(isolate) {}
182  virtual v8::Local<v8::Object> operator()(T * cpp_object, DestructorBehavior & destructor_behavior) const override;
183 };
184 
185 
186 
187 
188 // type to convert to, typelist of all types to check, sfinae helper type
189 template<class, class, class = void>
190 struct TypeChecker;
191 
192 
193 // Fallback when everything in the typelist has been tried
194 template<class T>
195  struct TypeChecker<T, TypeList<>> : public TypeCheckerBase<T>
196 {
197 
198  TypeChecker(v8::Isolate * isolate) : TypeCheckerBase<T>(isolate) {}
199  virtual T * check(AnyBase * any_base, bool first_call = true
200  ) const override {
201  ANYBASE_PRINT("Failed to find match for anybase ({}) with type string: {}", demangle<T>(), any_base->type_name);
202  return nullptr;
203  }
204 };
205 
206 
207 // Specialization for types that cannot possibly work -- casting const value to non-const return
208 template<class T, class Head, class... Tail>
209 struct TypeChecker<T, v8toolkit::TypeList<Head, Tail...>,
210  std::enable_if_t<!std::is_const<T>::value && std::is_const<Head>::value>> : public TypeChecker<T, TypeList<Tail...>> {
211 
212  using SUPER = TypeChecker<T, TypeList<Tail...>>;
213 
214  TypeChecker(v8::Isolate * isolate) : SUPER(isolate) {}
215 
216  virtual T * check(AnyBase * any_base, bool first_call = true) const override {
217  ANYBASE_PRINT("In Type Checker<{}> Const mismatch: {} (string: {})", demangle<T>(), demangle<Head>(), any_base->type_name);
218  if (dynamic_cast<AnyPtr<Head> *>(any_base) != nullptr) {
219  ANYBASE_PRINT("Not a match, but the value is a const version of goal type! {} vs {} Should you be casting to a const type instead?", demangle<T>(), demangle<Head>());
220  }
221  return SUPER::check(any_base);
222  }
223 
224 };
225 
226 
227 // tests an AnyBase * against a list of types compatible with T
228 // to see if the AnyBase is an Any<TypeList...> ihn
229 template<class T, class Head, class... Tail>
230 struct TypeChecker<T, v8toolkit::TypeList<Head, Tail...>,
231 
232  // if it's *not* the condition of the specialization above
233  std::enable_if_t<std::is_const<T>::value ||
234  !std::is_const<Head>::value>
235  > : public TypeChecker<T, TypeList<Tail...>> {
236 
237 
238  using SUPER = TypeChecker<T, TypeList<Tail...>>;
239  TypeChecker(v8::Isolate * isolate) : SUPER(isolate) {}
240 
241  virtual T * check(AnyBase * any_base, bool first_call = true) const override;
242 
243 };
244 
245 
246 
247 /**
248  * Stores a list of callbacks to clean up V8ClassWrapper objects when the associated isolate is destroyed.
249  * An isolate created after a previous isolate is destroyed may have the same address but a new wrapper must be
250  * created.
251  */
253 private:
254  std::map<v8::Isolate *, std::vector<func::function<void()>>> isolate_to_callback_map;
255 
256 public:
257  void add_callback(v8::Isolate * isolate, func::function<void()> callback) {
258  this->isolate_to_callback_map[isolate].push_back(callback);
259  };
260  void cleanup_isolate(v8::Isolate * isolate) {
261 // std::cerr << fmt::format("cleaning up isolate: {}", (void*)isolate) << std::endl;
262  for (auto & callback : this->isolate_to_callback_map[isolate]) {
263  callback();
264  }
265  this->isolate_to_callback_map.erase(isolate);
266  }
267 };
268 
270 
271 
272 
273 
274 
275  // Cannot make class wrappers for pointer or reference types
276 #define V8TOOLKIT_V8CLASSWRAPPER_NO_POINTER_NO_REFERENCE_SFINAE !std::is_pointer<T>::value && !std::is_reference<T>::value
277 
278 #define V8TOOLKIT_V8CLASSWRAPPER_FULL_TEMPLATE_SFINAE_PREFIX std::is_base_of<WrappedClassBase, T>::value
279 /**
280  * Allows user to specify a list of types to instantiate real V8ClassWrapper template for -- CastToNative/CastToJS will otherwise
281  * try to instantiate it for a very large number of types which can drastically slow down compilation.
282  * Setting an explicit value for this is NOT required - it is just a compile-time, compile-RAM (and maybe binary size) optimizatioan
283  */
284 
285 
286  // uncomment this to see the effects of generating the wrapper class on compile time (but won't actually run correctly)
287 // #define TEST_NO_REAL_WRAPPERS
288 
289 
290 #ifdef TEST_NO_REAL_WRAPPERS
291 
292 #define V8TOOLKIT_V8CLASSWRAPPER_USE_REAL_TEMPLATE_SFINAE std::enable_if_t<std::is_same<T, void>::value>
293 #define V8TOOLKIT_V8CLASSWRAPPER_USE_FAKE_TEMPLATE_SFINAE std::enable_if_t<!std::is_same<T, void>::value>
294 
295  #else
296 // Use the real V8ClassWrapper specialization if the class inherits from WrappedClassBase or is in the user-provided sfinae
297 #define V8TOOLKIT_V8CLASSWRAPPER_USE_REAL_TEMPLATE_SFINAE std::enable_if_t<(V8TOOLKIT_V8CLASSWRAPPER_NO_POINTER_NO_REFERENCE_SFINAE) && \
298  (V8TOOLKIT_V8CLASSWRAPPER_FULL_TEMPLATE_SFINAE_PREFIX)>
299 
300 // otherwise use the 'cheap' specialization
301 #define V8TOOLKIT_V8CLASSWRAPPER_USE_FAKE_TEMPLATE_SFINAE std::enable_if_t<(V8TOOLKIT_V8CLASSWRAPPER_NO_POINTER_NO_REFERENCE_SFINAE) && \
302  !(V8TOOLKIT_V8CLASSWRAPPER_FULL_TEMPLATE_SFINAE_PREFIX)>
303 #endif
304 
305 
306 
307 /**
308  * Constructor names already used, including things reserved by JavaScript like "Object" and "Number"
309  */
310 extern std::map<v8::Isolate *, std::vector<std::string>> used_constructor_name_list_map;
311 
312 
313 /**
314 * Provides a mechanism for creating javascript-ready objects from an arbitrary C++ class
315 * Can provide a JS constructor method or wrap objects created in another c++ function
316 *
317 * Const types should not be wrapped directly. Instead, a const version of a non-const type will
318 * automatically be created and populated with read-only members and any const-qualified method added
319 * to the non-const version.
320 *
321 * All members/methods must be added, then finalize() called, then any desired constructors may be created.
322 *
323 *
324 */
325 template<class T, class = void>
326 class V8ClassWrapper;
327 
328 template<class T>
330 {
331  using ConstT = std::add_const_t<T>;
332 
333 private:
334 
335  /*** TYPEDEFS ***/
336 
337  // Callback type to add members to an ObjectTemplate
339 
340  // Callback type to add a static method to an ObjectTemplate
342 
343  // Callback type for notifying when a property has been changed
344  using PropertyChangedCallback = func::function<void(v8::Isolate * isolate,
345  v8::Local<v8::Object> & self,
346  const std::string &,
347  const v8::Local<v8::Value> & value)>;
348 
349  // Callback type to add a FakeMethod to an ObjectTemplate
351 
352  // Callback type to add a method
354 
355 
356 
357  /*** DATA MEMBERS ***/
358 
359  // Callbacks for adding members to an ObjectTemplate
360  std::vector<AttributeAdder> member_adders;
361 
362  // Callbacks for adding static methods to an ObjectTemplate
363  std::vector<StaticMethodAdder> static_method_adders;
364 
365 
366  /// List of callbacks for when attributes change
367  std::vector<PropertyChangedCallback> property_changed_callbacks;
368 
369 
370  // stores callbacks to add calls to lambdas whos first parameter is of type T* and are automatically passed
371  // the "this" pointer before any javascript parameters are passed in
372  std::vector<FakeMethodAdder> fake_method_adders;
373 
374  /**
375  * Name that will be reported from JavaScript `typeof` function
376  */
377  std::string class_name = demangle<T>();
378 
379  /**
380  * List of names already in use for methods/static methods/accessors
381  * Used to make sure duplicate names aren't requested
382  */
383  std::vector<std::string> used_attribute_name_list;
384 
385  /**
386  * List of names already used for properties on the constructor function
387  */
388  std::vector<std::string> used_static_attribute_name_list;
389 
390 
391  /**
392  * Mapping between CPP object pointer and JavaScript object for CPP objects which have already been
393  * wrapped so the previous wrapped object can be returned.
394  * Note: this stops any objects from ever being deleted and can cause problems if another object of the same
395  * type is created at the same memory address.
396  */
397  MapT<T *, v8::Global<v8::Object>> existing_wrapped_objects;
398 
399  /**
400  * Isolate associated with this V8ClassWrapper
401  */
402  v8::Isolate * isolate;
403 
404  /**
405  * Wrapped classes are per-isolate, so this tracks each wrapped class/isolate tuple for later retrieval
406  */
407  static MapT<v8::Isolate *, V8ClassWrapper<T> *> isolate_to_wrapper_map;
408 
409 
410  // Stores a functor capable of converting compatible types into a <T> object
411  // do non-const type first, so if it's a match, we don't try converting the non-const type to const and hit a debug assertion
412  TypeCheckerBase<T> * type_checker = new TypeChecker<T, TypeList<std::remove_const_t<T>, std::add_const_t<T>>>(this->isolate); // std::unique_ptr adds too much compilation time
413  WrapAsMostDerivedBase<T> * wrap_as_most_derived_object = new WrapAsMostDerived<T, TypeList<>>(this->isolate); // std::unique_ptr adds too much compilation time
414 
415 
416 
417  /****** METHODS *******/
418 
419  /**
420  * Stores a function template with any methods from the parent already in place.
421  * Used as the prototype for any new object
422  */
423  v8::Global<v8::FunctionTemplate> global_parent_function_template;
424 
425  /**
426  * Have to store all the function templates this class wrapper has ever made so
427  * they can all be tried as parameters to v8::Object::GetInstanceFromPrototypeChain
428  */
429  std::vector<v8::Global<v8::FunctionTemplate>> this_class_function_templates;
430 
431 
432 
433  /**
434  * Forces user to state that all members/methods have been added before any
435  * instances of the wrapped object are created
436  */
437  bool finalized = false;
438 
439  /**
440  * Whether the type should try to determine the most derived type of a CPP object or just wrap it as the
441  * presented static type - takes longer to determine the most derived type, but may be necessary for having the
442  * appropriate attributes on the returned JS object
443  */
444  bool wrap_as_most_derived_flag = false;
445 
446  // Nothing may ever be removed from this vector, as things point into it
447  std::list<MethodAdderData> method_adders;
448 
449  // makes a single function to be run when the wrapping javascript object is called with ()
450  MethodAdderData callable_adder;
451 
453  v8::IndexedPropertyGetterCallback indexed_property_getter = nullptr;
454 
455  std::vector<FunctionTemplateCallback> function_template_callbacks;
456 
457 
458  /*** METHODS ***/
459 
460 
461 
462 
463 
464  V8ClassWrapper() = delete;
465  V8ClassWrapper(const V8ClassWrapper<T> &) = delete;
466  V8ClassWrapper(const V8ClassWrapper<T> &&) = delete;
467  V8ClassWrapper& operator=(const V8ClassWrapper<T> &) = delete;
468  V8ClassWrapper& operator=(const V8ClassWrapper<T> &&) = delete;
469 
470 
471  /**
472  * Private constructor that places the newly created object in the "singleton" map.
473  * Users of the library should call get_instance, not this constructor directly
474  * @param isolate isolate for which the class wrapper is for
475  */
476  V8ClassWrapper(v8::Isolate * isolate);
477 
478 
479 
480 
481  /**
482  * Calls registered callbacks when the specified property name is changed
483  * @param object JavaScript object on which the property is being changed
484  * @param property_name property name being changed
485  * @param value new value of property
486  */
487  void call_callbacks(v8::Local<v8::Object> object, const std::string & property_name, v8::Local<v8::Value> & value);
488 
489 
490  /**
491  * Checks to see if a name has already been used because the V8 error message for a duplicate name is not helpful
492  * @param name name to check
493  */
494  void check_if_name_used(const std::string & name);
495 
496 
497  /**
498  * static methods go on the constructor function, so it can have names which overlap with the per-instance object attributes
499  * @param name name of static method to check
500  */
501  void check_if_static_name_used(const std::string & name);
502 
503 
504  /**
505  * returns whether the name is already used at a global level
506  * @param name name to check
507  */
508  void check_if_constructor_name_used(std::string const &);
509 
510 
511  // function used to return the value of a C++ variable backing a javascript variable visible
512  // via the V8 SetAccessor method
513  template<auto member> // type being returned
514  static void _getter_helper(v8::Local<v8::Name> property,
515  v8::PropertyCallbackInfo<v8::Value> const & info) {
516 
517  auto isolate = info.GetIsolate();
518 
519  auto cpp_object = V8ClassWrapper<T>::get_instance(isolate).get_cpp_object(info.Holder());
520 
521  // add lvalue ref as to know not to delete the object if the JS object is garbage collected
522  info.GetReturnValue().Set(CastToJS<std::add_lvalue_reference_t<decltype(cpp_object->*member)>>()(isolate, cpp_object->*member));
523  }
524 
525 
526  /**
527  * Called when a JavaScript object's property is assigned to
528  * @param property property name
529  * @param value new value for property
530  * @param info general JavaScript state info
531  */
532  template<auto member/*, std::enable_if_t<std::is_copy_assignable_v<pointer_to_member_t<member>>> = 0*/>
533  static void _setter_helper(v8::Local<v8::Name> property,
534  v8::Local<v8::Value> value,
535  v8::PropertyCallbackInfo<void> const & info) {
536 
537 
538  auto isolate = info.GetIsolate();
539 
540  auto & wrapper = V8ClassWrapper<T>::get_instance(isolate);
541 
542 
543  T * cpp_object = wrapper.get_cpp_object(info.Holder());
544  using MemberT = std::remove_reference_t<decltype(cpp_object->*member)>;
545  static_assert(
546  std::is_copy_assignable_v<MemberT> ||
547  std::is_move_assignable_v<MemberT>, "Cannot add_member with a type that is not either copy or move assignable. Use add_member_readonly instead");
548 
549  // if it's copyable, then the assignment is pretty easy
550  if constexpr(is_wrapped_type_v<MemberT>)
551  {
552  if constexpr(std::is_copy_assignable_v<MemberT>)
553  {
554  cpp_object->*member = CastToNative<MemberT &>()(isolate, value);
555  }
556  // otherwise if it's a wrapped type and it's move assignable, if the object owns its memory, then try a
557  // move assignment
558  else if constexpr(std::is_move_assignable_v<MemberT>)
559  {
560 
561  auto object = check_value_is_object(value, demangle<MemberT>());
562  if (wrapper.does_object_own_memory(object)) {
563  cpp_object->*member = CastToNative<MemberT &&>()(isolate, value);
564  }
565  }
566  }
567  // for an unwrapped type, always try to make a copy and do a move assignment from it
568  else {
569  cpp_object->*member = CastToNative<MemberT>()(isolate, value);
570  }
571 
572 
573  // call any registered change callbacks
574  V8ClassWrapper<T>::get_instance(isolate).call_callbacks(info.Holder(), *v8::String::Utf8Value(property), value);
575  }
576 
577 
578 
579  // Helper for creating objects when "new MyClass" is called from javascript
580  template<typename DefaultArgsTupleType, typename ... CONSTRUCTOR_PARAMETER_TYPES>
581  static void v8_constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
582  auto isolate = info.GetIsolate();
583 
584  // default arguments are in tuple
585  DefaultArgsTupleType * default_args_tuple_ptr =
586  static_cast<DefaultArgsTupleType *>(v8::Local<v8::External>::Cast(info.Data())->Value());
587 
588  T * new_cpp_object = nullptr;
589  func::function<void(CONSTRUCTOR_PARAMETER_TYPES...)> constructor =
590  [&new_cpp_object](CONSTRUCTOR_PARAMETER_TYPES... info)->void{new_cpp_object = new T(std::forward<CONSTRUCTOR_PARAMETER_TYPES>(info)...);};
591 
592  // route any cpp exceptions through javascript
593  try {
595  info,
596  std::index_sequence_for<CONSTRUCTOR_PARAMETER_TYPES...>{},
597  DefaultArgsTupleType(*default_args_tuple_ptr));
598 
599  } catch(std::exception & e) {
600  isolate->ThrowException(v8::String::NewFromUtf8(isolate, e.what()));
601  return;
602  }
603 
604 
605  // if the object was created by calling new in javascript, it should be deleted when the garbage collector
606  // GC's the javascript object, there should be no c++ references to it
607  auto & deleter = *v8toolkit::V8ClassWrapper<T>::get_instance(isolate).destructor_behavior_delete;
608  initialize_new_js_object(isolate, info.This(), new_cpp_object, deleter);
609 
610  // // return the object to the javascript caller
611  info.GetReturnValue().Set(info.This());
612  }
613 
614  // takes a Data() parameter of a StdFunctionCallbackType lambda and calls it
615  // Useful because capturing lambdas don't have a traditional function pointer type
616  static void callback_helper(const v8::FunctionCallbackInfo<v8::Value>& args);
617 
618 
619 public:
620 
621  // these probably shouldn't be public, but they are for now
622  std::unique_ptr<DestructorBehavior> destructor_behavior_delete = std::make_unique<DestructorBehavior_Delete<T>>();
623  std::unique_ptr<DestructorBehavior> destructor_behavior_leave_alone = std::make_unique<DestructorBehavior_LeaveAlone>();
624 
625 
626  /**
627  * Returns the embedded C++ object in a JavaScript object
628  * @param object JavaScript object containing an embedded C++ object
629  * @return The embedded C++ object
630  */
631  T * get_cpp_object(v8::Local<v8::Object> object);
632 
633 
634  /**
635  * Experimental
636  * @param callback
637  */
638  void register_callback(PropertyChangedCallback callback);
639 
640  /**
641  * Object still has the memory, it just doesn't own it. It may or may not have owned it before, but now
642  * it definitely doesn't.
643  * @param object JS object to make sure no longer owns its internal CPP object
644  * @return the cpp object from inside the provided JS object
645  */
647 // std::cerr << fmt::format("Trying to release internal field memory") << std::endl;
648  auto wrap = v8::Local<v8::External>::Cast(object->GetInternalField(0));
649  WrappedData<T> *wrapped_data = static_cast<WrappedData<T> *>(wrap->Value());
650 
651  if (!this->does_object_own_memory(object)) {
652  throw CastException("Cannot V8ClassWrapper::release_internal_field_memory because the object does not own its own memory");
653  }
654 
655  if (!std::has_virtual_destructor<T>::value && dynamic_cast<AnyPtr<T> *>(wrapped_data->native_object) == nullptr) {
656 #ifdef ANYBASE_DEBUG
657  std::cerr << fmt::format("cached anybase type: {} vs T: {}", wrapped_data->native_object->type_name, demangle<T>()) << std::endl;
658 #endif
659  throw CastException("Tried to release internal field memory on the wrong type for a non-virtual-destructor type");
660  }
661 
662  wrapped_data->weak_callback_data->global.ClearWeak();
663 
664  // since the weak callback won't run to delete the SetWeakCallbackData memory, do that now
665  delete wrapped_data->weak_callback_data;
666  wrapped_data->weak_callback_data = nullptr;
667 
668  return V8ClassWrapper<T>::get_cpp_object(object);
669  }
670 
672  auto wrap = v8::Local<v8::External>::Cast(object->GetInternalField(0));
673  WrappedData<T> *wrapped_data = static_cast<WrappedData<T> *>(wrap->Value());
674 // std::cerr << fmt::format("Does object own memory? ptr: {}, weak callback data: {} type: {}, wrapped_data: {}", (void*) wrapped_data->original_pointer, (void*)wrapped_data->weak_callback_data, demangle<T>(), (void*)wrapped_data) << std::endl;
675  if (wrapped_data->weak_callback_data) {
676 // std::cerr << fmt::format("callback data destructive? {}", wrapped_data->weak_callback_data->destructive) << std::endl;
677  }
678  return wrapped_data->weak_callback_data != nullptr && wrapped_data->weak_callback_data->destructive;
679  }
680 
681  // Common tasks to do for any new js object regardless of how it is created
682  static void initialize_new_js_object(v8::Isolate * isolate,
683  v8::Local<v8::Object> js_object,
684  T * cpp_object,
685  DestructorBehavior const & destructor_behavior)
686  {
687 #ifdef V8_CLASS_WRAPPER_DEBUG
688  fprintf(stderr, "Initializing new js object for %s for v8::object at %p and cpp object at %p and any_ptr at %p\n", demangle<T>().c_str(), *js_object, cpp_object, (void*)any_ptr);
689 #endif
690  if (js_object->InternalFieldCount() == 0) {
691  fprintf(stderr, "Maybe you are calling a constructor without 'new'?");
692  }
693  assert(js_object->InternalFieldCount() >= 1);
694 
695 
696  auto weak_callback_data = v8toolkit::global_set_weak(
697  isolate,
698  js_object,
699  [isolate, cpp_object, &destructor_behavior](v8::WeakCallbackInfo<SetWeakCallbackData> const & info) {
700  destructor_behavior(isolate, cpp_object);
701  },
702  destructor_behavior.destructive()
703  );
704 
705 
706  WrappedData<T> * wrapped_data = new WrappedData<T>(cpp_object, weak_callback_data);
707 
708 #ifdef V8_CLASS_WRAPPER_DEBUG
709  fprintf(stderr, "inserting anyptr<%s>at address %p pointing to cpp object at %p\n", typeid(T).name(), wrapped_data->native_object, cpp_object);
710 #endif
711 
712 
713  js_object->SetInternalField(0, v8::External::New(isolate, wrapped_data));
714 
715  // tell V8 about the memory we allocated so it knows when to do garbage collection
716  isolate->AdjustAmountOfExternalAllocatedMemory(sizeof(T));
717 
718  }
719 
720 
721  /**
722  * Creates a new v8::FunctionTemplate capabale of creating wrapped T objects based on previously added methods and members.
723  * TODO: This needs to track all FunctionTemplates ever created so it can try to use them in GetInstanceByPrototypeChain
724  */
725  v8::Local<v8::FunctionTemplate> make_wrapping_function_template(v8::FunctionCallback callback = nullptr,
726  const v8::Local<v8::Value> & data = v8::Local<v8::Value>());
727 
728 
729 
730  /**
731  * Returns an existing constructor function template for the class/isolate OR creates one if none exist.
732  * This is to keep the number of constructor function templates as small as possible because looking up
733  * which one created an object takes linear time based on the number that exist
734  */
735  v8::Local<v8::FunctionTemplate> get_function_template();
736 
737 
738 
739  /**
740  * Check to see if an object can be converted to type T, else return nullptr
741  */
742  T * cast(AnyBase * any_base);
743 
744 
745  void init_instance_object_template(v8::Local<v8::ObjectTemplate> object_template);
746 
747  void init_prototype_object_template(v8::Local<v8::ObjectTemplate> object_template);
748 
749  void init_static_methods(v8::Local<v8::FunctionTemplate> constructor_function_template);
750 
751  // Any cleanup for when an isolate is destroyed goes in here
752  void isolate_about_to_be_destroyed(v8::Isolate * isolate) {
753 // std::cerr << fmt::format("wrapper<{}> for isolate {} being destroyed", this->class_name, (void*)isolate) << std::endl;
754 
755  // forces object to be re-created next time an instance is requested
756  isolate_to_wrapper_map.erase(isolate);
757 
758  // "global" names in the isolate also become available again
759  used_constructor_name_list_map.erase(isolate);
760  }
761 
762 
763  /**
764  * Returns a "singleton-per-isolate" instance of the V8ClassWrapper for the wrapped class type.
765  * For each isolate you need to add constructors/methods/members separately.
766  */
767  static V8ClassWrapper<T> & get_instance(v8::Isolate * isolate) {
768 
769  auto wrapper_find_result = isolate_to_wrapper_map.find(isolate);
770  if ( wrapper_find_result != isolate_to_wrapper_map.end()) {
771 
772  V8ClassWrapper<T> * wrapper = wrapper_find_result->second;
773  return *wrapper;
774  }
775  return *new V8ClassWrapper<T>(isolate);
776  }
777 
778 
779  /**
780  * Specify the name of the object which will be used for debugging statements as well as
781  * being the type returned from javascript typeof
782  */
783  void set_class_name(const std::string & name);
784 
785 
786 
787  /**
788  * Species other types that can be substituted for T when calling a function expecting T
789  * but T is not being passsed. Only available for classes derived from T.
790  * Every type is always compatible with itself and should not be specified here.
791  * Not calling this means that only T objects will be accepted for things that want a T.
792  * There is no automatic determination of inherited types by this library because I cannot
793  * figure out how.
794  * It's VERY important that any type marked as having this type as a parent be marked as
795  * being a compatible type.
796  */
797  template<class... CompatibleTypes>
798  std::enable_if_t<static_all_of<std::is_base_of<T,CompatibleTypes>::value...>::value>
800  {
801 
802  assert(!is_finalized());
803 
804  if (!std::is_const<T>::value) {
805  using ConstT = std::add_const_t<T>;
806  V8ClassWrapper<ConstT>::get_instance(isolate).template set_compatible_types<std::add_const_t<CompatibleTypes>...>();
807  }
808 
809  // Try to convert to T any of: T, non-const T, any explicit compatible types and their const versions
810  if (type_checker != nullptr) {
811  // smart pointers are too compile-time expensive
812  delete type_checker;
813  }
814  // TODO: EXPENSIVE
815  type_checker = new TypeChecker<T, TypeList<std::add_const_t<T>, std::remove_const_t<T>, CompatibleTypes..., std::add_const_t<CompatibleTypes>...>>(this->isolate);
816  if (this->wrap_as_most_derived_object != nullptr) {
817  delete this->wrap_as_most_derived_object;
818  }
819  // TODO: EXPENSIVE
820  this->wrap_as_most_derived_object = new WrapAsMostDerived<T, TypeList<CompatibleTypes...>>(this->isolate);
821  }
822 
823  // allows specifying a special deleter type for objects which need to be cleaned up in a specific way
824  template<template<class> class Deleter>
825  void set_deleter() {
826  assert(!this->finalized);
827  using ConstT = std::add_const_t<T>;
828  if (!std::is_const<T>::value) {
829  V8ClassWrapper<ConstT>::get_instance(this->isolate).template set_deleter<Deleter>();
830  }
831  this->destructor_behavior_delete = std::make_unique<Deleter<T>>();
832  }
833 
834 
835  /**
836  * This wrapped class will inherit all the methods from the parent type (and its parent...)
837  *
838  * It is VERY important that the type being marked as the parent type has this type set with
839  * set_compatible_types<>()
840  */
841  template<class ParentType>
842  std::enable_if_t<std::is_base_of<ParentType, T>::value>
844  assert(!is_finalized());
845  if (!std::is_const<T>::value) {
846  using ConstT = std::add_const_t<T>;
847  using ConstParent = std::add_const_t<ParentType>;
848  V8ClassWrapper<ConstT>::get_instance(isolate).template set_parent_type<ConstParent>();
849  }
850 
851 
852 
853  if (!V8ClassWrapper<ParentType>::get_instance(isolate).is_finalized()) {
854  fprintf(stderr, "Tried to set parent type of %s to unfinalized %s\n",
855  demangle<T>().c_str(), demangle<ParentType>().c_str());
856 
857  }
858  assert(V8ClassWrapper<ParentType>::get_instance(isolate).is_finalized());
859 // fprintf(stderr, "Setting parent of %s to %s\n", demangle<T>().c_str(), demangle<ParentType>().c_str());
860  ISOLATE_SCOPED_RUN(isolate);
861  global_parent_function_template =
862  v8::Global<v8::FunctionTemplate>(isolate,
863  V8ClassWrapper<ParentType>::get_instance(isolate).get_function_template());
864 
865  }
866 
867 
868  /**
869  * V8ClassWrapper objects shouldn't be deleted during the normal flow of your program unless the associated isolate
870  * is going away forever. Things will break otherwise as no additional objects will be able to be created
871  * even though V8 will still present the ability to your javascript (I think)
872  */
873  virtual ~V8ClassWrapper();
874 
875  /**
876  * Creates a javascript method with the specified name inside `parent_template` which, when called with the "new" keyword, will return
877  * a new object of this type.
878  */
879  template<typename ... CONSTRUCTOR_PARAMETER_TYPES, class DefaultArgsTuple = std::tuple<>>
880  void add_constructor(const std::string & js_constructor_name,
881  v8::Local<v8::ObjectTemplate> parent_template,
882  DefaultArgsTuple const & default_args = DefaultArgsTuple())
883  {
884  assert(((void)"Type must be finalized before calling add_constructor", this->finalized) == true);
885  check_if_constructor_name_used(js_constructor_name);
886 
887  auto constructor_template =
888  make_wrapping_function_template(&V8ClassWrapper<T>::template v8_constructor<DefaultArgsTuple, CONSTRUCTOR_PARAMETER_TYPES...>,
889  v8::External::New(this->isolate, new DefaultArgsTuple(std::move(default_args))));
890 
891  // Add the constructor function to the parent object template (often the global template)
892 // std::cerr << "Adding constructor to global with name: " << js_constructor_name << std::endl;
893  parent_template->Set(v8::String::NewFromUtf8(isolate, js_constructor_name.c_str()), constructor_template);
894  }
895 
896 
897  /**
898  * When you don't want a "constructor" but you still need something to attach the static method names to, use this
899  * instead of add_constructor
900  */
901  void expose_static_methods(const std::string & js_name,
902  v8::Local<v8::ObjectTemplate> parent_template) {
903  assert(((void)"Type must be finalized before calling expose_static_methods", this->finalized) == true);
904  check_if_constructor_name_used(js_name);
905 
906  if (global_name_conflicts(js_name)) {
907  throw V8Exception(this->isolate, "name conflicts with global names (bug: this ignores if your parent template isn't the global object)");
908  }
909 
910  auto non_constructor_template =
911  make_wrapping_function_template([](const v8::FunctionCallbackInfo<v8::Value>& args)->void{
912  throw V8Exception(args.GetIsolate(), "You cannot create an object of this type");
913  },
915 
916  // Add the constructor function to the parent object template (often the global template)
917 // std::cerr << "Adding static-method holder (non-constructor) to global with name: " << js_name << std::endl;
918  parent_template->Set(v8::String::NewFromUtf8(isolate, js_name.c_str()), non_constructor_template);
919  }
920 
921 
922  /**
923  * Used when wanting to return an object from a c++ function call back to javascript, or in conjunction with
924  * add_variable to give a javascript name to an existing c++ object
925  * \code{cpp}
926  * add_variable(context, context->GetGlobal(), "js_name", class_wrapper.wrap_existing_cpp_object(context, some_c++_object));
927  * \endcode
928  */
929  v8::Local<v8::Object> wrap_existing_cpp_object(v8::Local<v8::Context> context, T * existing_cpp_object, DestructorBehavior & destructor_behavior, bool force_wrap_this_type = false)
930  {
931  // TODO: Expensive - when combined with add_method -- maybe try and move this out of V8ClassWrapper?
932  auto isolate = this->isolate;
933  assert(existing_cpp_object != nullptr);
934 
935  // if it's not finalized, try to find an existing CastToJS conversion because it's not a wrapped class
936  //*** IF YOU ARE HERE LOOKING AT AN INFINITE RECURSION CHECK THE TYPE IS ACTUALLY WRAPPED ***
937  if (!this->is_finalized()) {
938  // fprintf(stderr, "wrap existing cpp object cast to js %s\n", typeid(T).name());
939 // return CastToJS<T>()(isolate, *existing_cpp_object).template As<v8::Object>();
940  throw CastException(fmt::format("Tried to wrap existing cpp object for a type that isn't finalized: {}", demangle<T>()));
941  }
942 
943 #ifdef V8_CLASS_WRAPPER_DEBUG
944  fprintf(stderr, "Wrapping existing c++ object %p in v8 wrapper this: %p isolate %p type: %s\n", existing_cpp_object, this, isolate, v8toolkit::demangle<T>().c_str());
945 #endif
946 
947  // if there's currently a javascript object wrapping this pointer, return that instead of making a new one
948  // This makes sure if the same object is returned multiple times, the javascript object is also the same
949  v8::Local<v8::Object> javascript_object;
950  if(this->existing_wrapped_objects.find(existing_cpp_object) != this->existing_wrapped_objects.end()) {
951 #ifdef V8_CLASS_WRAPPER_DEBUG
952  fprintf(stderr, "Found existing javascript object for c++ object %p - %s\n", existing_cpp_object, v8toolkit::demangle<T>().c_str());
953 #endif
954  javascript_object = v8::Local<v8::Object>::New(isolate, this->existing_wrapped_objects[existing_cpp_object]);
955 
956  } else {
957 
958  V8TOOLKIT_DEBUG("Creating new javascript object for c++ object %p - %s\n", existing_cpp_object, v8toolkit::demangle<T>().c_str());
959 
960  v8::Isolate::Scope is(isolate);
961  v8::Context::Scope cs(context);
962 
963 #ifdef V8TOOLKIT_BIDIRECTIONAL_ENABLED
964  // try to get the javascript object inside a JSWrapper birectional object if it is one
965  auto jswrapper_javascript_object = safe_get_javascript_object(existing_cpp_object);
966  if (!jswrapper_javascript_object.IsEmpty()) {
967  return jswrapper_javascript_object;
968  }
969 #endif
970 
971 
972  if (this->wrap_as_most_derived_flag && !force_wrap_this_type) {
973  javascript_object = this->wrap_as_most_derived(existing_cpp_object, destructor_behavior);
974  } else {
975  javascript_object = get_function_template()->GetFunction()->NewInstance();
976 
977  // fprintf(stderr, "New object is empty? %s\n", javascript_object.IsEmpty()?"yes":"no");
978  // fprintf(stderr, "Created new JS object to wrap existing C++ object. Internal field count: %d\n", javascript_object->InternalFieldCount());
979 
980  initialize_new_js_object(isolate, javascript_object, existing_cpp_object, destructor_behavior);
981 
982  this->existing_wrapped_objects.emplace(existing_cpp_object,
983  v8::Global<v8::Object>(isolate, javascript_object));
984 
985  V8TOOLKIT_DEBUG("Inserting new %s object into existing_wrapped_objects hash that is now of size: %d\n", typeid(T).name(), (int)this->existing_wrapped_objects.size());
986  V8TOOLKIT_DEBUG("Wrap existing cpp object returning object about to be cast to a value: %s - %s\n", *v8::String::Utf8Value(javascript_object), v8toolkit::demangle<T>().c_str());
987  }
988  }
989  return javascript_object;
990  }
991 
992 
993 
994 
995  template<class R, class... Params, class DefaultArgs = std::tuple<>>
996  void add_static_method(const std::string & method_name, R(*callable)(Params...), DefaultArgs const & default_args_tuple = DefaultArgs{}) {
997  static std::vector<std::string> reserved_names = {"arguments", "arity", "caller", "displayName",
998  "length", "name", "prototype"};
999 
1000  if (std::find(reserved_names.begin(), reserved_names.end(), method_name) != reserved_names.end()) {
1001  throw InvalidCallException(fmt::format("The name: '{}' is a reserved property in javascript functions, so it cannot be used as a static method name", method_name));
1002  }
1003 
1004  if (!std::is_const<T>::value) {
1005  V8ClassWrapper<ConstT>::get_instance(isolate).add_static_method(method_name, callable);
1006  }
1007 
1008  // must be set before finalization
1009  assert(!this->finalized);
1010 
1011  this->check_if_static_name_used(method_name);
1012 
1013 
1014  auto static_method_adder = [this, method_name, callable](v8::Local<v8::FunctionTemplate> constructor_function_template) {
1015 
1016  auto static_method_function_template = v8toolkit::make_function_template(this->isolate,
1017  callable, method_name);
1018 // fprintf(stderr, "Adding static method %s onto %p for %s\n", method_name.c_str(), &constructor_function_template, this->class_name.c_str());
1019  constructor_function_template->Set(this->isolate,
1020  method_name.c_str(),
1021  static_method_function_template);
1022  };
1023 
1024  this->static_method_adders.emplace_back(static_method_adder);
1025  };
1026 
1027 
1028 
1029  template<class Callable, class DefaultArgs = std::tuple<>>
1030  void add_static_method(const std::string & method_name, Callable callable, DefaultArgs const & default_args_tuple = DefaultArgs{}) {
1031  if (!std::is_const<T>::value) {
1032  V8ClassWrapper<ConstT>::get_instance(isolate).add_static_method(method_name, callable);
1033  }
1034 
1035  // must be set before finalization
1036  assert(!this->finalized);
1037 
1038  this->check_if_static_name_used(method_name);
1039 
1040  auto static_method_adder = [this, method_name, callable](v8::Local<v8::FunctionTemplate> constructor_function_template) {
1041 
1042  auto static_method_function_template = v8toolkit::make_function_template(this->isolate,
1043  callable, method_name);
1044 
1045 // fprintf(stderr, "Adding static method %s onto %p for %s\n", method_name.c_str(), &constructor_function_template, this->class_name.c_str());
1046  constructor_function_template->Set(this->isolate,
1047  method_name.c_str(),
1048  static_method_function_template);
1049  };
1050 
1051  this->static_method_adders.emplace_back(static_method_adder);
1052  }
1053 
1054 
1055 
1056 
1057 
1058  /**
1059  * Function to force API user to declare that all members/methods have been added before any
1060  * objects of the wrapped type can be created to make sure everything stays consistent
1061  * Must be called before adding any constructors or using wrap_existing_object()
1062  */
1063  void finalize(bool wrap_as_most_derived = false);
1064 
1065  /**
1066  * returns whether finalize() has been called on this type for this isolate
1067  */
1068  bool is_finalized() {
1069  return this->finalized;
1070  }
1071 
1072 
1073 
1074  /**
1075  * Adds a getter and setter method for the specified class member
1076  * add_member(&ClassName::member_name, "javascript_attribute_name");
1077  */
1078  template<class MemberType,
1079  class MemberClass,
1080  MemberType (MemberClass::*member)>
1081  void add_member(std::string const & member_name) {
1082  this->add_member<member>(member_name);
1083  }
1084 
1085 
1086  template<auto member>
1087  void add_member(std::string const & member_name) {
1088  assert(this->finalized == false);
1089 
1090 
1091 
1092  // This function could forward the request to add_member_readonly instead, but having it error instead makes the
1093  // caller be clear that they understand it's a const type. If it turns out this is really annoying, it can be changed
1094  static_assert(!is_pointer_to_const_data_member_v<member>, "Cannot V8ClassWrapper::add_member a const data member. Use add_member_readonly instead");
1095 
1096  if constexpr(!std::is_const_v<T>) {
1098  template add_member_readonly<member>(member_name);
1099  }
1100 
1101  this->check_if_name_used(member_name);
1102 
1103  // store a function for adding the member on to an object template in the future
1104  member_adders.emplace_back([this, member_name](v8::Local<v8::ObjectTemplate> & constructor_template){
1105 
1106 
1107  constructor_template->SetAccessor(v8::Local<v8::Name>::Cast(v8::String::NewFromUtf8(isolate, member_name.c_str())),
1108  _getter_helper<member>,
1109  _setter_helper<member>);
1110  });
1111  }
1112 
1113 
1114  template<class MemberType,
1115  class MemberClass, // allow members from parent types of T
1116  MemberType (MemberClass::*member),
1117  std::enable_if_t<std::is_base_of<MemberClass, T>::value, int> = 0>
1118  void add_member_readonly(std::string const & member_name) {
1119  this->add_member_readonly<member>(member_name);
1120  }
1121 
1122  template<auto member>
1123  void add_member_readonly(std::string const & member_name) {
1124 
1125  // the field may be added read-only even to a non-const type, so make sure it's added to the const type, too
1126  if (!std::is_const<T>::value) {
1127  V8ClassWrapper<ConstT>::get_instance(isolate).template add_member_readonly<member>(member_name);
1128  }
1129 
1130  assert(this->finalized == false);
1131 
1132  this->check_if_name_used(member_name);
1133 
1134  member_adders.emplace_back([this, member_name](v8::Local<v8::ObjectTemplate> & constructor_template){
1135 
1136  constructor_template->SetAccessor(v8::String::NewFromUtf8(isolate, member_name.c_str()),
1137  _getter_helper<member>,
1138  0);
1139  });
1140  }
1141 
1142 
1143  /**
1144  * The specified function will be called when the JavaScript object is called like a function
1145  * @param method function to call
1146  */
1147  template<class R, class TBase, class... Args,
1148  std::enable_if_t<std::is_base_of<TBase, T>::value, int> = 0>
1149  void make_callable(R(TBase::*method)(Args...))
1150  {
1151  _add_method("unused name", method, TypeList<Args...>(), std::tuple<>(), true);
1152  }
1153 
1154 
1155 
1156  /**
1157  * Adds const-qualified member instance functions
1158  * @param method_name JavaScript property name to use
1159  * @param method member instance function pointer
1160  * @param default_args any default arguments to be used if insufficient JavaScript arguments provided
1161  */
1162  template<class R, class TBase, class... Args, class DefaultArgs = std::tuple<>,
1163  std::enable_if_t<std::is_base_of<TBase, T>::value, int> = 0>
1164  void add_method(const std::string & method_name, R(TBase::*method)(Args...) const, DefaultArgs const & default_args = DefaultArgs()) {
1165  if (!std::is_const<T>::value) {
1166  V8ClassWrapper<std::add_const_t<T>>::get_instance(isolate)._add_method(method_name, method, TypeList<Args...>(), default_args);
1167  }
1168  _add_method(method_name, method, TypeList<Args...>(), default_args);
1169  }
1170 
1171  /**
1172  * Adds const-and-lvalue-qualified member instance functions
1173  * @param method_name JavaScript property name to use
1174  * @param method member instance function pointer
1175  * @param default_args any default arguments to be used if insufficient JavaScript arguments provided
1176  */
1177  template<class R, class TBase, class... Args, class DefaultArgs = std::tuple<>,
1178  std::enable_if_t<std::is_base_of<TBase, T>::value, int> = 0>
1179  void add_method(const std::string & method_name, R(TBase::*method)(Args...) const &, DefaultArgs const & default_args = DefaultArgs()) {
1180  if (!std::is_const<T>::value) {
1181  V8ClassWrapper<std::add_const_t<T>>::get_instance(isolate)._add_method(method_name, method, TypeList<Args...>(), default_args);
1182  }
1183  _add_method(method_name, method, TypeList<Args...>(), default_args);
1184  }
1185 
1186 
1187  /**
1188  * const rvalue instance functions not supported yet. Using this triggers a static assertion failure
1189  * @param method_name ignored
1190  * @param method member ignored
1191  * @param default_args ignored
1192  */
1193  template<class R, class TBase, class... Args, class DefaultArgs = std::tuple<>,
1194  std::enable_if_t<std::is_base_of<TBase, T>::value, int> = 0>
1195  void add_method(const std::string & method_name, R(TBase::*method)(Args...) const &&, DefaultArgs const & default_args = DefaultArgs()) {
1196  static_assert(std::is_same<R, void>::value && !std::is_same<R, void>::value, "not supported");
1197  }
1198 
1199 
1200  /**
1201  * Adds const-and-lvalue-qualified member instance functions
1202  * @param method_name JavaScript property name to use
1203  * @param method member instance function pointer
1204  * @param default_args any default arguments to be used if insufficient JavaScript arguments provided
1205  */
1206  template<class R, class TBase, class... Args, class DefaultArgs = std::tuple<>,
1207  std::enable_if_t<std::is_base_of<TBase, T>::value, int> = 0>
1208  void add_method(const std::string & method_name, R(TBase::*method)(Args...), DefaultArgs const & default_args = DefaultArgs())
1209  {
1210  _add_method(method_name, method, TypeList<Args...>(), default_args);
1211  }
1212 
1213  /**
1214  * Adds lvalue-qualified member instance functions
1215  * @param method_name JavaScript property name to use
1216  * @param method member instance function pointer
1217  * @param default_args any default arguments to be used if insufficient JavaScript arguments provided
1218  */
1219  template<class R, class TBase, class... Args, class DefaultArgs = std::tuple<>,
1220  std::enable_if_t<std::is_base_of<TBase, T>::value, int> = 0>
1221  void add_method(const std::string & method_name, R(TBase::*method)(Args...) &, DefaultArgs const & default_args = DefaultArgs())
1222  {
1223  _add_method(method_name, method, TypeList<Args...>(), default_args);
1224  }
1225 
1226 
1227  /**
1228  * rvalue instance functions not supported yet. Using this triggers a static assertion failure
1229  * @param method_name ignored
1230  * @param method member ignored
1231  * @param default_args ignored
1232  */
1233  template<class R, class TBase, class... Args, class DefaultArgs = std::tuple<>,
1234  std::enable_if_t<std::is_base_of<TBase, T>::value, int> = 0>
1235  void add_method(const std::string & method_name, R(TBase::*method)(Args...) &&, DefaultArgs const & default_args = DefaultArgs())
1236  {
1237  static_assert(std::is_same<R, void>::value && !std::is_same<R, void>::value, "not supported");
1238  }
1239 
1240 
1241 
1242 
1243  /**
1244  * If the method is marked const, add it to the const version of the wrapped type
1245  */
1246  template<class R, class Head, class... Tail, class DefaultArgs = std::tuple<>,
1247  std::enable_if_t<std::is_const<Head>::value && !std::is_const<T>::value, int> = 0>
1248  void add_fake_method_for_const_type(const std::string & method_name, func::function<R(Head *, Tail...)> method,
1249  DefaultArgs const & default_args = DefaultArgs()) {
1250  V8ClassWrapper<ConstT>::get_instance(isolate)._add_fake_method(method_name, method, default_args);
1251  };
1252 
1253 
1254  /**
1255  * If the method is not marked const, don't add it to the const type (since it's incompatible)
1256  */
1257  template<class R, class Head, class... Tail, class DefaultArgs = std::tuple<>,
1258  std::enable_if_t<!(std::is_const<Head>::value && !std::is_const<T>::value), int> = 0>
1259  void add_fake_method_for_const_type(const std::string & method_name, func::function<R(Head *, Tail...)> method,
1260  DefaultArgs const & default_args = DefaultArgs()) {
1261  // nothing to do here
1262  };
1263 
1264 
1265  /**
1266  * Creates a "fake" method from any callable which takes a T* as its first parameter. Does not create
1267  * the method on the const type
1268  * @param method_name JavaScript name to expose this method as
1269  * @param method method to call when JavaScript function invoked
1270  */
1271  template<class R, class Head, class... Args, class DefaultArgs = std::tuple<>,
1272  std::enable_if_t<std::is_pointer<Head>::value && // Head must be T * or T const *
1273  std::is_same<std::remove_const_t<std::remove_pointer_t<Head>>, T>::value, int> = 0>
1274  void add_method(const std::string & method_name, func::function<R(Head, Args...)> & method,
1275  DefaultArgs const & default_args = DefaultArgs()) {
1276  _add_fake_method(method_name, method, default_args);
1277  }
1278 
1279 
1280  /**
1281  * Creates a "fake" method from any callable which takes a T* as its first parameter. Does not create
1282  * the method on the const type
1283  * @param method_name JavaScript name to expose this method as
1284  * @param method method to call when JavaScript function invoked
1285  */
1286  template<class R, class Head, class... Args, class DefaultArgs = std::tuple<>,
1287  std::enable_if_t<std::is_pointer<Head>::value &&
1288  std::is_base_of<std::remove_const_t<std::remove_pointer_t<Head>>, T>::value, int> = 0>
1289  void add_method(const std::string & method_name, R(*method)(Head, Args...),
1290  DefaultArgs const & default_args = DefaultArgs()) {
1291 
1292  _add_fake_method(method_name, func::function<R(Head, Args...)>(method), default_args);
1293  }
1294 
1295 
1296  /**
1297  * Takes a lambda taking a T* as its first parameter and creates a 'fake method' with it
1298  */
1299  template<class Callback, class DefaultArgs = std::tuple<> >
1300  void add_method(const std::string & method_name, Callback && callback,
1301  DefaultArgs const & default_args = DefaultArgs()) {
1302  decltype(LTG<Callback>::go(&Callback::operator())) f(callback);
1303  this->_add_fake_method(method_name, f, default_args);
1304 
1305  }
1306 
1307 
1308  v8::Local<v8::Object> wrap_as_most_derived(T * cpp_object, DestructorBehavior & destructor_behavior) {
1309  return this->wrap_as_most_derived_object->operator()(cpp_object, destructor_behavior);
1310  }
1311 
1312 
1313 
1314  template<class R, class Head, class... Tail, class DefaultArgsTuple,
1315  std::enable_if_t<std::is_pointer<Head>::value && // Head must be T * or T const *
1316  std::is_same<std::remove_const_t<std::remove_pointer_t<Head>>, std::remove_const_t<T>>::value, int> = 0>
1317  void _add_fake_method(const std::string & method_name, func::function<R(Head, Tail...)> method, DefaultArgsTuple const & default_args)
1318  {
1319  assert(this->finalized == false);
1320 
1321  // conditionally add the method to the const type
1322  add_fake_method_for_const_type(method_name, method);
1323 
1324  this->check_if_name_used(method_name);
1325 
1326 
1327  // This puts a function on a list that creates a new v8::FunctionTemplate and maps it to "method_name" on the
1328  // Object template that will be passed in later when the list is traversed
1329  fake_method_adders.emplace_back([this, default_args, method_name, method](v8::Local<v8::ObjectTemplate> prototype_template) {
1330 
1331  using CopyFunctionType = func::function<R(Head, Tail...)>;
1332  CopyFunctionType * copy = new func::function<R(Head, Tail...)>(method);
1333 
1334 
1335  // This is the actual code associated with "method_name" and called when javascript calls the method
1336  StdFunctionCallbackType * method_caller =
1337  new StdFunctionCallbackType([method_name, default_args, copy](const v8::FunctionCallbackInfo<v8::Value>& info) {
1338 
1339 
1340  auto fake_method = *(func::function<R(Head, Tail...)>*)v8::External::Cast(*(info.Data()))->Value();
1341  auto isolate = info.GetIsolate();
1342 
1343  auto holder = info.Holder();
1344 
1345  // it should not be 0, and right now there is no known way for it being more than 1.
1346  assert(holder->InternalFieldCount() == 1);
1347 
1348  // a crash here may have something to do with a native override of toString
1349  auto cpp_object = V8ClassWrapper<T>::get_instance(isolate).get_cpp_object(info.Holder());
1350 
1351 
1352  // V8 does not support C++ exceptions, so all exceptions must be caught before control
1353  // is returned to V8 or the program will instantly terminate
1354  try {
1355  CallCallable<CopyFunctionType, Head>()(*copy, info, cpp_object, std::index_sequence_for<Tail...>(), default_args); // just Tail..., not Head, Tail...
1356  } catch(std::exception & e) {
1357  isolate->ThrowException(v8::String::NewFromUtf8(isolate, e.what()));
1358  return;
1359  }
1360  return;
1361  });
1362 
1363  // create a function template, set the lambda created above to be the handler
1364  auto function_template = v8::FunctionTemplate::New(this->isolate);
1365  function_template->SetCallHandler(callback_helper, v8::External::New(this->isolate, method_caller));
1366 
1367  // methods are put into the protype of the newly created javascript object
1368  prototype_template->Set(v8::String::NewFromUtf8(isolate, method_name.c_str()), function_template);
1369  });
1370  }
1371 
1372  /**
1373  * A list of methods to be added to each object
1374  */
1375 
1376 
1377 
1378 
1379  template<class M, class... Args, class... DefaultArgTypes>
1380  void _add_method(const std::string & method_name,
1381  M method,
1382  TypeList<Args...> const &,
1383  std::tuple<DefaultArgTypes...> const & default_args_tuple,
1384  bool add_as_callable_object_callback = false)
1385  {
1386  // TODO: EXPENSIVE - when combined with
1387  assert(this->finalized == false);
1388 
1389  this->check_if_name_used(method_name);
1390 
1391  MethodAdderData method_adder_data = MethodAdderData{method_name,
1392  StdFunctionCallbackType([this, default_args_tuple, method, method_name](const v8::FunctionCallbackInfo<v8::Value>& info) {
1393  auto isolate = info.GetIsolate();
1394 
1395  // get the behind-the-scenes c++ object
1396  // However, Holder() refers to the most-derived object, so the prototype chain must be
1397  // inspected to find the appropriate v8::Object with the T* in its internal field
1398  auto holder = info.Holder();
1399  v8::Local<v8::Object> self;
1400 
1401 #ifdef V8_CLASS_WRAPPER_DEBUG
1402  fprintf(stderr, "Looking for instance match in prototype chain %s :: %s\n", demangle<T>().c_str(),
1403  demangle<M>().c_str());
1404  fprintf(stderr, "Holder: %s\n", stringify_value(isolate, holder).c_str());
1405  dump_prototypes(isolate, holder);
1406 #endif
1407 
1408  auto function_template_count = this->this_class_function_templates.size();
1409  int current_template_count = 0;
1410  for (auto &function_template : this->this_class_function_templates) {
1411  current_template_count++;
1412  V8TOOLKIT_DEBUG("Checking function template %d / %d\n", current_template_count,
1413  (int) function_template_count);
1414  self = holder->FindInstanceInPrototypeChain(function_template.Get(isolate));
1415  if (!self.IsEmpty() && !self->IsNull()) {
1416  V8TOOLKIT_DEBUG("Found instance match in prototype chain\n");
1417  break;
1418  } else {
1419  V8TOOLKIT_DEBUG("No match on this one\n");
1420  }
1421  }
1422  if (self.IsEmpty()) {
1423  V8TOOLKIT_DEBUG("No match in prototype chain after looking through all potential function templates\n");
1424  assert(false);
1425  }
1426 
1427 
1428  // void* pointer = instance->GetAlignedPointerFromInternalField(0);
1429  auto wrap = v8::Local<v8::External>::Cast(self->GetInternalField(0));
1430 
1431 // if (V8_CLASS_WRAPPER_DEBUG) fprintf(stderr, "uncasted internal field: %p\n", wrap->Value());
1432  WrappedData<T> *wrapped_data = static_cast<WrappedData<T> *>(wrap->Value());
1433  auto backing_object_pointer = V8ClassWrapper<T>::get_instance(isolate).cast(
1434  wrapped_data->native_object);
1435 
1436 // assert(backing_object_pointer != nullptr);
1437  // bind the object and method into a func::function then build the parameters for it and call it
1438 // if (V8_CLASS_WRAPPER_DEBUG) fprintf(stderr, "binding with object %p\n", backing_object_pointer);
1439  auto bound_method = v8toolkit::bind<T>(*backing_object_pointer, method);
1440 
1441 
1442 
1443  // V8 does not support C++ exceptions, so all exceptions must be caught before control
1444  // is returned to V8 or the program will instantly terminate
1445  try {
1446  // make a copy of default_args_tuple so it's non-const - probably better to do this on a per-parameter basis
1447  CallCallable<decltype(bound_method)>()(bound_method, info, std::index_sequence_for<Args...>{}, std::tuple<DefaultArgTypes...>(default_args_tuple));
1448  } catch (std::exception &e) {
1449  isolate->ThrowException(v8::String::NewFromUtf8(isolate, e.what()));
1450  return;
1451  }
1452  return;
1453  })};
1454 
1455  if (add_as_callable_object_callback) {
1456  // can only set this once
1457  assert(!callable_adder.callback);
1458  callable_adder = method_adder_data;
1459  } else {
1460  method_adders.push_back(method_adder_data);
1461  }
1462  }
1463 
1464  /**
1465  * http://v8.paulfryzel.com/docs/master/classv8_1_1_object_template.html#ae3303f3d55370684ac02b02e67712eac
1466  * This version hasn't been enhanced yet like the named property version has.
1467  * Sets a callback when some_object[4] is called
1468  * @param callable function to be called
1469  */
1470  void add_index_getter(v8::IndexedPropertyGetterCallback getter) {
1471  // can only set one per class type
1472  assert(indexed_property_getter == nullptr);
1473  indexed_property_getter = getter;
1474  }
1475 
1476 
1477  struct NamedPropertyCallbackData {
1478  T * cpp_object = nullptr;
1480  v8::PropertyCallbackInfo<v8::Value> const &)> getter;
1482  v8::Local<v8::Value> new_property_value,
1483  v8::PropertyCallbackInfo<v8::Value> const &)> setter;
1484  };
1485 
1486  template<class ReturnT>
1488 
1489  template<class ReturnT>
1491 
1492 
1493 
1494 
1495  class NoResultAvailable : public std::exception {
1496  public:
1497  virtual const char * what() const noexcept override {return "";}
1498  };
1499 
1500 
1501  /* Helper for handling pointer return types from named property getter
1502  * allows checking for null pointers
1503  */
1504  template<class ResultT>
1505  std::enable_if_t<std::is_pointer<ResultT>::value, void> handle_getter_result(ResultT&& result,
1506  v8::PropertyCallbackInfo<v8::Value> const &info) {
1507  if (result != nullptr) {
1508  info.GetReturnValue().Set(CastToJS<ResultT>()(info.GetIsolate(), std::forward<ResultT>(result)));
1509  }
1510  }
1511 
1512  /* Helper for handling non-pointer return types from named property getter */
1513  template<class ResultT>
1514  std::enable_if_t<!std::is_pointer<ResultT>::value, void> handle_getter_result(ResultT&& result,
1515  v8::PropertyCallbackInfo<v8::Value> const &info) {
1516 
1517  info.GetReturnValue().Set(CastToJS<ResultT>()(info.GetIsolate(), std::move(result)));
1518 
1519  }
1520 
1521 #if 0
1522  /**
1523  * http://v8.paulfryzel.com/docs/master/classv8_1_1_object_template.html#a66fa7b04c87676e20e35497ea09a0ad0
1524  * Returning either a nullptr or throwing NoResultAvailable exception means the value was not found
1525  * @param callback function to be called
1526  */
1527  template<class GetterReturnT, class SetterReturnT>
1528  void add_named_property_handler(NamedPropertyGetter<GetterReturnT> getter_callback,
1529  NamedPropertySetter<SetterReturnT> setter_callback) {
1530  assert(!this->finalized);
1531  assert(!this->named_property_adder);
1532  named_property_adder = [this, getter_callback, setter_callback](v8::Local<v8::ObjectTemplate> object_template) {
1533 
1534  auto data = new NamedPropertyCallbackData();
1535 
1536  if (getter_callback) {
1537  data->getter = [this, getter_callback](v8::Local<v8::Name> property_name,
1538  v8::PropertyCallbackInfo<v8::Value> const &info) {
1539  // don't know how to handle symbols - can't find a way to stringify them
1540  if (property_name->IsSymbol()) {
1541  printf("symbol name: %s\n", *v8::String::Utf8Value(v8::Local<v8::Symbol>::Cast(property_name)->Name()));
1542  return;
1543  }
1544  T *cpp_object = v8toolkit::V8ClassWrapper<T>::get_cpp_object(info.This());
1545 
1546  try {
1547  auto str = *v8::String::Utf8Value(property_name);
1548  assert(str);
1549  handle_getter_result(getter_callback(cpp_object, str), info);
1550  return;
1551  } catch(NoResultAvailable) {
1552  return;
1553  }
1554  };
1555  }
1556  if (setter_callback) {
1557  data->setter = [this, setter_callback](v8::Local<v8::Name> property_name,
1558  v8::Local<v8::Value> new_property_value,
1559  v8::PropertyCallbackInfo<v8::Value> const &info) {
1560 
1561  T *cpp_object = v8toolkit::V8ClassWrapper<T>::get_cpp_object(info.This());
1562  setter_callback(cpp_object, *v8::String::Utf8Value(property_name)) =
1564  new_property_value);
1565  info.GetReturnValue().Set(true);
1566  };
1567  }
1568 
1569 
1570  object_template->SetHandler(v8::NamedPropertyHandlerConfiguration(
1571  // Getter
1572  [](v8::Local<v8::Name> property_name,
1573  v8::PropertyCallbackInfo<v8::Value> const & info){
1574  auto external_data = v8::External::Cast(*info.Data());
1575  NamedPropertyCallbackData * data = static_cast<NamedPropertyCallbackData *>(external_data->Value());
1576  data->getter(property_name, info);
1577  },
1578  // setter
1579  [](v8::Local<v8::Name> property_name,
1580  v8::Local<v8::Value> new_property_value,
1581  v8::PropertyCallbackInfo<v8::Value> const & info){
1582  auto external_data = v8::External::Cast(*info.Data());
1583  NamedPropertyCallbackData * data = static_cast<NamedPropertyCallbackData *>(external_data->Value());
1584  data->setter(property_name, new_property_value, info);
1585  },
1586  // query - returns attributes on the given property name
1587  // http://brendanashworth.github.io/v8-docs/namespacev8.html#a05f25f935e108a1ea2d150e274602b87
1588  [](v8::Local< v8::Name > property_name, v8::PropertyCallbackInfo< v8::Integer> const & info){
1589  printf("In query callback %s\n", *v8::String::Utf8Value(property_name));
1590  },
1591  // deleter
1592  [](v8::Local<v8::Name> property_name,
1593  v8::PropertyCallbackInfo<v8::Boolean> const & info){
1594  printf("IN DELETER CALLBACK %s\n", *v8::String::Utf8Value(property_name));
1595  },
1596  // enumerator
1597  [](v8::PropertyCallbackInfo<v8::Array> const & info) {
1598  printf("IN ENUMERATOR CALLBACK\n");
1599  info.GetReturnValue().Set(v8::Array::New(info.GetIsolate()));
1600  },
1601  v8::External::New(this->isolate, (void *)data),
1602  v8::PropertyHandlerFlags::kNonMasking // don't call on properties that exist
1603 // v8::PropertyHandlerFlags::kNone // call on everything (doesn't work well with added methods/members)
1604  ));
1605  };
1606  }
1607 
1608 
1609  /**
1610  * This version accepts a &T::operator[] directly -- does the std::bind for you
1611  * @param callback T instance member function to be called
1612  */
1613  template<class GetterReturnT, class GetterObjectT, class SetterReturnT, class SetterObjectT>
1614  void add_named_property_handler(GetterReturnT(GetterObjectT::*getter_callback)(std::string const &) const,
1615  SetterReturnT(SetterObjectT::*setter_callback)(std::string const &)) {
1618  if (getter_callback != nullptr) {
1619  bound_getter = std::bind(getter_callback, std::placeholders::_1, std::placeholders::_2);
1620  }
1621  if (setter_callback != nullptr) {
1622  bound_setter = std::bind(setter_callback, std::placeholders::_1, std::placeholders::_2);
1623  }
1624 
1625  this->add_named_property_handler(bound_getter, bound_setter);
1626  }
1627 
1628 #endif
1629 
1630  /**
1631  * ADVANCED: allows direct customization of the v8::FunctionTemplate used to create objects
1632  * Use for anything this library doesn't take care of
1633  */
1635  assert(!this->finalized);
1636  function_template_callbacks.push_back(callback);
1637  }
1638 
1639 };
1640 
1641 } // end v8toolkit namespace
1642 
1643 #ifndef V8TOOLKIT_WRAPPER_FAST_COMPILE
1644 #include "v8_class_wrapper_impl.h"
1645 #endif
1646 
1647 namespace v8toolkit {
1648 
1649 /**
1650 * Stores the "singleton" per isolate
1651 */
1652 template <class T>
1654 
1655 template<class T>
1656 class JSWrapper;
1657 
1658 
1659 
1660 
1661 template<class T>
1662 struct CastToJS<T, std::enable_if_t<is_wrapped_type_v<T>>> {
1663 
1664  // An lvalue is presented, so the memory will not be cleaned up by JavaScript
1665  v8::Local<v8::Value> operator()(v8::Isolate * isolate, T & cpp_object) {
1666  auto & wrapper = V8ClassWrapper<T>::get_instance(isolate);
1667  return wrapper.wrap_existing_cpp_object(isolate->GetCurrentContext(), &cpp_object,
1668  *wrapper.destructor_behavior_leave_alone);
1669  }
1670 
1671  // An rvalue is presented, so move construct a new object from the presented data
1672  v8::Local<v8::Value> operator()(v8::Isolate * isolate, T && cpp_object) {
1673  auto & wrapper = V8ClassWrapper<T>::get_instance(isolate);
1674  return wrapper.wrap_existing_cpp_object(isolate->GetCurrentContext(), new T(std::move(cpp_object)),
1675  *wrapper.destructor_behavior_delete);
1676  }
1677 };
1678 
1679 
1680 /**
1681  * CastToNative a std::unique_ptr to a wrapped type
1682  */
1683 template<class T, class... Rest>
1684 struct CastToJS<std::unique_ptr<T, Rest...>, std::enable_if_t<is_wrapped_type_v<T>>> {
1685 
1686  v8::Local<v8::Value> operator()(v8::Isolate *isolate, std::unique_ptr<T, Rest...> & unique_ptr) {
1687  return CastToJS<T>()(isolate, *unique_ptr.get());
1688  }
1689 
1690  v8::Local<v8::Value> operator()(v8::Isolate *isolate, std::unique_ptr<T, Rest...> && unique_ptr) {
1691 
1692 
1693 // std::cerr << fmt::format("cast to js {} type {}", (void*)unique_ptr.get(), demangle<T>()) << std::endl;
1694  auto & wrapper = V8ClassWrapper<T>::get_instance(isolate);
1695 
1696  // create new owning JavaScript object with the contents of the unique_ptr
1697 // std::cerr << fmt::format("creating object from unique ptr with destructor behavior {}: {} {}", wrapper.destructor_behavior_delete->name(), (void*)wrapper.destructor_behavior_delete.get(), wrapper.destructor_behavior_delete->destructive()) << std::endl;
1698  auto result = wrapper.wrap_existing_cpp_object(isolate->GetCurrentContext(), unique_ptr.release(), *wrapper.destructor_behavior_delete);
1699 // std::cerr << fmt::format("Immediately after creation from unique_ptr, does own memory? {} ", V8ClassWrapper<T>::does_object_own_memory(result)) << std::endl;
1700  auto wrap = v8::Local<v8::External>::Cast(result->ToObject()->GetInternalField(0));
1701  WrappedData<T> *wrapped_data = static_cast<WrappedData<T> *>(wrap->Value());
1702 // std::cerr << fmt::format("WrappedData: {}", (void*)wrapped_data) << std::endl;
1703  return result;
1704 
1705  }
1706 };
1707 
1708 
1709 
1710 
1711 
1712 template<class T>
1713 struct CastToJS<T *, std::enable_if_t<is_wrapped_type_v<T>>> {
1714 
1715  // An lvalue is presented, so the memory will not be cleaned up by JavaScript
1716  v8::Local<v8::Value> operator()(v8::Isolate * isolate, T * const cpp_object) {
1717  if (cpp_object == nullptr) {
1718  return v8::Undefined(isolate);
1719  }
1720 
1721  assert(cpp_object != (void *) 0xbebebebebebebebe);
1722 
1723  V8TOOLKIT_DEBUG("CastToJS from T* %s\n", demangle_typeid_name(typeid(T).name()).c_str());
1724  auto context = isolate->GetCurrentContext();
1725  V8ClassWrapper <T> & class_wrapper = V8ClassWrapper<T>::get_instance(isolate);
1726 
1727 #ifdef V8TOOLKIT_BIDIRECTIONAL_ENABLED
1728  using JSWrapperType = JSWrapper<std::remove_const_t<T>>;
1729 // fprintf(stderr, "Checking to see if object * is a JSWrapper *\n");
1730 
1731  if (std::is_const<T>::value) {
1732  auto js_wrapper = safe_dynamic_cast<JSWrapperType const *>(cpp_object);
1733  if (js_wrapper) {
1734  return CastToJS<const JSWrapperType>()(isolate, *js_wrapper);
1735  }
1736  } else {
1737  // this only runs if it's non-const, so casting is not scary - only to trick compiler
1738  using NonConstT = std::remove_const_t<T>;
1739  auto js_wrapper = safe_dynamic_cast<JSWrapperType *>(const_cast<NonConstT *>(cpp_object));
1740  if (js_wrapper) {
1741  return CastToJS<JSWrapperType>()(isolate, *js_wrapper);
1742  }
1743  }
1744 
1745 #endif
1746  V8TOOLKIT_DEBUG("CastToJS<T*> returning wrapped existing object for %s\n", typeid(T).name());
1747 
1748  /** If you are here looking for an INFINITE RECURSION make sure the type is wrapped **/
1749  return class_wrapper.template wrap_existing_cpp_object(context, cpp_object,
1750  *class_wrapper.destructor_behavior_leave_alone);
1751  }
1752 };
1753 
1754 
1755 template<class T>
1756 struct CastToJS<T&, std::enable_if_t<is_wrapped_type_v<T>>> {
1757 
1758  // An lvalue is presented, so the memory will not be cleaned up by JavaScript
1759  v8::Local<v8::Value> operator()(v8::Isolate * isolate, T & cpp_object) {
1760  return CastToJS<T*>()(isolate, &cpp_object);
1761  }
1762 };
1763 
1764 
1765 template<class T>
1766 struct CastToJS<T&&, std::enable_if_t<is_wrapped_type_v<T>>> {
1767 
1768  v8::Local<v8::Value> operator()(v8::Isolate * isolate, T && cpp_object) {
1769  return CastToJS<std::unique_ptr<T>>()(isolate, std::make_unique<T>(std::move(cpp_object)));
1770  }
1771 };
1772 
1773 
1774 
1775 
1776 template<typename T>
1777 struct CastToNative<T, std::enable_if_t<!std::is_const_v<T> && std::is_copy_constructible<T>::value && is_wrapped_type_v<T>>>
1778 {
1779  T operator()(v8::Isolate * isolate, v8::Local<v8::Value> value) {
1780  return T(*CastToNative<T*>()(isolate, value));
1781  }
1782  static constexpr bool callable(){return true;}
1783 };
1784 
1785 
1786 
1787 template<typename T>
1788 struct CastToNative<T, std::enable_if_t<!std::is_copy_constructible<T>::value && is_wrapped_type_v<T>>>
1789 {
1790  template<class U = T> // just to make it dependent so the static_asserts don't fire before `callable` can be called
1791  T operator()(v8::Isolate * isolate, v8::Local<v8::Value> value) const {
1792  static_assert(always_false_v<T>, "Cannot return a copy of an object of a type that is not copy constructible");
1793  }
1794  static constexpr bool callable(){return false;}
1795 
1796 };
1797 
1798 
1799 
1800 template<typename T>
1801 struct CastToNative<T&, std::enable_if_t<is_wrapped_type_v<T>>>
1802 {
1803  T& operator()(v8::Isolate * isolate, v8::Local<v8::Value> value) {
1804  return *CastToNative<T*>()(isolate, value);
1805  }
1806  static constexpr bool callable(){return true;}
1807 };
1808 
1809 
1810 template<typename T>
1811 struct CastToNative<T&&, std::enable_if_t<is_wrapped_type_v<T>>>
1812 {
1813  // to "give permission" to have the object moved out of, this object must own the memory. It CANNOT
1814  // release ownership of the memory for the object, just the data in the object
1815  // TODO: Is this the right model? This seems the safest policy, but is technically restricting of potentially valid operations
1816  T&& operator()(v8::Isolate * isolate, v8::Local<v8::Value> value) {
1817  v8::Local<v8::Object> object = check_value_is_object(value, demangle<T>());
1818  T * cpp_object = V8ClassWrapper<T>::get_instance(isolate).get_cpp_object(object);
1820  return std::move(*cpp_object); // but do not release the memory
1821  }
1822  throw CastException("Could not cast object to {} && because it doesn't own it's memory", demangle<T>());
1823  }
1824  static constexpr bool callable(){return true;}
1825 };
1826 
1827 template<typename T>
1828 struct CastToNative<T*, std::enable_if_t<is_wrapped_type_v<T>>>
1829 {
1830  T* operator()(v8::Isolate * isolate, v8::Local<v8::Value> value) {
1831  auto & wrapper = V8ClassWrapper<T>::get_instance(isolate);
1832  v8::Local<v8::Object> object = check_value_is_object(value, demangle<T>());
1833 
1834  return wrapper.get_cpp_object(object);
1835  }
1836  static constexpr bool callable(){return true;}
1837 };
1838 
1839 
1840 
1841 template<class T>
1843  return fmt::format("const: {} pointer: {} reference: {} typeid: {}",
1844  std::is_const<T>::value, std::is_pointer<T>::value,
1845  std::is_reference<T>::value, typeid(T).name());
1846  }
1847 
1848 
1849 // specialization for pointers and reference types
1850  template<class T, std::enable_if_t<std::is_pointer<T>::value || std::is_reference<T>::value, int> = 0>
1851  T & get_object_from_embedded_cpp_object(v8::Isolate * isolate, v8::Local<v8::Value> value) {
1852  throw CastException(fmt::format("Pointer and reference types ({}) won't ever succeed in getting an embedded cpp object", demangle<T>()));
1853  }
1854 
1855 /**
1856  * This can be used from CastToNative<UserType> calls to fall back to if other conversions aren't appropriate
1857  */
1858 template<class T, std::enable_if_t<!std::is_pointer<T>::value && !std::is_reference<T>::value, int> = 0>
1859 T & get_object_from_embedded_cpp_object(v8::Isolate * isolate, v8::Local<v8::Value> value) {
1860 
1861  V8TOOLKIT_DEBUG("cast to native\n");
1862  v8::Local<v8::Object> object = check_value_is_object(value, demangle<T>());
1863 
1864  if (object->InternalFieldCount() <= 0) {
1865  throw CastException(fmt::format("No specialization CastToNative<{}> found (for any shortcut notation) and provided Object is not a wrapped C++ object. It is a native Javascript Object", demangle<T>()));
1866  }
1867  v8::Local<v8::External> wrap = v8::Local<v8::External>::Cast(object->GetInternalField(0));
1868  auto wrapped_data = static_cast<WrappedData<T> *>(wrap->Value());
1869 
1870 // std::cerr << fmt::format("about to call cast on {}", demangle<T>()) << std::endl;
1871  T * t;
1872  if ((t = V8ClassWrapper<T>::get_instance(isolate).cast(wrapped_data->native_object)) == nullptr) {
1873  fprintf(stderr, "Failed to convert types: want: %d %s\n", std::is_const<T>::value, typeid(T).name());
1874  throw CastException(fmt::format("Cannot convert AnyBase to {}", demangle<T>()));
1875  }
1876 // std::cerr << fmt::format("Successfully converted") << std::endl;
1877  return *t;
1878 }
1879 
1880 template<class T, class... Rest>
1881 struct CastToNative<std::unique_ptr<T, Rest...>, std::enable_if_t<is_wrapped_type_v<T>>>
1882 {
1883  std::unique_ptr<T, Rest...> operator()(v8::Isolate * isolate, v8::Local<v8::Value> value) const {
1885 
1886  auto object = check_value_is_object(value, demangle<T>());
1887 
1888  // if the javascript object owns the memory, it gives up that ownership to the unique_ptr
1890  auto & wrapper = V8ClassWrapper<T>::get_instance(isolate);
1891  return std::unique_ptr<T, Rest...>(wrapper.release_internal_field_memory(object));
1892  } else if constexpr(std::is_copy_constructible_v<T>) {
1893  T & cpp_object = get_object_from_embedded_cpp_object<T>(isolate, value);
1894  return std::unique_ptr<T, Rest...>(new T(cpp_object));
1895  } else {
1896  throw CastException("Cannot CastToNative<unique_ptr<{}>> from object that doesn't own it's memory and isn't copy constructible: {}",
1897  demangle<T>(), *v8::String::Utf8Value(value));
1898  }
1899  }
1900  static constexpr bool callable(){return true;}
1901 
1902 };
1903 
1904 // If no more-derived option was found, wrap as this type
1905 template<class T>
1906 v8::Local<v8::Object> WrapAsMostDerived<T, v8toolkit::TypeList<>>::operator()(T * cpp_object, DestructorBehavior & destructor_behavior) const {
1907  auto context = this->isolate->GetCurrentContext();
1908 
1909  // TODO: Expensive
1910  auto & wrapper = v8toolkit::V8ClassWrapper<T>::get_instance(this->isolate);
1911  return wrapper.template wrap_existing_cpp_object(context, cpp_object, destructor_behavior, true /* don't infinitely recurse */);
1912 }
1913 
1914 
1915 
1916 template<class T, class Head, class... Tail> T *
1917 TypeChecker<T, v8toolkit::TypeList<Head, Tail...>,
1918  std::enable_if_t<std::is_const<T>::value ||
1919  !std::is_const<Head>::value>
1920  >::check(AnyBase * any_base, bool first_call) const {
1921  assert(any_base != nullptr);
1922  ANYBASE_PRINT("typechecker::check for {} with anyptr {} (string: {})", demangle<Head>(), (void*)any_base, any_base->type_name);
1923  if(auto any = dynamic_cast<AnyPtr<Head> *>(any_base)) {
1924  ANYBASE_PRINT("Got match on: {}, returning {}", demangle<Head>(), (void*)(any->get()));
1925  return static_cast<T*>(any->get());
1926  }
1927 
1928  ANYBASE_PRINT("didn't find match, testing const type now...");
1929 
1930  // if goal type is const and the type to check isn't const, try checking for the const type now
1931  if (!std::is_same<std::remove_const_t<T>, std::remove_const_t<Head>>::value) {
1932  if (auto derived_result = V8ClassWrapper<Head>::get_instance(this->isolate).cast(any_base)) {
1933  return derived_result;
1934  }
1935  }
1936 
1937  ANYBASE_PRINT("no match on const type either, continuing down chain");
1938 
1939  return SUPER::check(any_base, false);
1940 }
1941 
1942 
1943 
1944 
1945 // if a more-derived type was found, pass it to that type to see if there's something even more derived
1946 template<class T, class Head, class... Tail>
1948  std::enable_if_t<!std::is_const<T>::value || std::is_const<Head>::value>>
1949 ::operator()(T * cpp_object, DestructorBehavior & destructor_behavior) const {
1950 
1951  // if they're the same, let it fall through to the empty typechecker TypeList base case
1952  if (!std::is_same<std::remove_const_t<T>, std::remove_const_t<Head>>::value) {
1953  using MatchingConstT = std::conditional_t<std::is_const<Head>::value, std::add_const_t<T>, std::remove_const_t<T>>;
1954 
1955  if (std::is_const<T>::value == std::is_const<Head>::value) {
1956  if (auto derived = safe_dynamic_cast<Head *>(const_cast<MatchingConstT *>(cpp_object))) {
1957  return v8toolkit::V8ClassWrapper<Head>::get_instance(this->isolate).wrap_as_most_derived(derived, destructor_behavior);
1958  }
1959  }
1960  }
1961  return SUPER::operator()(cpp_object, destructor_behavior);
1962 }
1963 
1964 
1965 
1966 template <class T>
1967 struct ParameterBuilder<T, std::enable_if_t<std::is_reference_v<T> && is_wrapped_type_v<std::remove_reference_t<T>>> > {
1968 
1969  using NoRefT = std::remove_reference_t<T>;
1970  using NoConstRefT = std::remove_const_t<NoRefT>;
1971 
1972  template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
1973  T /*T& or T&&*/ operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
1974  std::vector<std::unique_ptr<StuffBase>> & stuff,
1975  DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
1976  PB_PRINT("ParameterBuilder handling wrapped type: {} {}", demangle<T>(),
1977  std::is_rvalue_reference_v<T> ? "&&" : "&");
1978  auto isolate = info.GetIsolate();
1979 
1980  if (i >= info.Length()) {
1981  return std::forward<T>(*get_default_parameter<NoConstRefT, default_arg_position>(info, i, stuff, default_args_tuple));
1982  } else {
1983 
1984  auto & wrapper = V8ClassWrapper<NoRefT>::get_instance(isolate);
1985  auto value = info[i++];
1986 
1987  if (value->IsObject()) {
1988  auto object = value->ToObject();
1989  if (auto cpp_object = wrapper.get_instance(isolate).get_cpp_object(object)) {
1990 
1991  if constexpr(std::is_rvalue_reference_v<T>)
1992  {
1993  if (wrapper.does_object_own_memory(object)) {
1994  return std::move(*cpp_object);
1995  } else if constexpr(std::is_copy_constructible_v<NoConstRefT>)
1996  {
1997  // make a copy, put it in stuff, and return an rvalue ref to the copy
1998  stuff.emplace_back(
1999  std::make_unique<Stuff<NoConstRefT>>(std::make_unique<NoRefT>(*cpp_object)));
2000  return std::forward<T>(*(static_cast<Stuff<NoConstRefT> &>(*stuff.back()).get()));
2001  }
2002  }
2003  // as a policy, only do this if it's an lvalue reference requested
2004  else {
2005  return *cpp_object;
2006  }
2007  }
2008  }
2009  if constexpr(std::is_move_constructible_v<NoConstRefT> && CastToNative<NoConstRefT>::callable())
2010  {
2011  stuff.emplace_back(std::make_unique<Stuff<NoConstRefT>>(CastToNative<NoConstRefT>()(isolate, value)));
2012  return std::forward<T>(*(static_cast<Stuff<NoConstRefT> &>(*stuff.back()).get()));
2013  }
2014  throw CastException("Could not create requested object of type: {} {}. Maybe you don't 'own' your memory?",
2015  demangle<T>(),
2016  std::is_rvalue_reference_v<T> ? "&&" : "&");
2017  }
2018  }
2019 };
2020 
2021 
2022 
2023 
2024 
2025 template <class T>
2026 struct ParameterBuilder<T, std::enable_if_t<std::is_copy_constructible_v<T> && is_wrapped_type_v<T> > > {
2027 
2028  template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
2029  T operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
2030  std::vector<std::unique_ptr<StuffBase>> & stuff,
2031  DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
2032  PB_PRINT("ParameterBuilder handling wrapped type: {}", demangle<T>());
2033  auto isolate = info.GetIsolate();
2034  auto & wrapper = V8ClassWrapper<T>::get_instance(isolate);
2035 
2036  if (i >= info.Length()) {
2037  return *get_default_parameter<T, default_arg_position>(info, i, stuff, default_args_tuple);
2038  } else {
2039  auto value = info[i++];
2040  if (value->IsObject()) {
2041  auto object = value->ToObject();
2042  if (auto cpp_object = wrapper.get_instance(isolate).get_cpp_object(object)) {
2043  // make a copy, put it in stuff, and return an rvalue ref to the copy
2044  return *cpp_object;
2045  }
2046  }
2047  return CastToNative<T>()(isolate, value);
2048  }
2049  }
2050 };
2051 
2052 
2053 
2054 
2055 
2056 } // namespace v8toolkit
2057 
2058 
2059 
2060 
2061 
2062 
2063 
2064 
2065 
2066 
2067 
2068 
2069 
2070 
2071 
2072 
2073 
2074 
2075 
2076 
2077 
2078 
2079 
2080 
2081 
2082 
2083 
2084 
2085 
2086 
2087 
2088 
2089 
2090 
2091 
2092 
2093 
2094 
2095 
2096 
2097 
2098 
2099 
2100 
2101 
2102 
2103 
2104 
2105 
2106 
2107 
2108 
2109 
2110 
2111 
2112 
2113 
2114 
2115 
2116 
2117 
2118 
2119 
2120 
2121 
2122 
2123 
2124 
2125 
2126 
2127 
2128 
2129 
2130 
2131 
2132 
2133 
2134 
2135 
2136 
2137 
2138 
2139 
2140 
2141 
2142 
2143 
2144 
2145 
2146 
2147 
2148 
2149 
2150 
2151 
2152 
2153 
2154 
2155 
2156 
2157 
2158 
2159 
2160 
2161 
2162 
2163 
2164 
2165 
2166 
2167 
2168 
2169 
2170 
2171 
2172 
2173 
2174 
2175 
2176 
2177 
2178 
2179 
2180 
2181 
2182 
2183 
2184 
2185 
2186 
2187 
2188 
2189 
2190 
2191 
2192 
2193 
2194 
2195 
2196 
2197 
2198 
2199 
2200 
2201 
2202 
2203 
2204 
2205 
2206 
2207 
2208 
2209 
2210 
2211 
2212 
2213 
2214 
2215 
2216 
2217 
2218 
2219 
2220 
2221 
2222 
2223 
2224 
2225 
2226 
2227 
2228 
2229 
2230 
2231 
2232 
2233 
2234 
2235 
2236 
2237 
2238 
2239 
2240 
2241 
2242 
2243 
2244 
2245 
2246 
2247 
2248 
2249 
2250 
2251 
2252 
2253 
2254 
2255 
2256 
2257 
2258 
2259 
2260 
2261 
2262 
2263 
2264 
2265 
2266 
2267 
2268 
2269 
2270 
2271 
2272 
2273 
2274 
2275 
2276 
2277 
2278 
2279 
2280 
2281 
2282 
2283 
2284 
2285 
2286 
2287 
2288 
2289 
2290 
2291 
2292 
2293 
2294 
2295 
2296 
2297 
2298 
2299 
2300 
2301 
2302 
2303 
2304 
2305 
2306 
2307 
2308 
2309 
2310 
2311 
2312 
2313 
2314 
2315 
2316 
2317 
2318 
2319 #if 0
2320 
2321 /**
2322 * Attempt to use V8ClassWrapper to wrap any remaining types not handled by the specializations in casts.hpp
2323 * That type must have had its methods and members added beforehand in the same isolate
2324 */
2325  template<typename T>
2326 struct CastToJS<T*, std::enable_if_t<std::is_polymorphic<T>::value>> {
2327  v8::Local<v8::Value> operator()(v8::Isolate * isolate, T * cpp_object){
2328  if (cpp_object == nullptr) {
2329  return v8::Undefined(isolate);
2330  }
2331 
2332  assert(cpp_object != (void *)0xbebebebebebebebe);
2333 
2334  V8TOOLKIT_DEBUG("CastToJS from T* %s\n", demangle_typeid_name(typeid(T).name()).c_str());
2335  auto context = isolate->GetCurrentContext();
2336  V8ClassWrapper<T> & class_wrapper = V8ClassWrapper<T>::get_instance(isolate);
2337 
2338 #ifdef V8TOOLKIT_BIDIRECTIONAL_ENABLED
2339  // if the type is polymorphic and potentially bidirectional, check to see if it actually is
2340  using JSWrapperType = JSWrapp er<std::remove_const_t<T>>;
2341 // fprintf(stderr, "Checking to see if object * is a JSWrapper *\n");
2342 
2343  if (std::is_const<T>::value) {
2344  auto js_wrapper = dynamic_cast<JSWrapperType const *>(cpp_object);
2345  if (js_wrapper) {
2346  return CastToJS<const JSWrapperType>()(isolate, *js_wrapper);
2347  }
2348  } else {
2349  // this only runs if it's non-const, so casting is not scary - only to trick compiler
2350  using NonConstT = std::remove_const_t<T>;
2351  auto js_wrapper = dynamic_cast<JSWrapperType *>(const_cast<NonConstT *>(cpp_object));
2352  if (js_wrapper) {
2353  return CastToJS<JSWrapperType>()(isolate, *js_wrapper);
2354  }
2355  }
2356 
2357 #endif
2358  V8TOOLKIT_DEBUG("CastToJS<T*> returning wrapped existing object for %s\n", typeid(T).name());
2359 
2360  /** If you are here looking for an INFINITE RECURSION make sure the type is wrapped *
2361  return class_wrapper.template wrap_existing_cpp_object(context, cpp_object, *class_wrapper.destructor_behavior_leave_alone);
2362  }
2363 };
2364 //
2365 //template<typename T>
2366 //struct CastToJS<T*, std::enable_if_t<!std::is_polymorphic<T>::value>> {
2367 // v8::Local<v8::Value> operator()(v8::Isolate * isolate, T * cpp_object){
2368 //
2369 // if (cpp_object == nullptr) {
2370 // return v8::Local<v8::Object>();
2371 // }
2372 // assert(cpp_object != (void *)0xbebebebebebebebe);
2373 //
2374 //
2375 // V8TOOLKIT_DEBUG("CastToJS from T* %s\n", demangle<T>().c_str());
2376 // auto context = isolate->GetCurrentContext();
2377 // V8ClassWrapper<T> & class_wrapper = V8ClassWrapper<T>::get_instance(isolate);
2378 //
2379 // V8TOOLKIT_DEBUG("CastToJS<T*> returning wrapped existing object for %s\n", typeid(T).name());
2380 //
2381 // return class_wrapper.template wrap_existing_cpp_object(context, cpp_object, *class_wrapper.destructor_behavior_leave_alone);
2382 // }
2383 //
2384 // };
2385 //
2386 
2387 // Pointers to wrapped types
2388  template<typename T>
2389  struct CastToJS<T, std::enable_if_t<std::is_pointer<T>::value && is_wrapped_type_v<T>>> {
2390  v8::Local<v8::Value> operator()(v8::Isolate * isolate, T const cpp_object){
2391 
2392  if (cpp_object == nullptr) {
2393  return v8::Local<v8::Object>();
2394  }
2395  assert(cpp_object != (void *)0xbebebebebebebebe);
2396 
2397  V8TOOLKIT_DEBUG("CastToJS from T* {}\n", demangle<T>().c_str());
2398  auto context = isolate->GetCurrentContext();
2399  auto & class_wrapper = V8ClassWrapper<std::remove_pointer_t<T>>::get_instance(isolate);
2400 
2401  V8TOOLKIT_DEBUG("CastToJS<T*> returning wrapped existing object for %s\n", typeid(T).name());
2402 
2403  return class_wrapper.template wrap_existing_cpp_object(context, cpp_object, *class_wrapper.destructor_behavior_leave_alone);
2404  }
2405  };
2406 //
2407 //
2408 // template<typename T>
2409 // struct CastToJS<T const * const> {
2410 // v8::Local<v8::Value> operator()(v8::Isolate * isolate, T const * cpp_object){
2411 //
2412 // if (cpp_object == nullptr) {
2413 // return v8::Local<v8::Object>();
2414 // }
2415 // assert(cpp_object != (void *)0xbebebebebebebebe);
2416 //
2417 // V8TOOLKIT_DEBUG("CastToJS from T* {}\n", demangle<T>().c_str());
2418 // auto context = isolate->GetCurrentContext();
2419 // auto & class_wrapper = V8ClassWrapper<T const>::get_instance(isolate);
2420 //
2421 // V8TOOLKIT_DEBUG("CastToJS<T*> returning wrapped existing object for %s\n", typeid(T).name());
2422 //
2423 // return class_wrapper.template wrap_existing_cpp_object(context, cpp_object, *class_wrapper.destructor_behavior_leave_alone);
2424 // }
2425 // };
2426 
2427 //static_assert(std::is_same<T, T*>::value, "Cannot CastToJS a pointer type not wrapped with V8ClassWrapper");
2428 
2429 template<typename T>
2430 struct CastToJS<T, std::enable_if_t<is_wrapped_type_v<T>>> {
2431 
2432 
2433  v8::Local<v8::Value> operator()(v8::Isolate * isolate, std::remove_reference_t<T> & cpp_object){
2434  V8TOOLKIT_DEBUG("CastToJS from lvalue ref %s\n", demangle<T>().c_str());
2435  return CastToJS<typename std::add_pointer<T>::type>()(isolate, &cpp_object);
2436  }
2437 
2438  /**
2439  * If an rvalue is passed in, a copy must be made.
2440  */
2441  v8::Local<v8::Value> operator()(v8::Isolate * isolate, std::remove_reference_t<T> && cpp_object){
2442  using NoRefT = std::remove_reference_t<T>;
2443  V8TOOLKIT_DEBUG("In base cast to js struct with rvalue ref");
2444  V8TOOLKIT_DEBUG("Asked to convert rvalue type, so copying it first\n");
2445 
2446  // this memory will be owned by the javascript object and cleaned up if/when the GC removes the object
2447  auto copy = new NoRefT(std::move(cpp_object));
2448  auto context = isolate->GetCurrentContext();
2449  V8ClassWrapper<NoRefT> & class_wrapper = V8ClassWrapper<NoRefT>::get_instance(isolate);
2450  auto result = class_wrapper.template wrap_existing_cpp_object(context, copy, *class_wrapper.destructor_behavior_delete);
2451  V8TOOLKIT_DEBUG("CastToJS<T> returning wrapped existing object: %s\n", *v8::String::Utf8Value(result));
2452 
2453  return result;
2454  }
2455 };
2456 
2457 
2458 template<typename T>
2459 struct CastToJS<T*&> {
2460  v8::Local<v8::Value> operator()(v8::Isolate * isolate, T * cpp_object) {
2461  return CastToJS<T*>()(isolate, cpp_object);
2462  }
2463 };
2464 
2465 
2466 template<typename T>
2467 struct CastToJS<T const *&> {
2468  v8::Local<v8::Value> operator()(v8::Isolate * isolate, T const * cpp_object) {
2469  return CastToJS<T const *>()(isolate, cpp_object);
2470  }
2471 };
2472 
2473 
2474 template<typename T>
2475 struct CastToJS<T* const &> {
2476  v8::Local<v8::Value> operator()(v8::Isolate * isolate, T * const cpp_object) {
2477  return CastToJS<T * const>()(isolate, cpp_object);
2478  }
2479 };
2480 
2481 
2482 template<typename T>
2483 struct CastToJS<T const * const &> {
2484  v8::Local<v8::Value> operator()(v8::Isolate * isolate, T const * const cpp_object) {
2485  return CastToJS<T const * const>()(isolate, cpp_object);
2486  }
2487 };
2488 
2489 #endif
2490 
2491 
2492 
2493 
2494 
func::function< void(const v8::FunctionCallbackInfo< v8::Value > &info)> StdFunctionCallbackType
Definition: v8helpers.h:42
void add_static_method(const std::string &method_name, Callable callable, DefaultArgs const &default_args_tuple=DefaultArgs{})
bool global_name_conflicts(const std::string &name)
Definition: v8helpers.cpp:358
void add_method(const std::string &method_name, R(TBase::*method)(Args...) const, DefaultArgs const &default_args=DefaultArgs())
v8::Local< v8::Object > wrap_as_most_derived(T *cpp_object, DestructorBehavior &destructor_behavior)
std::enable_if_t< static_all_of< std::is_base_of< T, CompatibleTypes >::value... >::value > set_compatible_types()
T & operator()(v8::Isolate *isolate, v8::Local< v8::Value > value)
std::enable_if_t<!std::is_pointer< ResultT >::value, void > handle_getter_result(ResultT &&result, v8::PropertyCallbackInfo< v8::Value > const &info)
T operator()(const v8::FunctionCallbackInfo< v8::Value > &info, int &i, std::vector< std::unique_ptr< StuffBase >> &stuff, DefaultArgsTuple &&default_args_tuple=DefaultArgsTuple())
#define ANYBASE_PRINT(format_string,...)
Definition: v8helpers.h:518
void add_new_constructor_function_template_callback(FunctionTemplateCallback const &callback)
void add_static_method(const std::string &method_name, R(*callable)(Params...), DefaultArgs const &default_args_tuple=DefaultArgs{})
#define V8TOOLKIT_V8CLASSWRAPPER_USE_REAL_TEMPLATE_SFINAE
::std::string string
Definition: gtest-port.h:1097
void _add_fake_method(const std::string &method_name, func::function< R(Head, Tail...)> method, DefaultArgsTuple const &default_args)
std::unique_ptr< T, Rest... > operator()(v8::Isolate *isolate, v8::Local< v8::Value > value) const
void add_constructor(const std::string &js_constructor_name, v8::Local< v8::ObjectTemplate > parent_template, DefaultArgsTuple const &default_args=DefaultArgsTuple())
v8::Local< v8::Value > operator()(v8::Isolate *isolate, T &&cpp_object)
STL namespace.
virtual std::string name() const override
std::string stringify_value(v8::Isolate *isolate, const v8::Local< v8::Value > &value, bool show_all_properties=false, std::vector< v8::Local< v8::Value >> &&processed_values=std::vector< v8::Local< v8::Value >>{})
Definition: v8helpers.cpp:187
v8::Local< v8::Object > check_value_is_object(v8::Local< v8::Value > value, std::string const &class_name)
Definition: v8helpers.h:683
v8::Local< v8::Object > wrap_existing_cpp_object(v8::Local< v8::Context > context, T *existing_cpp_object, DestructorBehavior &destructor_behavior, bool force_wrap_this_type=false)
v8::Local< v8::Value > operator()(v8::Isolate *isolate, T *const cpp_object)
std::enable_if_t< std::is_pointer< ResultT >::value, void > handle_getter_result(ResultT &&result, v8::PropertyCallbackInfo< v8::Value > const &info)
std::string type_details()
void _add_method(const std::string &method_name, M method, TypeList< Args... > const &, std::tuple< DefaultArgTypes... > const &default_args_tuple, bool add_as_callable_object_callback=false)
void add_callback(v8::Isolate *isolate, func::function< void()> callback)
internal::ArgsMatcher< InnerMatcher > Args(const InnerMatcher &matcher)
TypeCheckerBase(v8::Isolate *isolate)
void add_method(const std::string &method_name, Callback &&callback, DefaultArgs const &default_args=DefaultArgs())
virtual bool destructive() const =0
v8::Local< v8::Value > operator()(v8::Isolate *isolate, std::unique_ptr< T, Rest... > &&unique_ptr)
void add_method(const std::string &method_name, R(*method)(Head, Args...), DefaultArgs const &default_args=DefaultArgs())
std::enable_if_t< std::is_base_of< JSWrapperBase, T >::value, v8::Local< v8::Object > > safe_get_javascript_object(T *object)
Definition: bidirectional.h:89
void add_method(const std::string &method_name, func::function< R(Head, Args...)> &method, DefaultArgs const &default_args=DefaultArgs())
SetWeakCallbackData * global_set_weak(v8::Isolate *isolate, const v8::Local< v8::Object > &javascript_object, func::function< void(v8::WeakCallbackInfo< SetWeakCallbackData > const &)> callback, bool destructive)
Definition: v8toolkit.cpp:863
void dump_prototypes(v8::Isolate *isolate, v8::Local< v8::Object > object)
Definition: v8toolkit.cpp:711
v8::Local< v8::Value > operator()(v8::Isolate *isolate, std::unique_ptr< T, Rest... > &unique_ptr)
T operator()(const v8::FunctionCallbackInfo< v8::Value > &info, int &i, std::vector< std::unique_ptr< StuffBase >> &stuff, DefaultArgsTuple &&default_args_tuple=DefaultArgsTuple())
void add_fake_method_for_const_type(const std::string &method_name, func::function< R(Head *, Tail...)> method, DefaultArgs const &default_args=DefaultArgs())
bool Value(const T &value, M matcher)
WrapAsMostDerivedBase(v8::Isolate *isolate)
void add_method(const std::string &method_name, R(TBase::*method)(Args...) const &, DefaultArgs const &default_args=DefaultArgs())
Destination safe_dynamic_cast(Source *source)
Definition: v8helpers.h:168
#define V8TOOLKIT_DEBUG(format_string,...)
func::function< R(Args...)> bind(CLASS &object, R(METHOD_CLASS::*method)(Args...))
Definition: v8toolkit.h:855
std::string type_name
Definition: v8helpers.h:531
T * operator()(v8::Isolate *isolate, v8::Local< v8::Value > value)
std::enable_if_t< std::is_base_of< ParentType, T >::value > set_parent_type()
v8::Local< v8::FunctionTemplate > make_function_template(v8::Isolate *isolate, func::function< R(Args...)> f, std::string const &name)
Definition: v8toolkit.h:356
StdFunctionCallbackType callback
Definition: v8helpers.h:45
void add_method(const std::string &method_name, R(TBase::*method)(Args...), DefaultArgs const &default_args=DefaultArgs())
static void initialize_new_js_object(v8::Isolate *isolate, v8::Local< v8::Object > js_object, T *cpp_object, DestructorBehavior const &destructor_behavior)
void cleanup_isolate(v8::Isolate *isolate)
#define PB_PRINT(format_string,...)
void operator()(v8::Isolate *isolate, const void *void_object) const override
v8::Local< v8::Value > operator()(v8::Isolate *isolate, T &cpp_object)
virtual std::string name() const override
eastl::vector_map< ArgTs... > MapT
void expose_static_methods(const std::string &js_name, v8::Local< v8::ObjectTemplate > parent_template)
#define ISOLATE_SCOPED_RUN(isolate)
Definition: v8toolkit.h:93
T & get_object_from_embedded_cpp_object(v8::Isolate *isolate, v8::Local< v8::Value > value)
void add_method(const std::string &method_name, R(TBase::*method)(Args...)&&, DefaultArgs const &default_args=DefaultArgs())
std::map< v8::Isolate *, std::vector< std::string > > used_constructor_name_list_map
Definition: v8toolkit.cpp:39
T && operator()(v8::Isolate *isolate, v8::Local< v8::Value > value)
v8::Local< v8::Value > operator()(v8::Isolate *isolate, T &cpp_object)
const T & move(const T &t)
Definition: gtest-port.h:1317
std::string demangle_typeid_name(const std::string &mangled_name)
Definition: v8helpers.cpp:45
#define HANDLE_FUNCTION_VALUES
Definition: casts.hpp:69
v8::Local< v8::Value > operator()(v8::Isolate *isolate, T &&cpp_object)
void operator()(v8::Isolate *isolate, const void *void_object) const override
void add_method(const std::string &method_name, R(TBase::*method)(Args...)&, DefaultArgs const &default_args=DefaultArgs())
void add_method(const std::string &method_name, R(TBase::*method)(Args...) const &&, DefaultArgs const &default_args=DefaultArgs())
virtual T * check(AnyBase *any_base, bool first_call=true) const override
V8ClassWrapperInstanceRegistry wrapper_registery
Definition: v8toolkit.cpp:42