Pyrogenesis HEAD
Pyrogenesis, a RTS Engine
ScriptFunction Class Reference

This class introduces templates to conveniently wrap C++ functions in JSNative functions. More...

#include <FunctionWrapper.h>

Collaboration diagram for ScriptFunction:

Classes

struct  args_info
 Convenient struct to get info on a [class] [const] function pointer. More...
 
struct  args_info< R(*)(Types ...)>
 
struct  args_info< R(C::*)(Types ...) const >
 
struct  args_info< R(C::*)(Types ...)>
 
struct  IgnoreResult_t
 
struct  IteratorResultError
 

Public Types

template<typename T >
using ObjectGetter = T *(*)(const ScriptRequest &, JS::CallArgs &)
 
template<class callableType >
using GetterFor = ObjectGetter< typename args_info< callableType >::object_type >
 

Static Public Member Functions

template<auto callable, GetterFor< decltype(callable)> thisGetter = nullptr>
static bool ToJSNative (JSContext *cx, unsigned argc, JS::Value *vp)
 The meat of this file. More...
 
template<typename R , typename ... Args>
static bool Call (const ScriptRequest &rq, JS::HandleValue val, const char *name, R &ret, const Args &... args)
 Call a JS function name, property of object val, with the arguments args. More...
 
template<typename ... Args>
static bool Call (const ScriptRequest &rq, JS::HandleValue val, const char *name, JS::MutableHandleValue ret, const Args &... args)
 
template<typename ... Args>
static bool CallVoid (const ScriptRequest &rq, JS::HandleValue val, const char *name, const Args &... args)
 Call a JS function name, property of object val, with the arguments args. More...
 
template<typename Callback >
static JS::Value RunGenerator (const ScriptRequest &rq, JS::HandleValue val, const char *name, JS::HandleValue arg, Callback yieldCallback)
 Call a JS function name, property of object val, with the argument args. More...
 
template<auto callable, GetterFor< decltype(callable)> thisGetter = nullptr>
static JSFunctionSpec Wrap (const char *name, const u16 flags=JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT)
 Return a function spec from a C++ function. More...
 
template<auto callable, GetterFor< decltype(callable)> thisGetter = nullptr>
static JSFunction * Create (const ScriptRequest &rq, const char *name, const u16 flags=JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT)
 Return a JSFunction from a C++ function. More...
 
template<auto callable, GetterFor< decltype(callable)> thisGetter = nullptr>
static void Register (const ScriptRequest &rq, const char *name, const u16 flags=JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT)
 Register a function on the native scope (usually 'Engine'). More...
 
template<auto callable, GetterFor< decltype(callable)> thisGetter = nullptr>
static void Register (JSContext *cx, JS::HandleObject scope, const char *name, const u16 flags=JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT)
 Register a function on. More...
 

Private Types

template<typename T >
using type_transform = std::conditional_t< std::is_same_v< const ScriptRequest &, T >||std::is_same_v< const ScriptInterface &, T >, T, std::remove_const_t< typename std::remove_reference_t< T > > >
 In JS->C++ calls, types are converted using FromJSVal, and this requires them to be default-constructible (as that function takes an out parameter) thus constref needs to be removed when defining the tuple. More...
 

Private Member Functions

 ScriptFunction ()=delete
 
 ScriptFunction (const ScriptFunction &)=delete
 
 ScriptFunction (ScriptFunction &&)=delete
 

Static Private Member Functions

template<size_t idx, typename T >
static T DoConvertFromJS (const ScriptRequest &rq, JS::CallArgs &args, bool &wentOk)
 DoConvertFromJS takes a type, a JS argument, and converts. More...
 
template<typename... T, size_t... idx>
static std::tuple< T... > DoConvertFromJS (std::index_sequence< idx... >, const ScriptRequest &rq, JS::CallArgs &args, bool &wentOk)
 Wrapper: calls DoConvertFromJS for each element in T. More...
 
template<typename ... Types>
static std::tuple< Types... > ConvertFromJS (const ScriptRequest &rq, JS::CallArgs &args, bool &wentOk, std::tuple< Types... > *)
 ConvertFromJS is a wrapper around DoConvertFromJS, and handles specific cases for the first argument (ScriptRequest, ...). More...
 
template<typename ... Types>
static std::tuple< const ScriptRequest &, Types... > ConvertFromJS (const ScriptRequest &rq, JS::CallArgs &args, bool &wentOk, std::tuple< const ScriptRequest &, Types... > *)
 
template<typename ... Types>
static std::tuple< const ScriptInterface &, Types... > ConvertFromJS (const ScriptRequest &rq, JS::CallArgs &args, bool &wentOk, std::tuple< const ScriptInterface &, Types... > *)
 
template<auto callable, typename T , typename tuple >
static args_info< decltype(callable)>::return_type call (T *object, tuple &args)
 Wrap std::apply for the case where we have an object method or a regular function. More...
 
template<typename... Types, size_t... idx>
static void ToJSValVector (std::index_sequence< idx... >, const ScriptRequest &rq, JS::MutableHandleValueVector argv, const Types &... params)
 Converts any number of arguments to a JS::MutableHandleValueVector. More...
 
template<typename R , typename ... Args>
static bool Call_ (const ScriptRequest &rq, JS::HandleValue val, const char *name, R &ret, const Args &... args)
 Wrapper around calling a JS function from C++. More...
 

Static Private Attributes

static IgnoreResult_t IgnoreResult
 

Detailed Description

This class introduces templates to conveniently wrap C++ functions in JSNative functions.

This is rather template heavy, so compilation times beware. The C++ code can have arbitrary arguments and arbitrary return types, so long as they can be converted to/from JS using Script::ToJSVal (FromJSVal respectively), and they are default-constructible (TODO: that can probably changed). (This could be a namespace, but I like being able to specify public/private).

Member Typedef Documentation

◆ GetterFor

template<class callableType >
using ScriptFunction::GetterFor = ObjectGetter<typename args_info<callableType>::object_type>

◆ ObjectGetter

template<typename T >
using ScriptFunction::ObjectGetter = T*(*)(const ScriptRequest&, JS::CallArgs&)

◆ type_transform

template<typename T >
using ScriptFunction::type_transform = std::conditional_t< std::is_same_v<const ScriptRequest&, T> || std::is_same_v<const ScriptInterface&, T>, T, std::remove_const_t<typename std::remove_reference_t<T> > >
private

In JS->C++ calls, types are converted using FromJSVal, and this requires them to be default-constructible (as that function takes an out parameter) thus constref needs to be removed when defining the tuple.

Exceptions are:

  • const ScriptRequest& (as the first argument only, for implementation simplicity).
  • const ScriptInterface& (as the first argument only, for implementation simplicity).
  • JS::HandleValue

Constructor & Destructor Documentation

◆ ScriptFunction() [1/3]

ScriptFunction::ScriptFunction ( )
privatedelete

◆ ScriptFunction() [2/3]

ScriptFunction::ScriptFunction ( const ScriptFunction )
privatedelete

◆ ScriptFunction() [3/3]

ScriptFunction::ScriptFunction ( ScriptFunction &&  )
privatedelete

Member Function Documentation

◆ Call() [1/2]

template<typename ... Args>
static bool ScriptFunction::Call ( const ScriptRequest rq,
JS::HandleValue  val,
const char *  name,
JS::MutableHandleValue  ret,
const Args &...  args 
)
inlinestatic

◆ Call() [2/2]

template<typename R , typename ... Args>
static bool ScriptFunction::Call ( const ScriptRequest rq,
JS::HandleValue  val,
const char *  name,
R &  ret,
const Args &...  args 
)
inlinestatic

Call a JS function name, property of object val, with the arguments args.

ret will be updated with the return value, if any.

Returns
the success (or failure) thereof.

◆ call()

template<auto callable, typename T , typename tuple >
static args_info< decltype(callable)>::return_type ScriptFunction::call ( T object,
tuple &  args 
)
inlinestaticprivate

Wrap std::apply for the case where we have an object method or a regular function.

◆ Call_()

template<typename R , typename ... Args>
static bool ScriptFunction::Call_ ( const ScriptRequest rq,
JS::HandleValue  val,
const char *  name,
R &  ret,
const Args &...  args 
)
inlinestaticprivate

Wrapper around calling a JS function from C++.

Arguments are const& to avoid lvalue/rvalue issues, and so can't be used as out-parameters. In particular, the problem is that Rooted are deduced as Rooted, not Handle, and so can't be copied. This could be worked around with more templates, but it doesn't seem particularly worth doing.

◆ CallVoid()

template<typename ... Args>
static bool ScriptFunction::CallVoid ( const ScriptRequest rq,
JS::HandleValue  val,
const char *  name,
const Args &...  args 
)
inlinestatic

Call a JS function name, property of object val, with the arguments args.

Returns
the success (or failure) thereof.

◆ ConvertFromJS() [1/3]

template<typename ... Types>
static std::tuple< const ScriptInterface &, Types... > ScriptFunction::ConvertFromJS ( const ScriptRequest rq,
JS::CallArgs &  args,
bool &  wentOk,
std::tuple< const ScriptInterface &, Types... > *   
)
inlinestaticprivate

◆ ConvertFromJS() [2/3]

template<typename ... Types>
static std::tuple< const ScriptRequest &, Types... > ScriptFunction::ConvertFromJS ( const ScriptRequest rq,
JS::CallArgs &  args,
bool &  wentOk,
std::tuple< const ScriptRequest &, Types... > *   
)
inlinestaticprivate

◆ ConvertFromJS() [3/3]

template<typename ... Types>
static std::tuple< Types... > ScriptFunction::ConvertFromJS ( const ScriptRequest rq,
JS::CallArgs &  args,
bool &  wentOk,
std::tuple< Types... > *   
)
inlinestaticprivate

ConvertFromJS is a wrapper around DoConvertFromJS, and handles specific cases for the first argument (ScriptRequest, ...).

Trick: to unpack the types of the tuple as a parameter pack, we deduce them from the function signature. To do that, we want the tuple in the arguments, but we don't want to actually have to default-instantiate, so we'll pass a nullptr that's static_cast to what we want.

◆ Create()

template<auto callable, GetterFor< decltype(callable)> thisGetter = nullptr>
static JSFunction * ScriptFunction::Create ( const ScriptRequest rq,
const char *  name,
const u16  flags = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT 
)
inlinestatic

Return a JSFunction from a C++ function.

◆ DoConvertFromJS() [1/2]

template<size_t idx, typename T >
static T ScriptFunction::DoConvertFromJS ( const ScriptRequest rq,
JS::CallArgs &  args,
bool &  wentOk 
)
inlinestaticprivate

DoConvertFromJS takes a type, a JS argument, and converts.

The type T must be default constructible (except for HandleValue, which is handled specially). (possible) TODO: this could probably be changed if FromJSVal had a different signature.

Parameters
wentOk- true if the conversion succeeded and wentOk was true before, false otherwise.

◆ DoConvertFromJS() [2/2]

template<typename... T, size_t... idx>
static std::tuple< T... > ScriptFunction::DoConvertFromJS ( std::index_sequence< idx... >  ,
const ScriptRequest rq,
JS::CallArgs &  args,
bool &  wentOk 
)
inlinestaticprivate

Wrapper: calls DoConvertFromJS for each element in T.

◆ Register() [1/2]

template<auto callable, GetterFor< decltype(callable)> thisGetter = nullptr>
static void ScriptFunction::Register ( const ScriptRequest rq,
const char *  name,
const u16  flags = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT 
)
inlinestatic

Register a function on the native scope (usually 'Engine').

◆ Register() [2/2]

template<auto callable, GetterFor< decltype(callable)> thisGetter = nullptr>
static void ScriptFunction::Register ( JSContext *  cx,
JS::HandleObject  scope,
const char *  name,
const u16  flags = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT 
)
inlinestatic

Register a function on.

Parameters
scope.Prefer the version taking ScriptRequest unless you have a good reason not to.
See also
Register

◆ RunGenerator()

template<typename Callback >
static JS::Value ScriptFunction::RunGenerator ( const ScriptRequest rq,
JS::HandleValue  val,
const char *  name,
JS::HandleValue  arg,
Callback  yieldCallback 
)
inlinestatic

Call a JS function name, property of object val, with the argument args.

Repeatetly invokes yieldCallback with the yielded value.

Returns
the final value of the generator.

◆ ToJSNative()

template<auto callable, GetterFor< decltype(callable)> thisGetter = nullptr>
static bool ScriptFunction::ToJSNative ( JSContext *  cx,
unsigned  argc,
JS::Value *  vp 
)
inlinestatic

The meat of this file.

This wraps a C++ function into a JSNative, so that it can be called from JS and manipulated in Spidermonkey. Most C++ functions can be directly wrapped, so long as their arguments are convertible from JS::Value and their return value is convertible to JS::Value (or void) The C++ function may optionally take const ScriptRequest& or ScriptInterface& as its first argument. The function may be an object method, in which case you need to pass an appropriate getter

Optimisation note: the ScriptRequest object is created even without arguments, as it's necessary for IsExceptionPending.

Parameters
thisGetterto get the object, if necessary.

TODO: error handling isn't standard, and since this can call any C++ function, there's no simple obvious way to deal with it. For now we check for pending JS exceptions, but it would probably be nicer to standardise on something, or perhaps provide an "errorHandler" here.

◆ ToJSValVector()

template<typename... Types, size_t... idx>
static void ScriptFunction::ToJSValVector ( std::index_sequence< idx... >  ,
const ScriptRequest rq,
JS::MutableHandleValueVector  argv,
const Types &...  params 
)
inlinestaticprivate

Converts any number of arguments to a JS::MutableHandleValueVector.

If idx is empty this function does nothing. For that case there is a [[maybe_unused]] on argv. GCC would issue a "-Wunused-but-set-parameter" warning. For references like rq this warning isn't issued.

◆ Wrap()

template<auto callable, GetterFor< decltype(callable)> thisGetter = nullptr>
static JSFunctionSpec ScriptFunction::Wrap ( const char *  name,
const u16  flags = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT 
)
inlinestatic

Return a function spec from a C++ function.

Member Data Documentation

◆ IgnoreResult

IgnoreResult_t ScriptFunction::IgnoreResult
inlinestaticprivate

The documentation for this class was generated from the following file: