/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ // Use the macro below to define types that will be passed by value to C++ functions. // NOTE: References are used just to avoid superfluous copy constructor calls // in the script wrapper code. They cannot be used as out-parameters. // They are const T& by default to avoid confusion about this, especially // because sometimes the function is not just exposed to scripts, but also // called from C++ code. template struct ScriptInterface::MaybeRef { typedef const T& Type; }; #define PASS_BY_VALUE_IN_NATIVE_WRAPPER(T) \ template <> struct ScriptInterface::MaybeRef \ { \ typedef T Type; \ }; \ PASS_BY_VALUE_IN_NATIVE_WRAPPER(JS::HandleValue) PASS_BY_VALUE_IN_NATIVE_WRAPPER(bool) PASS_BY_VALUE_IN_NATIVE_WRAPPER(int) PASS_BY_VALUE_IN_NATIVE_WRAPPER(uint8_t) PASS_BY_VALUE_IN_NATIVE_WRAPPER(uint16_t) PASS_BY_VALUE_IN_NATIVE_WRAPPER(uint32_t) PASS_BY_VALUE_IN_NATIVE_WRAPPER(fixed) PASS_BY_VALUE_IN_NATIVE_WRAPPER(float) PASS_BY_VALUE_IN_NATIVE_WRAPPER(double) #undef PASS_BY_VALUE_IN_NATIVE_WRAPPER // This works around a bug in Visual Studio (error C2244 if ScriptInterface:: is included in the // type specifier of MaybeRef::Type for parameters inside the member function declaration). // It's probably the bug described here, but I'm not quite sure (at least the example there still // cause error C2244): // https://connect.microsoft.com/VisualStudio/feedback/details/611863/vs2010-c-fails-with-error-c2244-gcc-4-3-4-compiles-ok // // TODO: When dropping support for VS 2015, check if this bug is still present in the supported // Visual Studio versions (replace the macro definitions in NativeWrapperDecls.h with these ones, // remove them from here and check if this causes error C2244 when compiling. #undef NUMBERED_LIST_TAIL_MAYBE_REF #undef NUMBERED_LIST_BALANCED_MAYBE_REF #define NUMBERED_LIST_TAIL_MAYBE_REF(z, i, data) , typename ScriptInterface::MaybeRef::Type #define NUMBERED_LIST_BALANCED_MAYBE_REF(z, i, data) BOOST_PP_COMMA_IF(i) typename ScriptInterface::MaybeRef::Type // (NativeWrapperDecls.h set up a lot of the macros we use here) // ScriptInterface_NativeWrapper::call(rq, rval, fptr, args...) will call fptr(cbdata, args...), // and if T != void then it will store the result in rval: // Templated on the return type so void can be handled separately template struct ScriptInterface_NativeWrapper { template static void call(const ScriptRequest& rq, JS::MutableHandleValue rval, F fptr, Ts... params) { ScriptInterface::AssignOrToJSValUnrooted(rq, rval, fptr(ScriptInterface::GetScriptInterfaceAndCBData(rq.cx), params...)); } }; // Overloaded to ignore the return value from void functions template <> struct ScriptInterface_NativeWrapper { template static void call(const ScriptRequest& rq, JS::MutableHandleValue UNUSED(rval), F fptr, Ts... params) { fptr(ScriptInterface::GetScriptInterfaceAndCBData(rq.cx), params...); } }; // Same idea but for method calls: template struct ScriptInterface_NativeMethodWrapper { template static void call(const ScriptRequest& rq, JS::MutableHandleValue rval, TC* c, F fptr, Ts... params) { ScriptInterface::AssignOrToJSValUnrooted(rq, rval, (c->*fptr)(params...)); } }; template struct ScriptInterface_NativeMethodWrapper { template static void call(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue UNUSED(rval), TC* c, F fptr, Ts... params) { (c->*fptr)(params...); } }; // JSFastNative-compatible function that wraps the function identified in the template argument list #define OVERLOADS(z, i, data) \ template \ bool ScriptInterface::call(JSContext* cx, uint argc, JS::Value* vp) \ { \ JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \ ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \ BOOST_PP_REPEAT_##z (i, CONVERT_ARG, ~) \ JS::RootedValue rval(rq.cx); \ ScriptInterface_NativeWrapper::template call(rq, &rval, fptr A0_TAIL(z,i)); \ args.rval().set(rval); \ return !ScriptException::IsPending(rq); \ } BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~) #undef OVERLOADS // Same idea but for methods #define OVERLOADS(z, i, data) \ template \ bool ScriptInterface::callMethod(JSContext* cx, uint argc, JS::Value* vp) \ { \ JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \ ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \ TC* c = ScriptInterface::GetPrivate(rq, args, CLS); \ if (! c) return false; \ BOOST_PP_REPEAT_##z (i, CONVERT_ARG, ~) \ JS::RootedValue rval(rq.cx); \ ScriptInterface_NativeMethodWrapper::template call(rq, &rval, c, fptr A0_TAIL(z,i)); \ args.rval().set(rval); \ return !ScriptException::IsPending(rq); \ } BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~) #undef OVERLOADS // const methods #define OVERLOADS(z, i, data) \ template \ bool ScriptInterface::callMethodConst(JSContext* cx, uint argc, JS::Value* vp) \ { \ JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \ ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \ TC* c = ScriptInterface::GetPrivate(rq, args, CLS); \ if (! c) return false; \ BOOST_PP_REPEAT_##z (i, CONVERT_ARG, ~) \ JS::RootedValue rval(rq.cx); \ ScriptInterface_NativeMethodWrapper::template call(rq, &rval, c, fptr A0_TAIL(z,i)); \ args.rval().set(rval); \ return !ScriptException::IsPending(rq); \ } BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~) #undef OVERLOADS template static void AssignOrToJSValHelper(const ScriptRequest& rq, JS::MutableHandleValueVector argv, const T& a, const Ts&... params) { ScriptInterface::AssignOrToJSVal(rq, argv[i], a); AssignOrToJSValHelper(rq, argv, params...); } template static void AssignOrToJSValHelper(const ScriptRequest& UNUSED(rq), JS::MutableHandleValueVector UNUSED(argv)) { cassert(sizeof...(Ts) == 0); // Nop, for terminating the template recursion. } template bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, R& ret, const Ts&... params) const { ScriptRequest rq(this); JS::RootedValue jsRet(rq.cx); JS::RootedValueVector argv(rq.cx); ignore_result(argv.resize(sizeof...(Ts))); AssignOrToJSValHelper<0>(rq, &argv, params...); if (!CallFunction_(val, name, argv, &jsRet)) return false; return FromJSVal(rq, jsRet, ret); } template bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, JS::Rooted* ret, const Ts&... params) const { ScriptRequest rq(this); JS::MutableHandle jsRet(ret); JS::RootedValueVector argv(rq.cx); ignore_result(argv.resize(sizeof...(Ts))); AssignOrToJSValHelper<0>(rq, &argv, params...); return CallFunction_(val, name, argv, jsRet); } template bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, JS::MutableHandle ret, const Ts&... params) const { ScriptRequest rq(this); JS::RootedValueVector argv(rq.cx); ignore_result(argv.resize(sizeof...(Ts))); AssignOrToJSValHelper<0>(rq, &argv, params...); return CallFunction_(val, name, argv, ret); } // Call the named property on the given object, with void return type template bool ScriptInterface::CallFunctionVoid(JS::HandleValue val, const char* name, const Ts&... params) const { ScriptRequest rq(this); JS::RootedValue jsRet(rq.cx); JS::RootedValueVector argv(rq.cx); ignore_result(argv.resize(sizeof...(Ts))); AssignOrToJSValHelper<0>(rq, &argv, params...); return CallFunction_(val, name, argv, &jsRet); } // Clean up our mess #undef NUMBERED_LIST_HEAD #undef NUMBERED_LIST_TAIL #undef NUMBERED_LIST_TAIL_MAYBE_REF #undef NUMBERED_LIST_BALANCED #undef NUMBERED_LIST_BALANCED_MAYBE_REF #undef CONVERT_ARG #undef TYPENAME_T0_HEAD #undef T0 #undef T0_MAYBE_REF #undef T0_TAIL #undef T0_TAIL_MAYBE_REF #undef A0_TAIL