v8toolkit  0.0.1
Utility library for embedding V8 Javascript engine in a c++ program
v8toolkit.h
Go to the documentation of this file.
1 
2 #pragma once
3 
4 #include <iostream>
5 #include <vector>
6 #include <fstream>
7 #include <sstream>
8 #include <tuple>
9 
10 #include <string.h>
11 
12 #include "v8helpers.h"
13 #include "casts.hpp"
14 #include "stdfunctionreplacement.h"
15 
16 #include "call_callable.h"
17 
18 
19 #ifndef _MSC_VER
20 #include <dirent.h>
21 #endif
22 
23 
24 #define V8_TOOLKIT_DEBUG false
25 
26 
27 /** TODO LIST
28 *
29 * All includes should be included as #include "v8toolkit/FILENAME.h" instead of just #include "FILENAME.h"
30 * Rename javascript.h file to something much better
31 * Including "v8toolkit/v8toolkit.h" should include everything, but specific includes should also work for
32 * v8helpers.h, <new name for v8toolkit.h>, <new name for javascript.h>
33 */
34 
35 
36 
37 namespace v8toolkit {
38 
39 
40 
42  SetWeakCallbackData(func::function<void(v8::WeakCallbackInfo<SetWeakCallbackData> const &)> callback,
43  v8::Isolate * isolate,
44  const v8::Local<v8::Object> & javascript_object,
45  bool destructive);
46 
48  v8::Global<v8::Object> global;
50  };
51 
52 
53  /**
54  * Holds the c++ object to be embedded inside a javascript object along with additional debugging information
55  * when requested
56  */
57  template<class T>
58  struct WrappedData {
60  std::string native_object_type = demangle<T>();
61  SetWeakCallbackData * weak_callback_data = nullptr;
63 
64  WrappedData(T * native_object,
65  SetWeakCallbackData * weak_callback_data) :
66  native_object(new AnyPtr<T>(native_object)),
67  weak_callback_data(weak_callback_data),
68  original_pointer(native_object)
69  {}
70  ~WrappedData(){delete native_object;}
71  };
72 
73 
74 
75 
76 
77 /* Use these to try to decrease the amount of template instantiations */
78 #define CONTEXT_SCOPED_RUN(local_context) \
79  v8::Isolate * _v8toolkit_internal_isolate = (local_context)->GetIsolate(); \
80  v8::Locker _v8toolkit_internal_locker(_v8toolkit_internal_isolate); \
81  v8::Isolate::Scope _v8toolkit_internal_isolate_scope(_v8toolkit_internal_isolate); \
82  v8::HandleScope _v8toolkit_internal_handle_scope(_v8toolkit_internal_isolate); \
83  v8::Context::Scope _v8toolkit_internal_context_scope((local_context));
84 
85 #define GLOBAL_CONTEXT_SCOPED_RUN(isolate, global_context) \
86  v8::Locker _v8toolkit_internal_locker(isolate); \
87  v8::Isolate::Scope _v8toolkit_internal_isolate_scope(isolate); \
88  v8::HandleScope _v8toolkit_internal_handle_scope(isolate); \
89  /* creating local context must be after creating handle scope */ \
90  v8::Local<v8::Context> _v8toolkit_internal_local_context = global_context.Get(isolate); \
91  v8::Context::Scope _v8toolkit_internal_context_scope(_v8toolkit_internal_local_context);
92 
93 #define ISOLATE_SCOPED_RUN(isolate) \
94  v8::Locker _v8toolkit_internal_locker((isolate)); \
95  v8::Isolate::Scope _v8toolkit_internal_isolate_scope((isolate)); \
96  v8::HandleScope _v8toolkit_internal_handle_scope((isolate));
97 
98 #define DEBUG_SCOPED_RUN(isolate) \
99  v8::Locker _v8toolkit_internal_locker((isolate)); \
100  v8::Isolate::Scope _v8toolkit_internal_isolate_scope((isolate)); \
101  v8::HandleScope _v8toolkit_internal_handle_scope((isolate)); \
102  v8::Context::Scope _v8toolkit_internal_context_scope(v8::Debug::GetDebugContext((isolate)));
103 
104 
105 /**
106  * Helper function to run the callable inside contexts.
107  * If the isolate is currently inside a context, it will use that context automatically
108  * otherwise no context::scope will be created
109  */
110 template<class T>
111 auto scoped_run(v8::Isolate * isolate, T callable) -> typename std::result_of<T()>::type
112 {
113  v8::Locker locker(isolate);
114  v8::Isolate::Scope isolate_scope(isolate);
115  v8::HandleScope handle_scope(isolate);
116 
117  if (isolate->InContext()) {
118  auto context = isolate->GetCurrentContext();
119  v8::Context::Scope context_scope(context);
120  return callable();
121  } else {
122  return callable();
123  }
124 }
125 
126 
127 /**
128 * Helper function to run the callable inside contexts.
129 * If the isolate is currently inside a context, it will use that context automatically
130 * otherwise no context::scope will be created
131 * The isolate will be passed to the callable
132 */
133 template<class T>
134 auto scoped_run(v8::Isolate * isolate, T callable) -> typename std::result_of<T(v8::Isolate*)>::type
135 {
136  v8::Locker locker(isolate);
137  v8::Isolate::Scope isolate_scope(isolate);
138  v8::HandleScope handle_scope(isolate);
139 
140  return callable(isolate);
141 }
142 
143 /**
144 * Helper function to run the callable inside contexts.
145 * If the isolate is currently inside a context, it will use that context automatically
146 * otherwise no context::scope will be created
147 * This version requires the isolate is currently in a context
148 * The isolate and context will be passed to the callable
149 */
150 template<class T>
151 auto scoped_run(v8::Isolate * isolate, T callable) -> typename std::result_of<T(v8::Isolate*, v8::Local<v8::Context>)>::type
152 {
153  v8::Locker locker(isolate);
154  v8::Isolate::Scope isolate_scope(isolate);
155  v8::HandleScope handle_scope(isolate);
156 
157  if (isolate->InContext()) {
158  auto context = isolate->GetCurrentContext();
159  v8::Context::Scope context_scope(context);
160  return callable(isolate, context);
161  } else {
162  throw InvalidCallException("Isolate not currently in a context, but callable expects a context.");
163  }
164 }
165 
166 
167 
168 // TODO: Probably don't need to take both an isolate and a local<context> - you can get isolate from a local<context> (but not a global one)
169 /**
170 * Helper function to run the callable inside contexts.
171 * This version is good when the isolate isn't currently within a context but a context
172 * has been created to be used
173 */
174 template<class T>
175 auto scoped_run(v8::Isolate * isolate, v8::Local<v8::Context> context, T callable) -> typename std::result_of<T()>::type
176 {
177 
178  v8::Locker locker(isolate);
179  v8::Isolate::Scope isolate_scope(isolate);
180  v8::HandleScope handle_scope(isolate);
181  v8::Context::Scope context_scope(context);
182 
183  return callable();
184 }
185 
186 
187 // TODO: Probably don't need to take both an isolate and a local<context> - you can get isolate from a local<context> (but not a global one)
188 /**
189 * Helper function to run the callable inside contexts.
190 * This version is good when the isolate isn't currently within a context but a context
191 * has been created to be used
192 * The isolate will be passed to the callable
193 */
194 template<class T>
195 auto scoped_run(v8::Isolate * isolate, v8::Local<v8::Context> context, T callable) -> typename std::result_of<T(v8::Isolate*)>::type
196 {
197  v8::Locker locker(isolate);
198  v8::Isolate::Scope isolate_scope(isolate);
199  v8::HandleScope handle_scope(isolate);
200  v8::Context::Scope context_scope(context);
201 
202  return callable(isolate);
203 }
204 
205 // TODO: Probably don't need to take both an isolate and a local<context> - you can get isolate from a local<context> (but not a global one)
206 /**
207 * Helper function to run the callable inside contexts.
208 * This version is good when the isolate isn't currently within a context but a context
209 * has been created to be used
210 * The isolate and context will be passed to the callable
211 */
212 template<class T>
213 auto scoped_run(v8::Isolate * isolate, v8::Local<v8::Context> context, T callable) ->
214  typename std::result_of<T(v8::Isolate*, v8::Local<v8::Context>)>::type
215 {
216  v8::Locker locker(isolate);
217  v8::Isolate::Scope isolate_scope(isolate);
218  v8::HandleScope handle_scope(isolate);
219  v8::Context::Scope context_scope(context);
220 
221  return callable(isolate, context);
222 }
223 
224 // Same as the ones above, but this one takes a global context for convenience
225 // Isolate is required since a Local<Context> cannot be created without creating a locker
226 // and handlescope which require an isolate to create
227 template<class T>
228 auto scoped_run(v8::Isolate * isolate, const v8::Global<v8::Context> & context, T callable)
229 {
230  v8::Locker l(isolate);
231  v8::HandleScope hs(isolate);
232  auto local_context = context.Get(isolate);
233  return scoped_run(isolate, local_context, callable);
234 }
235 
236 
237 
238 
239 /**
240 * When the V8 engine itself generates an error (or a user calls isolate->ThrowException manually with a v8::Value for some reason)
241 * That exception is re-thrown as a standard C++ exception of this type. The V8 Value thrown is available.
242 * get_local_value must be called within a HandleScope
243 * get_value returns a new Global handle to the value.
244 */
245 class V8Exception : public std::exception {
246 private:
247  v8::Isolate * isolate;
248  v8::Global<v8::Value> value;
249  std::string value_for_what;
250 
251 public:
252  V8Exception(v8::Isolate * isolate, v8::Global<v8::Value>&& value) : isolate(isolate), value(std::move(value)) {
253  std::string str(*v8::String::Utf8Value(this->value.Get(isolate)));
254  value_for_what = str == "" ? "unknown error" : str;
255  }
256  V8Exception(v8::Isolate * isolate, v8::Local<v8::Value> value) : V8Exception(isolate, v8::Global<v8::Value>(isolate, value)) {}
257  V8Exception(v8::Isolate * isolate, std::string reason) : V8Exception(isolate, v8::String::NewFromUtf8(isolate, reason.c_str())) {}
258  virtual const char * what() const noexcept override {
259  return value_for_what.c_str();
260  }
261  v8::Local<v8::Value> get_local_value(){return value.Get(isolate);}
262  v8::Isolate * get_isolate(){return isolate;}
263  v8::Global<v8::Value> get_value(){return v8::Global<v8::Value>(isolate, value);}
264 };
265 
266 
268 public:
269  V8AssertionException(v8::Isolate * isolate, v8::Local<v8::Value> value) :
270  V8Exception(isolate, value) {}
271  V8AssertionException(v8::Isolate * isolate, v8::Global<v8::Value>&& value) :
272  V8Exception(isolate, std::forward<v8::Global<v8::Value>>(value)) {}
273  V8AssertionException(v8::Isolate * isolate, std::string reason) : V8Exception(isolate, reason) {}
274 };
275 
277  std::string stacktrace;
278 public:
279 
280  V8ExecutionException(v8::Isolate * isolate, v8::TryCatch & tc) :
281  V8Exception(isolate, tc.Exception())
282  {
283 
284  auto stacktrace_maybe = tc.StackTrace(isolate->GetCurrentContext());
285  if (!stacktrace_maybe.IsEmpty()) {
286  stacktrace = *v8::String::Utf8Value(stacktrace_maybe.ToLocalChecked());
287  }
288  }
289  const std::string & get_stacktrace(){return stacktrace;}
290 };
291 
292 
293 
294 
295 
296  /**
297 * Same as a V8 exception, except if this type is thrown it indicates the exception was generated
298 * during compilation, not at runtime.
299 */
301 public:
302  V8CompilationException(v8::Isolate * isolate, v8::Global<v8::Value>&& value) :
303  V8Exception(isolate, std::forward<v8::Global<v8::Value>>(value)) {}
304  V8CompilationException(v8::Isolate * isolate, v8::Local<v8::Value> value) :
305  V8Exception(isolate, value) {}
306  V8CompilationException(v8::Isolate * isolate, std::string reason) : V8Exception(isolate, reason) {}
307 
308 };
309 
310 
311 
312 
313 
314 
315 
316 
317 
318 template<class T>
320  using type = T;
321 };
322 
323 
324 template<class T>
325 struct remove_const_from_reference<T const &>{
326  using type = T &&;
327 };
328 
329 template<class T>
331  using type = T;
332 };
333 
334 
335 template<class T>
337 
338 
339 /**
340  * Struct of data passed down through building the parameters to call the function and actually
341  * calling the function
342  */
343 template<class R, class... Args>
347 };
348 
349 
350 
351 
352 /**
353 * Creates a function template from a func::function
354 */
355 template <class R, class... Args>
357  func::function<R(Args...)> f,
358  std::string const & name)
359 {
360  auto data = new FunctionTemplateData<R, Args...>();
361  data->callable = f;
362  data->name = name;
363 
364  // wrap the actual call in this lambda
365  return v8::FunctionTemplate::New(isolate, [](const v8::FunctionCallbackInfo<v8::Value>& info) {
366  auto isolate = info.GetIsolate();
367 
368  FunctionTemplateData<R, Args...> & data = *(FunctionTemplateData<R, Args...> *)v8::External::Cast(*(info.Data()))->Value();
369 
370  try {
371  CallCallable<decltype(data.callable)>()(data.callable, info, std::index_sequence_for<Args...>{});
372 
373  } catch (std::exception & e) {
374 
375  isolate->ThrowException(v8::String::NewFromUtf8(isolate, e.what()));
376 
377  // OLD CODE PUSHED EXCEPTION BACK THROUGH JAVASCRIPT TO C++ BUT EXCEPTION NOT AVAILABLE IN JAVASCRIPT
378  //auto anyptr_t = new Any<std::exception_ptr>( std::current_exception());
379  // always put in the base ptr so you can cast to it safely and then use dynamic_cast to try to figure
380  // out what it really is
381  //isolate->ThrowException(v8::External::New(isolate, static_cast<AnyBase*>(anyptr_t)));
382  }
383  return; // no return value, PB sets it in the 'info' object
384 
385  }, v8::External::New(isolate, (void*)data));
386 }
387 
388 
389 /**
390 * Takes an arbitrary class method and returns a func::function wrapping it
391 */
392 template<class R, class CLASS, class... Args>
393 func::function<R(Args...)> make_std_function_from_callable(R(CLASS::*f)(Args...) const, CLASS callable)
394 {
395  return func::function<R(Args...)>(callable);
396 }
397 
398 
399 
400 template<class R, class... Args>
402  return func::function<R(Args...)>(callable);
403 };
404 
405 
406 /**
407 * Creates a v8::FunctionTemplate for an arbitrary callable
408 */
409 template<class T>
411 {
412  return make_function_template(isolate, make_std_function_from_callable(&T::operator(), callable), name);
413 }
414 
415 
416 /**
417 * Creates a function template from a c-style function pointer
418 */
419 template <class R, class... Args>
420 v8::Local<v8::FunctionTemplate> make_function_template(v8::Isolate * isolate, R(*f)(Args...), std::string const & name)
421 {
422  return make_function_template(isolate, func::function<R(Args...)>(f), name);
423 }
424 
425 
426 /**
427 * Helper to both create a function template from a func::function and bind it with the specified name to the specified object template
428 * Adding functions to an object_template allows creation of multiple contexts with the function already added to each context
429 */
430 template<class R, class... Args>
431 void add_function(v8::Isolate * isolate, const v8::Local<v8::ObjectTemplate> & object_template, const char * name, func::function<R(Args...)> function) {
432  object_template->Set(isolate, name, make_function_template(isolate, function, name));
433 }
434 
435 /**
436 * Helper to both create a function template from an arbitrary callable and bind it with the specified name to the specified object template
437 * Adding functions to an object_template allows creation of multiple contexts with the function already added to each context
438 */
439 template<class T>
440 void add_function(v8::Isolate * isolate, const v8::Local<v8::ObjectTemplate> & object_template, const char * name, T callable) {
441  decltype(LTG<T>::go(&T::operator())) f(callable);
442  object_template->Set(isolate, name, make_function_template(isolate, f, name));
443 }
444 
445 /**
446 * Helper to both create a function template from an arbitrary function pointer and bind it with the specified name to the specified object template
447 * Adding functions to an object_template allows creation of multiple contexts with the function already added to each context
448 */
449 template<class R, class... Args>
450 void add_function(v8::Isolate * isolate, const v8::Local<v8::ObjectTemplate> & object_template, const char * name, R(*function)(Args...)) {
451  object_template->Set(isolate, name, make_function_template(isolate, function, name));
452 }
453 
454 /**
455 * Helper to both create a function template from an arbitrary callable and bind it with the specified name to the specified object template
456 * Adding functions to an object allows adding a function to any object, including a context's global object.
457 */
458 
459 template<class T>
460 void add_function(const v8::Local<v8::Context> & context, const v8::Local<v8::Object> & object, const char * name, T callable)
461 {
462  CONTEXT_SCOPED_RUN(context);
463 
464  auto isolate = context->GetIsolate();
465  auto function_template = make_function_template(isolate, callable, name);
466  auto function = function_template->GetFunction();
467  (void)object->Set(context, v8::String::NewFromUtf8(isolate, name), function);
468 }
469 
470 /**
471 * Makes the given javascript value available to all objects created with the object_template as name.
472 * Often used to populate the object_template used to create v8::Context objects so the variable is available from
473 * all contexts created from that object_template
474 */
475 void add_variable(v8::Isolate * isolate, const v8::Local<v8::ObjectTemplate> & object_template, const char * name, const v8::Local<v8::Data> value);
476 
477 
478 /**
479 * Makes the given javascript value available in the given object as name.
480 * Often used to add a variable to a specific context's global object
481 */
482 void add_variable(const v8::Local<v8::Context> context,
483  const v8::Local<v8::Object> & object,
484  const char * name,
485  const v8::Local<v8::Value> value);
486 
487 
488 
489 /**
490 * add a function that directly handles the v8 callback data
491 * explicit function typing needed to coerce non-capturing lambdas into c-style function pointers
492 */
493 void add_function(v8::Isolate * isolate, const v8::Local<v8::ObjectTemplate> & object_template, const char * name, void(*function)(const v8::FunctionCallbackInfo<v8::Value>&));
494 
495 template<int position, class Tuple>
497 
498 /**
499 * Populates an array of v8::Values with the values of the tuple, casted by the tuple element's type
500 */
501 template<int position, class Tuple>
502 struct TupleForEach : public TupleForEach<position - 1, Tuple> {
503  using super = TupleForEach<position - 1, Tuple>;
504  void operator()(v8::Isolate * isolate, v8::Local<v8::Value> * params, const Tuple & tuple){
505  constexpr int array_position = position - 1;
506  params[array_position] = CastToJS<typename std::tuple_element<array_position, Tuple>::type>()(isolate, std::get<array_position>(tuple));
507  super::operator()(isolate, params, tuple);
508  }
509 };
510 
511 /**
512 * Base case for no remaining elements to parse (as determined by the position being 0)
513 */
514 template<class Tuple>
515 struct TupleForEach<0, Tuple> {
516  void operator()(v8::Isolate *, v8::Local<v8::Value> *, const Tuple &){}
517 };
518 
519 
520 /**
521  *
522  */
523 template<class... OriginalTypes, class... Ts>
525  const v8::Local<v8::Function> function,
526  const v8::Local<v8::Object> receiver,
527  const TypeList<OriginalTypes...> & type_list,
528  Ts&&... ts) {
529  auto isolate = context->GetIsolate();
530  std::vector<v8::Local<v8::Value>> parameters {CastToJS<OriginalTypes>()(isolate, std::forward<Ts>(ts))...};
531  auto parameter_count = sizeof...(ts);
532  v8::TryCatch tc(isolate);
533  auto maybe_result = function->Call(context, receiver, parameter_count, &parameters[0]);
534  if(tc.HasCaught() || maybe_result.IsEmpty()) {
535  printf("Error running javascript function: '%s'\n", *v8::String::Utf8Value(tc.Exception()));
536  if (v8toolkit::static_any<std::is_const<std::remove_reference_t<OriginalTypes>>::value...>::value) {
537  printf("Some of the types are const, make sure what you are using them for is available on the const type\n");
538  }
539  ReportException(isolate, &tc);
540  throw V8ExecutionException(isolate, tc);
541  }
542  return maybe_result.ToLocalChecked();
543 
544 }
545 
546 /**
547 * Returns true on success with the result in the "result" parameter
548 */
549 template<class TupleType = std::tuple<>>
551  const v8::Local<v8::Function> function,
552  const v8::Local<v8::Object> receiver,
553  const TupleType & tuple = {})
554 {
555  constexpr int tuple_size = std::tuple_size<TupleType>::value;
556  std::array<v8::Local<v8::Value>, tuple_size> parameters;
557  auto isolate = context->GetIsolate();
558  TupleForEach<tuple_size, TupleType>()(isolate, parameters.data(), tuple);
559 
560  v8::TryCatch tc(isolate);
561 
562  // printf("\n\n**** Call_javascript_function with receiver: %s\n", stringify_value(isolate, v8::Local<v8::Value>::Cast(receiver)).c_str());
563  auto maybe_result = function->Call(context, receiver, tuple_size, parameters.data());
564  if(tc.HasCaught() || maybe_result.IsEmpty()) {
565  ReportException(isolate, &tc);
566  printf("Error running javascript function: '%s'\n", *v8::String::Utf8Value(tc.Exception()));
567  throw V8ExecutionException(isolate, tc);
568  }
569  return maybe_result.ToLocalChecked();
570 }
571 
572 /**
573 * Returns true on success with the result in the "result" parameter
574 */
575 template<class TupleType = std::tuple<>>
577  const std::string & function_name,
578  const v8::Local<v8::Object> receiver,
579  const TupleType & tuple = {})
580 {
581  auto maybe_value = receiver->Get(context, v8::String::NewFromUtf8(context->GetIsolate(),function_name.c_str()));
582  if(maybe_value.IsEmpty()) {
583  throw InvalidCallException(fmt::format("Function name {} could not be found", function_name));
584  }
585 
586  auto value = maybe_value.ToLocalChecked();
587  if(!value->IsFunction()) {
588  throw InvalidCallException(fmt::format("{} was found but is not a function", function_name));;
589  }
590 
591  return call_javascript_function(context, v8::Local<v8::Function>::Cast(value), receiver, tuple);
592 }
593 
594 
595 // helper for getting exposed variables
596 template<class T>
598  const v8::PropertyCallbackInfo<v8::Value>& info)
599 {
600  auto isolate = info.GetIsolate();
601  T * variable = (T*)v8::External::Cast(*(info.Data()))->Value();
602 // if (return_most_derived) {
603 // //TODO: ME
604 // assert(false);
605 // } else {
606  info.GetReturnValue().Set(CastToJS<T>()(isolate, *variable));
607 // }
608 }
609 
610 
611 // setter is a no-op if the type is const
612 template<class T, std::enable_if_t<std::is_const<T>::value, int> = 0>
613 void _variable_setter(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)
614 {
615  // should this throw a V8 exception?
616 }
617 
618 
619 // if the type is not const, then set the value as requested
620 template<class T, std::enable_if_t<!std::is_const<T>::value, int> = 0>
621 void _variable_setter(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)
622 {
623  // TODO: This doesnt work well with pointer types - we want to assign to the dereferenced version, most likely.
624  *(T*)v8::External::Cast(*(info.Data()))->Value() = CastToNative<T>()(info.GetIsolate(), value);
625 }
626 //
627 //template <class T>
628 //struct AccessorData {
629 // T * variable;
630 // bool return_most_derived = false;
631 // AccessorData(T * variable, bool return_most_derived = false) :
632 // variable(variable),
633 // return_most_derived(return_most_derived)
634 // {}
635 //};
636 
637 /**
638 * Exposes the specified variable to javascript as the specified name in the given object template (usually the global template).
639 * Allows reads and writes to the variable
640 */
641 template<class T>
642 void expose_variable(v8::Isolate * isolate, const v8::Local<v8::ObjectTemplate> & object_template, const char * name, T & variable) {
643  object_template->SetAccessor(v8::String::NewFromUtf8(isolate, name),
644  _variable_getter<T>,
645  _variable_setter<T>,
646  v8::External::New(isolate, &variable));
647 }
648 
649 
650 template<class T, class... Rest>
651  void expose_variable(v8::Isolate * isolate,
652  const v8::Local<v8::ObjectTemplate> & object_template,
653  const char * name,
654  std::unique_ptr<T, Rest...> & variable) {
655 object_template->SetAccessor(v8::String::NewFromUtf8(isolate, name),
656  _variable_getter<std::unique_ptr<T, Rest...>&>,
657  _variable_setter<std::unique_ptr<T, Rest...>&>,
658  v8::External::New(isolate, variable.get()));
659 }
660 
661 
662 
663 /**
664 * Exposes the specified variable to javascript as the specified name in the given object template (usually the global template).
665 * Allows reads to the variable. Writes are ignored.
666 * TODO: consider making writes errors (throw?)
667 */
668 template<class T>
669 void expose_variable_readonly(v8::Isolate * isolate, const v8::Local<v8::ObjectTemplate> & object_template, const char * name, T & variable) {
670  object_template->SetAccessor(v8::String::NewFromUtf8(isolate, name),
671  _variable_getter<T>,
672  0,
673  v8::External::New(isolate, &variable));
674 }
675 
676 /**
677 * Exposes the C++ variable 'variable' to a specific javascript object as a read/write variable
678 * Often used to add the variable to a context's global object
679 */
680 template<class T>
681 void expose_variable(v8::Local<v8::Context> context, const v8::Local<v8::Object> & object, const char * name, T & variable) {
682  auto isolate = context->GetIsolate();
683  object->SetAccessor(v8::String::NewFromUtf8(isolate, name),
684  _variable_getter<T>,
685  _variable_setter<T>,
686  v8::External::New(isolate, &variable));
687 }
688 
689 /**
690 * Exposes the C++ variable 'variable' to a specific javascript object as a read-only variable (writes are ignored but are not errors)
691 * TODO: consider making them errors (throw?)
692 * Often used to add the variable to a context's global object
693 */
694 template<class T>
695 void expose_variable_readonly(v8::Local<v8::Context> context, const v8::Local<v8::Object> & object, const char * name, T & variable) {
696  auto isolate = context->GetIsolate();
697  object->SetAccessor(v8::String::NewFromUtf8(isolate, name), _variable_getter<T>, 0, v8::External::New(isolate, &variable));
698 }
699 
700 
701 
702 
703 /**
704 * Takes a local and creates a weak global reference callback for it
705 * Useful for clearing out C++-allocated memory on javascript garbage collection of an associated javascript object
706  * Remember, this is not guaranteed to ever be called
707 */
708 SetWeakCallbackData * global_set_weak(v8::Isolate * isolate,
709  const v8::Local<v8::Object> & javascript_object,
710  func::function<void(v8::WeakCallbackInfo<SetWeakCallbackData> const &)> callback,
711  bool destructive);
712 
713 
714 
715 // takes a format string and some javascript objects and does a printf-style print using boost::format
716 // fills missing parameters with empty strings and prints any extra parameters with spaces between them
717 std::string _printf_helper(const v8::FunctionCallbackInfo<v8::Value>& args, bool append_newline);
718 
719 
720 /**
721 * Returns the values in a FunctionCallbackInfo object breaking out first-level arrays into their
722 * contained values (but not subsequent arrays for no particular reason)
723 */
724 std::vector<v8::Local<v8::Value>> get_all_values(const v8::FunctionCallbackInfo<v8::Value>& args, int depth = 1);
725 
726 
727 
728 // prints out arguments with a space between them
729 std::string _print_helper(const v8::FunctionCallbackInfo<v8::Value>& args, bool append_newline);
730 
731 /**
732 * Adds the print functions listed below to the given object_template (usually a v8::Context's global object)
733 * Optional callback function can be used to send the output to another source (defaults to stdout)
734 *
735 * call this to add a set of print* functions to whatever object template you pass in (probably the global one)
736 * print takes a single variable or an array and prints each value separated by spaces
737 *
738 * println same as print but automatically appends a newlines
739 *
740 * printf - Treats the first parameter as a format string.
741 * any additional values will be used to fill the format string. If there are insufficient parameters
742 * to fill the format, the empty string "" will be used. Any extra parameters will be printed after
743 * the filled format string separated by spaces
744 *
745 * printfln - same as printf but automatically appends a newline
746 *
747 * printobj - prints a bunch of information about an object - format highly susceptible to frequent change
748 */
749 void add_print(v8::Isolate * isolate, v8::Local<v8::ObjectTemplate> object_template, func::function<void(const std::string &)> = [](const std::string & s){printf("%s", s.c_str());} );
750 void add_print(const v8::Local<v8::Context> context, func::function<void(const std::string &)> callback = [](const std::string & s){printf("%s", s.c_str());});
751 
752 /**
753 * Adds an assert method that calls assert.h assert() on failure. This is different than the add_assert() in javascript.h that throws an exception on failure
754 * because if an exception is not caught before it reaches V8 execution, the program is terminated. javascript.h *Helper classes automatically catch
755 * and re-throw exceptions so it is safe to throw in that version, but not this one. The error message resulting from throwing an exception
756 * reaching code compiled without exception support is not easy to understand which is why a simple assert is preferable.
757 */
758 void add_assert(v8::Isolate * isolate, v8::Local<v8::ObjectTemplate> object_template);
759 
760 // returns true if the two values are the same by value, including nested data structures
761 bool compare_contents(v8::Isolate * isolate, const v8::Local<v8::Value> & left, const v8::Local<v8::Value> & right);
762 
763 
764 /**
765 * Accepts an object and a method on that object to be called later via its operator()
766 * Does not require knowledge of how many parameters the method takes or any placeholder arguments
767 * Can be wrapped with a func::function
768 */
769 template<class>
770 struct Bind{};
771 
772 /**
773  * Non-const object to non-const method
774  */
775 template<class R, class T, class... Args>
776 struct Bind<R(T::*)(Args...)> {
777 
778  Bind(T & object, R(T::*method)(Args...) )
779  :
780  object(object), method(method)
781  {}
782 
783  ~Bind(){}
784 
785  T & object;
786  R(T::*method)(Args...);
787 
788  R operator()(Args && ... params){
789  return (object.*method)(std::forward<Args>(params)...);
790 // return R();
791  }
792 };
793 
794 template<class R, class T, class... Args>
795 struct Bind<R(T::*)(Args...) &> {
796 
797  Bind(T & object, R(T::*method)(Args...) &) :
798  object(object), method(method){}
799 
800  ~Bind(){}
801 
802  T & object;
803  R(T::*method)(Args...) &;
804 
805  R operator()(Args && ... params){
806  return (object.*method)(std::forward<Args>(params)...);
807  }
808 };
809 
810 
811 
812 /**
813  * Non-const object to const method
814  */
815 template<class R, class T, class... Args>
816 struct Bind<R(T::*)(Args...) const> {
817 
818  Bind(T const & object, R(T::*method)(Args...) const) :
819  object(object), method(method){}
820 
821  T const & object;
822  R(T::*method)(Args...) const;
823 
824  R operator()(Args && ... params){
825  return (object.*method)(std::forward<Args>(params)...);
826  }
827 };
828 
829 
830 template<class R, class T, class... Args>
831 struct Bind<R(T::*)(Args...) const &> {
832 
833  Bind(T const & object, R(T::*method)(Args...) const &) :
834  object(object), method(method){}
835 
836  T const & object;
837  R(T::*method)(Args...) const &;
838 
839  R operator()(Args && ... params){
840  return (object.*method)(std::forward<Args>(params)...);
841  }
842 };
843 
844 
845 
846 
847 
848 /**
849  * unqualified
850 * Helper function to create a Bind object using type deduction and wrap it in a
851 * func::function object.
852 * This specialization is for handling non-const class methods
853 */
854 template <class CLASS, class R, class METHOD_CLASS, class... Args>
855  func::function<R(Args...)> bind(CLASS & object, R(METHOD_CLASS::*method)(Args...))
856 // func::function<R(Args...)> bind(CLASS & object, R(METHOD_CLASS::*method)(Args...))
857 // auto bind(CLASS & object, R(METHOD_CLASS::*method)(Args...))
858 
859  {
860  return Bind<decltype(method)>(object, method);
861 }
862 
863 
864 /**
865  * l-value qualified
866  * @param object
867  * @param method
868  * @return
869  */
870 template <class CLASS, class R, class METHOD_CLASS, class... Args>
871 func::function<R(Args...)> bind(CLASS & object, R(METHOD_CLASS::*method)(Args...) &)
872 {
873  return Bind<decltype(method)>(object, method);
874 }
875 
876 
877 
878 /**
879  * Const qualified
880  * @param object
881  * @param method
882  * @return
883  */
884 template <class CLASS, class R, class METHOD_CLASS, class... Args>
885 func::function<R(Args...)> bind(CLASS & object, R(METHOD_CLASS::*method)(Args...) const)
886 {
887  return Bind<decltype(method)>(object, method);
888 }
889 
890 
891 /**
892  * l-value and const qualified
893  * @param object
894  * @param method
895  * @return
896  */
897 template <class CLASS, class R, class METHOD_CLASS, class... Args>
898 func::function<R(Args...)> bind(CLASS & object, R(METHOD_CLASS::*method)(Args...) const &)
899 {
900  return Bind<decltype(method)>(object, method);
901 }
902 
903 
904 /**
905 * Example allocator code from V8 Docs
906 */
908  public:
909  inline virtual void* Allocate(size_t length) override {
910  void* data = AllocateUninitialized(length);
911  return data == NULL ? data : memset(data, 0, length);
912  }
913  inline virtual void* AllocateUninitialized(size_t length) override { return malloc(length); }
914  inline virtual void Free(void* data, size_t) override { free(data); }
915 };
916 
917 
918 /**
919 * If the filename `filename` exists, reeturns true and sets the last modificaiton time and contents
920 * otherwise returns false
921 */
922 bool get_file_contents(std::string filename, std::string & file_contents, time_t & file_modification_time);
923 
924 /**
925 * same as longer version, just doesn't return modification time if it's not desired
926 */
927 bool get_file_contents(std::string filename, std::string & file_contents);
928 
929 
930 /**
931  * Before deleting an isolate using require, make sure to clean up
932  * its require cache or the program will crash while exiting
933  * @param isolate which isolate to delete the cache for
934  */
935 void delete_require_cache_for_isolate(v8::Isolate * isolate);
936 
937 
938 /**
939 * adds 'require' method to javascript to emulate node require.
940 * Adds an self-referential "global" alias to the global object
941 * Must be run after the context is created so "global" can refer to the global object
942 * (if this could be changed, it should be, but I don't know how to do it beforehand)
943 */
944 void add_require(v8::Isolate * isolate, const v8::Local<v8::ObjectTemplate> & context, const std::vector<std::string> & paths);
945 
946 
947 /**
948 * adds "module_list()" to javascript to require a dictionary of module path+names to exported objects
949 * currently required into the specified isolate
950 */
951 void add_module_list(v8::Isolate * isolate, const v8::Local<v8::ObjectTemplate> & object_template);
952 
953 
954 
956  v8::Isolate * isolate;
957  v8::Global<v8::Context> context;
958  v8::Global<v8::Function> function;
959  v8::Global<v8::Value> result;
960  time_t time;
961  RequireResult(v8::Isolate * isolate,
962  v8::Local<v8::Context> context,
963  v8::Local<v8::Function> function,
964  v8::Local<v8::Value> result,
965  const time_t & time) :
966  isolate(isolate),
967  context(v8::Global<v8::Context>(isolate, context)),
968  function(v8::Global<v8::Function>(isolate, function)),
969  result(v8::Global<v8::Value>(isolate, result)),
970  time(time)
971  {}
972  // IF CRASHING IN RequireResult DESTRUCTOR, MAKE SURE TO CALL delete_require_cache_for_isolate BEFORE DESTROYING ISOLATE
973 };
974 
975 
976  /**
977 * Attempts to load the specified module name from the given paths (in order).
978 * Returns the exported object from the module.
979 * Same as calling require() from javascript - this is the code that is actually run for that
980 */
981 bool require(v8::Local<v8::Context> context,
982  std::string filename,
983  v8::Local<v8::Value> & result,
984  const std::vector<std::string> & paths,
985  bool track_modification_times = false,
986  bool use_cache = true,
987  func::function<void(RequireResult const &)> callback = func::function<void(RequireResult const &)>(),
988  func::function<std::string(std::string const &)> resource_name_callback = func::function<std::string(std::string const &)>()
989  );
990 
991 
992 /**
993 * requires all the files in a directory
994 */
995 void require_directory(v8::Local<v8::Context> context, std::string directory_name);
996 
997 
998 void dump_prototypes(v8::Isolate * isolate, v8::Local<v8::Object> object);
999 
1000 
1001 
1002 // void require_directory(std::string directory_name)
1003 // {
1004 //
1005 // // #include <boost/filesystem.hpp>
1006 // //
1007 // // boost::filesystem::path p = boost::filesystem::current_path();
1008 // // boost::filesystem::directory_iterator it{p};
1009 // // while (it != boost::filesystem::directory_iterator{})
1010 // // std::cout << *it++ << '\n';
1011 // //
1012 //
1013 // // This probably works on more than just APPLE
1014 // #ifdef __APPLE__
1015 // DIR * dir = opendir(".");
1016 // if (dir == NULL)
1017 // return;
1018 // struct dirent * dp;
1019 // while ((dp = readdir(dir)) != NULL) {
1020 // // if (dp->d_namlen == len && strcmp(dp->d_name, name) == 0) {
1021 // // (void)closedir(dir);
1022 // // return (FOUND);
1023 // // }
1024 // }
1025 // (void)closedir(dir);
1026 // return;
1027 //
1028 // #endif // __APPLE__
1029 //
1030 // }
1031 
1032 std::vector<std::string> get_interesting_properties(v8::Local<v8::Context> context, v8::Local<v8::Object> object);
1033 
1035 
1036 
1037 void foreach_file(const std::string & directory_name, std::function<void(const std::string &)> const & callback);
1038 
1039 void foreach_directory(const std::string & directory_name, std::function<void(const std::string &)> const & callback);
1040 
1041 } // end v8toolkit namespace
1042 
1043 
1044 /** \mainpage v8toolkit API documentation
1045 * v8toolkit is a multi-layer library to simplify working with the V8 Javascript engine.
1046 *
1047 * It contains a set of primitive functions in v8toolkit.h, a library that extends
1048 * the functionality in v8toolkit to user-defined class types in v8_class_wrapper.h,
1049 * and a set of high level classes for integrating V8 with virtually no knowledge of the
1050 * underlying v8 API in javascript.h.
1051 *
1052 * Each of these libraries has internal documentation of its types and functions as well
1053 * as an example usage files (any .cpp file with "sample" in its name).
1054 */
1055 
1056 
1057 
1058 #include "casts_impl.hpp"
RequireResult(v8::Isolate *isolate, v8::Local< v8::Context > context, v8::Local< v8::Function > function, v8::Local< v8::Value > result, const time_t &time)
Definition: v8toolkit.h:961
typename remove_const_from_reference< T >::type remove_const_from_reference_t
Definition: v8toolkit.h:336
void _variable_getter(v8::Local< v8::String > property, const v8::PropertyCallbackInfo< v8::Value > &info)
Definition: v8toolkit.h:597
SetWeakCallbackData(func::function< void(v8::WeakCallbackInfo< SetWeakCallbackData > const &)> callback, v8::Isolate *isolate, const v8::Local< v8::Object > &javascript_object, bool destructive)
Definition: v8toolkit.cpp:852
#define CONTEXT_SCOPED_RUN(local_context)
Definition: v8toolkit.h:78
virtual void * Allocate(size_t length) override
Definition: v8toolkit.h:909
std::string _printf_helper(const v8::FunctionCallbackInfo< v8::Value > &args, bool append_newline)
Definition: v8toolkit.cpp:116
void expose_variable_readonly(v8::Isolate *isolate, const v8::Local< v8::ObjectTemplate > &object_template, const char *name, T &variable)
Definition: v8toolkit.h:669
Bind(T &object, R(T::*method)(Args...)&)
Definition: v8toolkit.h:797
v8::Global< v8::Value > result
Definition: v8toolkit.h:959
v8::Local< v8::Value > run_script(v8::Local< v8::Context > context, v8::Local< v8::Script > script)
Definition: v8toolkit.cpp:380
v8::Global< v8::Context > context
Definition: v8toolkit.h:957
virtual void Free(void *data, size_t) override
Definition: v8toolkit.h:914
void expose_variable(v8::Isolate *isolate, const v8::Local< v8::ObjectTemplate > &object_template, const char *name, T &variable)
Definition: v8toolkit.h:642
std::vector< std::string > get_interesting_properties(v8::Local< v8::Context > context, v8::Local< v8::Object > object)
Definition: v8toolkit.cpp:843
std::string _print_helper(const v8::FunctionCallbackInfo< v8::Value > &args, bool append_newline)
Definition: v8toolkit.cpp:147
::std::string string
Definition: gtest-port.h:1097
void add_assert(v8::Isolate *isolate, v8::Local< v8::ObjectTemplate > object_template)
Definition: v8toolkit.cpp:212
STL namespace.
void operator()(v8::Isolate *isolate, v8::Local< v8::Value > *params, const Tuple &tuple)
Definition: v8toolkit.h:504
V8Exception(v8::Isolate *isolate, v8::Global< v8::Value > &&value)
Definition: v8toolkit.h:252
void add_require(v8::Isolate *isolate, const v8::Local< v8::ObjectTemplate > &context, const std::vector< std::string > &paths)
Definition: v8toolkit.cpp:650
R operator()(Args &&...params)
Definition: v8toolkit.h:805
internal::ArgsMatcher< InnerMatcher > Args(const InnerMatcher &matcher)
void delete_require_cache_for_isolate(v8::Isolate *isolate)
Definition: v8toolkit.cpp:340
Definition: sample.cpp:29
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
v8::Global< v8::Value > get_value()
Definition: v8toolkit.h:263
void dump_prototypes(v8::Isolate *isolate, v8::Local< v8::Object > object)
Definition: v8toolkit.cpp:711
R operator()(Args &&...params)
Definition: v8toolkit.h:788
Bind(T const &object, R(T::*method)(Args...) const)
Definition: v8toolkit.h:818
void _variable_setter(v8::Local< v8::String > property, v8::Local< v8::Value > value, const v8::PropertyCallbackInfo< void > &info)
Definition: v8toolkit.h:613
bool Value(const T &value, M matcher)
void add_module_list(v8::Isolate *isolate, const v8::Local< v8::ObjectTemplate > &object_template)
Definition: v8toolkit.cpp:632
V8AssertionException(v8::Isolate *isolate, std::string reason)
Definition: v8toolkit.h:273
V8AssertionException(v8::Isolate *isolate, v8::Local< v8::Value > value)
Definition: v8toolkit.h:269
func::function< R(Args...)> bind(CLASS &object, R(METHOD_CLASS::*method)(Args...))
Definition: v8toolkit.h:855
V8CompilationException(v8::Isolate *isolate, v8::Local< v8::Value > value)
Definition: v8toolkit.h:304
bool compare_contents(v8::Isolate *isolate, const v8::Local< v8::Value > &left, const v8::Local< v8::Value > &right)
Definition: v8toolkit.cpp:732
v8::Local< v8::FunctionTemplate > make_function_template(v8::Isolate *isolate, func::function< R(Args...)> f, std::string const &name)
Definition: v8toolkit.h:356
v8::Local< v8::Value > get_local_value()
Definition: v8toolkit.h:261
v8::Isolate * isolate
Definition: v8toolkit.h:956
virtual void * AllocateUninitialized(size_t length) override
Definition: v8toolkit.h:913
Bind(T &object, R(T::*method)(Args...))
Definition: v8toolkit.h:778
auto scoped_run(v8::Isolate *isolate, T callable) -> typename std::result_of< T()>::type
Definition: v8toolkit.h:111
AnyBase * native_object
Definition: v8toolkit.h:59
void foreach_directory(const std::string &directory_name, std::function< void(const std::string &)> const &callback)
Definition: v8toolkit.cpp:947
V8Exception(v8::Isolate *isolate, v8::Local< v8::Value > value)
Definition: v8toolkit.h:256
bool require(v8::Local< v8::Context > context, std::string filename, v8::Local< v8::Value > &result, const std::vector< std::string > &paths, bool track_modification_times=false, bool use_cache=true, func::function< void(RequireResult const &)> callback=func::function< void(RequireResult const &)>(), func::function< std::string(std::string const &)> resource_name_callback=func::function< std::string(std::string const &)>())
Definition: v8toolkit.cpp:492
void ReportException(v8::Isolate *isolate, v8::TryCatch *try_catch)
Definition: v8helpers.cpp:316
Bind(T const &object, R(T::*method)(Args...) const &)
Definition: v8toolkit.h:833
v8::Global< v8::Object > global
Definition: v8toolkit.h:48
V8CompilationException(v8::Isolate *isolate, v8::Global< v8::Value > &&value)
Definition: v8toolkit.h:302
const std::string & get_stacktrace()
Definition: v8toolkit.h:289
void foreach_file(const std::string &directory_name, std::function< void(const std::string &)> const &callback)
Definition: v8toolkit.cpp:943
void add_function(v8::Isolate *isolate, const v8::Local< v8::ObjectTemplate > &object_template, const char *name, func::function< R(Args...)> function)
Definition: v8toolkit.h:431
func::function< void(v8::WeakCallbackInfo< SetWeakCallbackData > const &)> callback
Definition: v8toolkit.h:47
WrappedData(T *native_object, SetWeakCallbackData *weak_callback_data)
Definition: v8toolkit.h:64
V8ExecutionException(v8::Isolate *isolate, v8::TryCatch &tc)
Definition: v8toolkit.h:280
void require_directory(v8::Local< v8::Context > context, std::string directory_name)
Definition: v8toolkit.cpp:678
V8CompilationException(v8::Isolate *isolate, std::string reason)
Definition: v8toolkit.h:306
void add_variable(v8::Isolate *isolate, const v8::Local< v8::ObjectTemplate > &object_template, const char *name, const v8::Local< v8::Data > value)
Definition: v8toolkit.cpp:65
v8::Local< v8::Value > call_javascript_function(const v8::Local< v8::Context > context, const v8::Local< v8::Function > function, const v8::Local< v8::Object > receiver, const TupleType &tuple={})
Definition: v8toolkit.h:550
func::function< R(Args...)> callable
Definition: v8toolkit.h:345
bool get_file_contents(std::string filename, std::string &file_contents, time_t &file_modification_time)
Definition: v8toolkit.cpp:249
v8::Local< v8::Value > call_javascript_function_with_vars(const v8::Local< v8::Context > context, const v8::Local< v8::Function > function, const v8::Local< v8::Object > receiver, const TypeList< OriginalTypes... > &type_list, Ts &&...ts)
Definition: v8toolkit.h:524
void add_print(v8::Isolate *isolate, v8::Local< v8::ObjectTemplate > object_template, func::function< void(const std::string &)>=[](const std::string &s){printf("%s", s.c_str());})
Definition: v8toolkit.cpp:166
V8AssertionException(v8::Isolate *isolate, v8::Global< v8::Value > &&value)
Definition: v8toolkit.h:271
std::vector< v8::Local< v8::Value > > get_all_values(const v8::FunctionCallbackInfo< v8::Value > &args, int depth=1)
Definition: v8toolkit.cpp:123
v8::Isolate * get_isolate()
Definition: v8toolkit.h:262
V8Exception(v8::Isolate *isolate, std::string reason)
Definition: v8toolkit.h:257
const T & move(const T &t)
Definition: gtest-port.h:1317
func::function< R(Args...)> make_std_function_from_callable(R(CLASS::*f)(Args...) const, CLASS callable)
Definition: v8toolkit.h:393
virtual const char * what() const noexcept override
Definition: v8toolkit.h:258
void operator()(v8::Isolate *, v8::Local< v8::Value > *, const Tuple &)
Definition: v8toolkit.h:516