LCOV - code coverage report
Current view: top level - source/scriptinterface - NativeWrapperDefns.h (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 84 239 35.1 %
Date: 2021-04-16 20:41:45 Functions: 12 348 3.4 %

          Line data    Source code
       1             : /* Copyright (C) 2020 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : // Use the macro below to define types that will be passed by value to C++ functions.
      19             : // NOTE: References are used just to avoid superfluous copy constructor calls
      20             : // in the script wrapper code. They cannot be used as out-parameters.
      21             : // They are const T& by default to avoid confusion about this, especially
      22             : // because sometimes the function is not just exposed to scripts, but also
      23             : // called from C++ code.
      24             : 
      25             : template <typename T> struct ScriptInterface::MaybeRef
      26             : {
      27             :     typedef const T& Type;
      28             : };
      29             : 
      30             : #define PASS_BY_VALUE_IN_NATIVE_WRAPPER(T) \
      31             : template <> struct ScriptInterface::MaybeRef<T> \
      32             : { \
      33             :     typedef T Type; \
      34             : }; \
      35             : 
      36             : PASS_BY_VALUE_IN_NATIVE_WRAPPER(JS::HandleValue)
      37             : PASS_BY_VALUE_IN_NATIVE_WRAPPER(bool)
      38             : PASS_BY_VALUE_IN_NATIVE_WRAPPER(int)
      39             : PASS_BY_VALUE_IN_NATIVE_WRAPPER(uint8_t)
      40             : PASS_BY_VALUE_IN_NATIVE_WRAPPER(uint16_t)
      41             : PASS_BY_VALUE_IN_NATIVE_WRAPPER(uint32_t)
      42             : PASS_BY_VALUE_IN_NATIVE_WRAPPER(fixed)
      43             : PASS_BY_VALUE_IN_NATIVE_WRAPPER(float)
      44             : PASS_BY_VALUE_IN_NATIVE_WRAPPER(double)
      45             : 
      46             : #undef PASS_BY_VALUE_IN_NATIVE_WRAPPER
      47             : 
      48             : // This works around a bug in Visual Studio (error C2244 if ScriptInterface:: is included in the
      49             : // type specifier of MaybeRef<T>::Type for parameters inside the member function declaration).
      50             : // It's probably the bug described here, but I'm not quite sure (at least the example there still
      51             : // cause error C2244):
      52             : // https://connect.microsoft.com/VisualStudio/feedback/details/611863/vs2010-c-fails-with-error-c2244-gcc-4-3-4-compiles-ok
      53             : //
      54             : // TODO: When dropping support for VS 2015, check if this bug is still present in the supported
      55             : // Visual Studio versions (replace the macro definitions in NativeWrapperDecls.h with these ones,
      56             : // remove them from here and check if this causes error C2244 when compiling.
      57             : #undef NUMBERED_LIST_TAIL_MAYBE_REF
      58             : #undef NUMBERED_LIST_BALANCED_MAYBE_REF
      59             : #define NUMBERED_LIST_TAIL_MAYBE_REF(z, i, data) , typename ScriptInterface::MaybeRef<data##i>::Type
      60             : #define NUMBERED_LIST_BALANCED_MAYBE_REF(z, i, data) BOOST_PP_COMMA_IF(i) typename ScriptInterface::MaybeRef<data##i>::Type
      61             : 
      62             : // (NativeWrapperDecls.h set up a lot of the macros we use here)
      63             : 
      64             : // ScriptInterface_NativeWrapper<T>::call(rq, rval, fptr, args...) will call fptr(cbdata, args...),
      65             : // and if T != void then it will store the result in rval:
      66             : 
      67             : // Templated on the return type so void can be handled separately
      68             : template <typename R>
      69             : struct ScriptInterface_NativeWrapper
      70             : {
      71             :     template<typename F, typename... Ts>
      72             :     static void call(const ScriptRequest& rq, JS::MutableHandleValue rval, F fptr, Ts... params)
      73             :     {
      74             :         ScriptInterface::AssignOrToJSValUnrooted<R>(rq, rval, fptr(ScriptInterface::GetScriptInterfaceAndCBData(rq.cx), params...));
      75             :     }
      76             : };
      77             : 
      78             : // Overloaded to ignore the return value from void functions
      79             : template <>
      80             : struct ScriptInterface_NativeWrapper<void>
      81             : {
      82             :     template<typename F, typename... Ts>
      83             :     static void call(const ScriptRequest& rq, JS::MutableHandleValue UNUSED(rval), F fptr, Ts... params)
      84             :     {
      85             :         fptr(ScriptInterface::GetScriptInterfaceAndCBData(rq.cx), params...);
      86             :     }
      87             : };
      88             : 
      89             : // Same idea but for method calls:
      90             : 
      91             : template <typename R, typename TC>
      92             : struct ScriptInterface_NativeMethodWrapper
      93             : {
      94             :     template<typename F, typename... Ts>
      95           1 :     static void call(const ScriptRequest& rq, JS::MutableHandleValue rval, TC* c, F fptr, Ts... params)
      96             :     {
      97           1 :         ScriptInterface::AssignOrToJSValUnrooted<R>(rq, rval, (c->*fptr)(params...));
      98           1 :     }
      99           0 : };
     100             : 
     101           0 : template <typename TC>
     102           0 : struct ScriptInterface_NativeMethodWrapper<void, TC>
     103           0 : {
     104             :     template<typename F, typename... Ts>
     105           0 :     static void call(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue UNUSED(rval), TC* c, F fptr, Ts... params)
     106           0 :     {
     107           0 :         (c->*fptr)(params...);
     108           0 :     }
     109           0 : };
     110           0 : 
     111           0 : // JSFastNative-compatible function that wraps the function identified in the template argument list
     112           0 : #define OVERLOADS(z, i, data) \
     113           0 :     template <typename R, TYPENAME_T0_HEAD(z,i)  R (*fptr) ( ScriptInterface::CmptPrivate* T0_TAIL_MAYBE_REF(z,i) )> \
     114           0 :     bool ScriptInterface::call(JSContext* cx, uint argc, JS::Value* vp) \
     115           0 :     { \
     116           0 :         JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
     117           0 :         ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
     118           0 :         BOOST_PP_REPEAT_##z (i, CONVERT_ARG, ~) \
     119           0 :         JS::RootedValue rval(rq.cx); \
     120           0 :         ScriptInterface_NativeWrapper<R>::template call<R( ScriptInterface::CmptPrivate* T0_TAIL_MAYBE_REF(z,i))  T0_TAIL(z,i)>(rq, &rval, fptr  A0_TAIL(z,i)); \
     121           0 :         args.rval().set(rval); \
     122           0 :         return !ScriptException::IsPending(rq); \
     123           0 :     }
     124           0 : BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
     125           0 : #undef OVERLOADS
     126           0 : 
     127           0 : // Same idea but for methods
     128           0 : #define OVERLOADS(z, i, data) \
     129           0 :     template <typename R, TYPENAME_T0_HEAD(z,i)  JSClass* CLS, typename TC, R (TC::*fptr) ( T0_MAYBE_REF(z,i) )> \
     130           0 :     bool ScriptInterface::callMethod(JSContext* cx, uint argc, JS::Value* vp) \
     131           0 :     { \
     132           0 :         JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
     133           0 :         ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
     134           0 :         TC* c = ScriptInterface::GetPrivate<TC>(rq, args, CLS); \
     135           0 :         if (! c) return false; \
     136           0 :         BOOST_PP_REPEAT_##z (i, CONVERT_ARG, ~) \
     137           0 :         JS::RootedValue rval(rq.cx); \
     138           0 :         ScriptInterface_NativeMethodWrapper<R, TC>::template call<R (TC::*)(T0_MAYBE_REF(z,i))  T0_TAIL(z,i)>(rq, &rval, c, fptr A0_TAIL(z,i)); \
     139           0 :         args.rval().set(rval); \
     140           0 :         return !ScriptException::IsPending(rq); \
     141           0 :     }
     142           1 : BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
     143           0 : #undef OVERLOADS
     144           0 : 
     145           0 : // const methods
     146           0 : #define OVERLOADS(z, i, data) \
     147           0 :     template <typename R, TYPENAME_T0_HEAD(z,i)  JSClass* CLS, typename TC, R (TC::*fptr) ( T0_MAYBE_REF(z,i) ) const> \
     148           0 :     bool ScriptInterface::callMethodConst(JSContext* cx, uint argc, JS::Value* vp) \
     149           0 :     { \
     150           0 :         JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
     151           0 :         ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
     152           0 :         TC* c = ScriptInterface::GetPrivate<TC>(rq, args, CLS); \
     153           0 :         if (! c) return false; \
     154           0 :         BOOST_PP_REPEAT_##z (i, CONVERT_ARG, ~) \
     155           0 :         JS::RootedValue rval(rq.cx); \
     156             :         ScriptInterface_NativeMethodWrapper<R, TC>::template call<R (TC::*)(T0_MAYBE_REF(z,i)) const  T0_TAIL(z,i)>(rq, &rval, c, fptr A0_TAIL(z,i)); \
     157           0 :         args.rval().set(rval); \
     158           0 :         return !ScriptException::IsPending(rq); \
     159           0 :     }
     160           0 : BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
     161           0 : #undef OVERLOADS
     162           0 : 
     163             : template<int i, typename T, typename... Ts>
     164           7 : static void AssignOrToJSValHelper(const ScriptRequest& rq, JS::MutableHandleValueVector argv, const T& a, const Ts&... params)
     165             : {
     166          95 :     ScriptInterface::AssignOrToJSVal(rq, argv[i], a);
     167          81 :     AssignOrToJSValHelper<i+1>(rq, argv, params...);
     168           7 : }
     169           0 : 
     170             : template<int i, typename... Ts>
     171           0 : static void AssignOrToJSValHelper(const ScriptRequest& UNUSED(rq), JS::MutableHandleValueVector UNUSED(argv))
     172           0 : {
     173           0 :     cassert(sizeof...(Ts) == 0);
     174           7 :     // Nop, for terminating the template recursion.
     175           0 : }
     176          14 : 
     177           7 : template<typename R, typename... Ts>
     178         128 : bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, R& ret, const Ts&... params) const
     179           0 : {
     180         121 :     ScriptRequest rq(this);
     181         242 :     JS::RootedValue jsRet(rq.cx);
     182         242 :     JS::RootedValueVector argv(rq.cx);
     183         121 :     ignore_result(argv.resize(sizeof...(Ts)));
     184         170 :     AssignOrToJSValHelper<0>(rq, &argv, params...);
     185         363 :     if (!CallFunction_(val, name, argv, &jsRet))
     186           0 :         return false;
     187         242 :     return FromJSVal(rq, jsRet, ret);
     188           0 : }
     189           0 : 
     190           0 : template<typename R, typename... Ts>
     191           0 : bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, JS::Rooted<R>* ret, const Ts&... params) const
     192           0 : {
     193           0 :     ScriptRequest rq(this);
     194           0 :     JS::MutableHandle<R> jsRet(ret);
     195           0 :     JS::RootedValueVector argv(rq.cx);
     196           0 :     ignore_result(argv.resize(sizeof...(Ts)));
     197           0 :     AssignOrToJSValHelper<0>(rq, &argv, params...);
     198          46 :     return CallFunction_(val, name, argv, jsRet);
     199           0 : }
     200          46 : 
     201          92 : template<typename R, typename... Ts>
     202          92 : bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, JS::MutableHandle<R> ret, const Ts&... params) const
     203          46 : {
     204          48 :     ScriptRequest rq(this);
     205         138 :     JS::RootedValueVector argv(rq.cx);
     206           0 :     ignore_result(argv.resize(sizeof...(Ts)));
     207          92 :     AssignOrToJSValHelper<0>(rq, &argv, params...);
     208          14 :     return CallFunction_(val, name, argv, ret);
     209           1 : }
     210          14 : 
     211          29 : // Call the named property on the given object, with void return type
     212          30 : template<typename... Ts>
     213          19 : bool ScriptInterface::CallFunctionVoid(JS::HandleValue val, const char* name, const Ts&... params) const
     214          29 : {
     215          47 :     ScriptRequest rq(this);
     216           9 :     JS::RootedValue jsRet(rq.cx);
     217          34 :     JS::RootedValueVector argv(rq.cx);
     218           5 :     ignore_result(argv.resize(sizeof...(Ts)));
     219          13 :     AssignOrToJSValHelper<0>(rq, &argv, params...);
     220          13 :     return CallFunction_(val, name, argv, &jsRet);
     221           7 : }
     222          15 : 
     223          16 : // Clean up our mess
     224           9 : #undef NUMBERED_LIST_HEAD
     225          15 : #undef NUMBERED_LIST_TAIL
     226          23 : #undef NUMBERED_LIST_TAIL_MAYBE_REF
     227           3 : #undef NUMBERED_LIST_BALANCED
     228          14 : #undef NUMBERED_LIST_BALANCED_MAYBE_REF
     229           2 : #undef CONVERT_ARG
     230           7 : #undef TYPENAME_T0_HEAD
     231          44 : #undef T0
     232           7 : #undef T0_MAYBE_REF
     233          58 : #undef T0_TAIL
     234         102 : #undef T0_TAIL_MAYBE_REF
     235          95 : #undef A0_TAIL

Generated by: LCOV version 1.13