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