18#ifndef INCLUDED_FUNCTIONWRAPPER
19#define INCLUDED_FUNCTIONWRAPPER
25#include <fmt/format.h>
59 std::is_same_v<const ScriptRequest&, T> || std::is_same_v<const ScriptInterface&, T>,
61 std::remove_const_t<typename std::remove_reference_t<T>>
71 template<
typename R,
typename ...Types>
74 static constexpr const size_t nb_args =
sizeof...(Types);
77 using arg_types = std::tuple<type_transform<Types>...>;
80 template<
typename C,
typename R,
typename ...Types>
82 template<
typename C,
typename R,
typename ...Types>
91 std::runtime_error{fmt::format(
"Failed to get `{}` from an `IteratorResult`.", property)}
93 using std::runtime_error::runtime_error;
105 template<
size_t idx,
typename T>
109 if constexpr (std::is_same_v<T, JS::HandleValue>)
113 if (idx >= args.length())
114 return JS::UndefinedHandleValue;
126 if (idx >= args.length())
131 wentOk &= Script::FromJSVal<T>(rq, args[idx], ret);
140 template<
typename...
T,
size_t... idx>
142 JS::CallArgs& args,
bool& wentOk)
144 return {DoConvertFromJS<idx, T>(rq, args, wentOk)...};
155 template<
typename ...Types>
157 std::tuple<Types...>*)
159 return DoConvertFromJS<Types...>(std::index_sequence_for<Types...>(), rq, args, wentOk);
163 template<
typename ...Types>
165 JS::CallArgs& args,
bool& wentOk, std::tuple<const ScriptRequest&, Types...>*)
167 return std::tuple_cat(std::tie(rq), DoConvertFromJS<Types...>(
168 std::index_sequence_for<Types...>(), rq, args, wentOk));
172 template<
typename ...Types>
174 JS::CallArgs& args,
bool& wentOk, std::tuple<const ScriptInterface&, Types...>*)
177 DoConvertFromJS<Types...>(std::index_sequence_for<Types...>(), rq, args, wentOk));
186 template <auto callable,
typename T,
typename tuple>
187 static typename args_info<
decltype(callable)>::return_type
call(
T*
object, tuple& args)
189 if constexpr(std::is_same_v<T, void>)
193 return std::apply(callable, args);
196 return std::apply(callable, std::tuple_cat(std::forward_as_tuple(*
object), args));
212 template<
typename... Types,
size_t... idx>
214 [[maybe_unused]] JS::MutableHandleValueVector argv,
const Types&... params)
225 template<
typename R,
typename ...Args>
226 static bool Call_(
const ScriptRequest& rq, JS::HandleValue val,
const char* name, R& ret,
const Args&... args)
228 JS::RootedObject obj(rq.
cx);
229 if (!JS_ValueToObject(rq.
cx, val, &obj) || !obj)
233 JS::RootedValue func(rq.
cx);
234 if (!JS_GetProperty(rq.
cx, obj, name, &func) || func.isUndefined())
237 JS::RootedValueVector argv(rq.
cx);
239 ToJSValVector(std::index_sequence_for<Args...>{}, rq, &argv, args...);
242 if constexpr (std::is_same_v<R, JS::MutableHandleValue>)
243 success = JS_CallFunctionValue(rq.
cx, obj, func, argv, ret);
246 JS::RootedValue jsRet(rq.
cx);
247 success = JS_CallFunctionValue(rq.
cx, obj, func, argv, &jsRet);
248 if constexpr (!std::is_same_v<R, IgnoreResult_t>)
263 template <
typename T>
268 template <
class callableType>
284 template <auto callable, GetterFor<decltype(callable)> thisGetter =
nullptr>
285 static bool ToJSNative(JSContext* cx,
unsigned argc, JS::Value* vp)
287 using ObjType =
typename args_info<
decltype(callable)>::object_type;
289 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
293 static_assert(std::is_same_v<
typename args_info<
decltype(callable)>::object_type,
void> || thisGetter !=
nullptr,
294 "ScriptFunction::Register - No getter specified for object method");
298#pragma GCC diagnostic push
299#pragma GCC diagnostic ignored "-Waddress"
301 ObjType* obj =
nullptr;
302 if constexpr (thisGetter !=
nullptr)
304 obj = thisGetter(rq, args);
309#pragma GCC diagnostic pop
314 static_cast<typename
args_info<decltype(callable)
>::arg_types*>(
nullptr));
324 if constexpr (std::is_same_v<void,
typename args_info<
decltype(callable)>::return_type>)
325 call<callable>(obj, outs);
326 else if constexpr (std::is_same_v<JS::Value,
typename args_info<
decltype(callable)>::return_type>)
327 args.rval().set(call<callable>(obj, outs));
339 template<
typename R,
typename ...Args>
340 static bool Call(
const ScriptRequest& rq, JS::HandleValue val,
const char* name, R& ret,
const Args&... args)
342 return Call_(rq, val, name, ret, std::forward<const Args>(args)...);
346 template<
typename ...Args>
347 static bool Call(
const ScriptRequest& rq, JS::HandleValue val,
const char* name, JS::MutableHandleValue ret,
const Args&... args)
349 return Call_(rq, val, name, ret, std::forward<const Args>(args)...);
356 template<
typename ...Args>
359 return Call(rq, val, name,
IgnoreResult, std::forward<const Args>(args)...);
367 template<
typename Callback>
369 JS::HandleValue arg, Callback yieldCallback)
371 JS::RootedValue generator{rq.
cx};
373 throw std::runtime_error{fmt::format(
"Failed to call the generator `{}`.", name)};
375 const auto continueGenerator = [&](
const char* property,
auto... args) -> JS::Value
377 JS::RootedValue iteratorResult{rq.
cx};
379 throw std::runtime_error{fmt::format(
"Failed to call `{}`.", name)};
380 return iteratorResult;
383 JS::PersistentRootedValue
error{rq.
cx, JS::UndefinedValue()};
386 JS::RootedValue iteratorResult{rq.
cx,
error.isUndefined() ? continueGenerator(
"next") :
387 continueGenerator(
"throw", std::exchange(
error, JS::UndefinedValue()))};
391 JS::RootedObject iteratorResultObject{rq.
cx, &iteratorResult.toObject()};
397 JS::RootedValue value{rq.
cx};
398 if (!JS_GetProperty(rq.
cx, iteratorResultObject,
"value", &value))
404 yieldCallback(value);
406 catch (
const std::exception& e)
410 throw std::runtime_error{
"Failed to construct `Error`."};
418 template <auto callable, GetterFor<decltype(callable)> thisGetter =
nullptr>
419 static JSFunctionSpec
Wrap(
const char* name,
420 const u16 flags = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
422 return JS_FN(name, (&ToJSNative<callable, thisGetter>),
args_info<
decltype(callable)>::nb_args, flags);
428 template <auto callable, GetterFor<decltype(callable)> thisGetter =
nullptr>
430 const u16 flags = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
432 return JS_NewFunction(rq.
cx, &ToJSNative<callable, thisGetter>,
args_info<
decltype(callable)>::nb_args, flags, name);
438 template <auto callable, GetterFor<decltype(callable)> thisGetter =
nullptr>
440 const u16 flags = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
442 JS_DefineFunction(rq.
cx, rq.
nativeScope, name, &ToJSNative<callable, thisGetter>,
args_info<
decltype(callable)>::nb_args, flags);
450 template <auto callable, GetterFor<decltype(callable)> thisGetter =
nullptr>
451 static void Register(JSContext* cx, JS::HandleObject scope,
const char* name,
452 const u16 flags = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
454 JS_DefineFunction(cx, scope, name, &ToJSNative<callable, thisGetter>,
args_info<
decltype(callable)>::nb_args, flags);
This class introduces templates to conveniently wrap C++ functions in JSNative functions.
Definition: FunctionWrapper.h:42
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.
Definition: FunctionWrapper.h:357
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++.
Definition: FunctionWrapper.h:226
ScriptFunction(const ScriptFunction &)=delete
static JSFunctionSpec Wrap(const char *name, const u16 flags=JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT)
Return a function spec from a C++ function.
Definition: FunctionWrapper.h:419
static bool Call(const ScriptRequest &rq, JS::HandleValue val, const char *name, JS::MutableHandleValue ret, const Args &... args)
Definition: FunctionWrapper.h:347
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.
Definition: FunctionWrapper.h:368
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').
Definition: FunctionWrapper.h:439
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.
Definition: FunctionWrapper.h:141
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.
Definition: FunctionWrapper.h:429
static IgnoreResult_t IgnoreResult
Definition: FunctionWrapper.h:203
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.
Definition: FunctionWrapper.h:213
ObjectGetter< typename args_info< callableType >::object_type > GetterFor
Definition: FunctionWrapper.h:269
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 ...
Definition: FunctionWrapper.h:156
static T DoConvertFromJS(const ScriptRequest &rq, JS::CallArgs &args, bool &wentOk)
DoConvertFromJS takes a type, a JS argument, and converts.
Definition: FunctionWrapper.h:106
static std::tuple< const ScriptInterface &, Types... > ConvertFromJS(const ScriptRequest &rq, JS::CallArgs &args, bool &wentOk, std::tuple< const ScriptInterface &, Types... > *)
Definition: FunctionWrapper.h:173
static bool ToJSNative(JSContext *cx, unsigned argc, JS::Value *vp)
The meat of this file.
Definition: FunctionWrapper.h:285
static void Register(JSContext *cx, JS::HandleObject scope, const char *name, const u16 flags=JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT)
Register a function on.
Definition: FunctionWrapper.h:451
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.
Definition: FunctionWrapper.h:187
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 > > > type_transform
In JS->C++ calls, types are converted using FromJSVal, and this requires them to be default-construct...
Definition: FunctionWrapper.h:62
ScriptFunction(ScriptFunction &&)=delete
static std::tuple< const ScriptRequest &, Types... > ConvertFromJS(const ScriptRequest &rq, JS::CallArgs &args, bool &wentOk, std::tuple< const ScriptRequest &, Types... > *)
Definition: FunctionWrapper.h:164
T *(*)(const ScriptRequest &, JS::CallArgs &) ObjectGetter
Definition: FunctionWrapper.h:264
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.
Definition: FunctionWrapper.h:340
Abstraction around a SpiderMonkey JS::Realm.
Definition: ScriptInterface.h:72
Spidermonkey maintains some 'local' state via the JSContext* object.
Definition: ScriptRequest.h:60
const ScriptInterface & GetScriptInterface() const
Return the scriptInterface active when creating this ScriptRequest.
Definition: ScriptInterface.cpp:98
JS::Value globalValue() const
Definition: ScriptInterface.cpp:93
JSContext * cx
Definition: ScriptRequest.h:92
JS::HandleObject nativeScope
Definition: ScriptRequest.h:94
#define UNUSED2(param)
mark a function local variable or parameter as unused and avoid the corresponding compiler warning.
Definition: code_annotation.h:58
void ignore_result(const T &)
Silence the 'unused result' warning.
Definition: code_annotation.h:67
bool IsPending(const ScriptRequest &rq)
Definition: ScriptExceptions.cpp:28
bool CatchPending(const ScriptRequest &rq)
Log and then clear the current pending exception.
Definition: ScriptExceptions.cpp:33
bool FromJSVal(const ScriptRequest &rq, const JS::HandleValue val, T &ret)
Convert a JS::Value to a C++ type.
void ToJSVal(const ScriptRequest &rq, JS::MutableHandleValue ret, T const &val)
Convert a C++ type to a JS::Value.
bool FromJSProperty(const ScriptRequest &rq, const JS::HandleValue val, const char *name, T &ret, bool strict=false)
Convert a named property of an object to a C++ type.
Definition: ScriptConversions.h:71
bool error(JSContext *cx, uint argc, JS::Value *vp)
Definition: ScriptInterface.cpp:173
Definition: ShaderDefines.cpp:31
#define T(string_literal)
Definition: secure_crt.cpp:77
Definition: FunctionWrapper.h:202
Definition: FunctionWrapper.h:86
IteratorResultError(const char *property)
Definition: FunctionWrapper.h:90
IteratorResultError(const std::string &property)
Definition: FunctionWrapper.h:87
std::tuple< type_transform< Types >... > arg_types
Definition: FunctionWrapper.h:77
void object_type
Definition: FunctionWrapper.h:76
R return_type
Definition: FunctionWrapper.h:75
Convenient struct to get info on a [class] [const] function pointer.
Definition: FunctionWrapper.h:69
uint16_t u16
Definition: types.h:38