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
|