Line data Source code
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 :
18 : #include <boost/preprocessor/punctuation/comma_if.hpp>
19 : #include <boost/preprocessor/repetition/repeat.hpp>
20 :
21 : // MaybeRef should be private, but has to be public due to a compiler bug in clang.
22 : // TODO: Make this private when the bug is fixed in all supported versions of clang.
23 : template <typename T> struct MaybeRef;
24 :
25 : // Define lots of useful macros:
26 :
27 : // Varieties of comma-separated list to fit on the head/tail/whole of another comma-separated list
28 : #define NUMBERED_LIST_HEAD(z, i, data) data##i,
29 : #define NUMBERED_LIST_TAIL(z, i, data) ,data##i
30 : #define NUMBERED_LIST_TAIL_MAYBE_REF(z, i, data) , typename MaybeRef<data##i>::Type
31 : #define NUMBERED_LIST_BALANCED(z, i, data) BOOST_PP_COMMA_IF(i) data##i
32 : #define NUMBERED_LIST_BALANCED_MAYBE_REF(z, i, data) BOOST_PP_COMMA_IF(i) typename MaybeRef<data##i>::Type
33 :
34 : // TODO: We allow optional parameters when the C++ type can be converted from JS::UndefinedValue.
35 : // FromJSVal is expected to either set a##i or return false (otherwise we could get undefined
36 : // behaviour because some types have undefined values when not being initialized).
37 : // This is not very clear and also a bit fragile. Another problem is that the error reporting lacks
38 : // a bit. SpiderMonkey will throw a JS exception and abort the execution of the current function when
39 : // we return false here (without printing a callstack or additional detail telling that an argument
40 : // conversion failed). So we have two TODOs here:
41 : // 1. On the conceptual side: How to consistently work with optional parameters (or drop them completely?)
42 : // 2. On the technical side: Improve error handling, find a better way to ensure parameters are initialized
43 : #define CONVERT_ARG(z, i, data) \
44 : bool typeConvRet##i; \
45 : T##i a##i = ScriptInterface::AssignOrFromJSVal<T##i>( \
46 : rq, \
47 : i < args.length() ? args[i] : JS::UndefinedHandleValue, \
48 : typeConvRet##i); \
49 : if (!typeConvRet##i) return false;
50 :
51 : // List-generating macros, named roughly after their first list item
52 : #define TYPENAME_T0_HEAD(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_HEAD, typename T) // "typename T0, typename T1, "
53 : #define T0(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_BALANCED, T) // "T0, T1"
54 : #define T0_MAYBE_REF(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_BALANCED_MAYBE_REF, T) // "const T0&, T1"
55 : #define T0_TAIL(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_TAIL, T) // ", T0, T1"
56 : #define T0_TAIL_MAYBE_REF(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_TAIL_MAYBE_REF, T) // ", const T0&, T1"
57 : #define A0_TAIL(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_TAIL, a) // ", a0, a1"
58 :
59 : // Define RegisterFunction<TR, T0..., f>
60 : #define OVERLOADS(z, i, data) \
61 : template <typename R, TYPENAME_T0_HEAD(z,i) R (*fptr) ( ScriptInterface::CmptPrivate* T0_TAIL_MAYBE_REF(z,i) )> \
62 : void RegisterFunction(const char* name) const \
63 : { \
64 : Register(name, call<R T0_TAIL(z,i), fptr>, nargs<T0(z,i)>()); \
65 : }
66 295 : BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
67 : #undef OVERLOADS
68 :
69 : // JSFastNative-compatible function that wraps the function identified in the template argument list
70 : // (Definition comes later, since it depends on some things we haven't defined yet)
71 : #define OVERLOADS(z, i, data) \
72 : template <typename R, TYPENAME_T0_HEAD(z,i) R (*fptr) ( ScriptInterface::CmptPrivate* T0_TAIL_MAYBE_REF(z,i) )> \
73 : static bool call(JSContext* cx, uint argc, JS::Value* vp);
74 : BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
75 : #undef OVERLOADS
76 :
77 : // Similar, for class methods
78 : #define OVERLOADS(z, i, data) \
79 : template <typename R, TYPENAME_T0_HEAD(z,i) JSClass* CLS, typename TC, R (TC::*fptr) ( T0_MAYBE_REF(z,i) )> \
80 : static bool callMethod(JSContext* cx, uint argc, JS::Value* vp);
81 : BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
82 : #undef OVERLOADS
83 :
84 : // const methods
85 : #define OVERLOADS(z, i, data) \
86 : template <typename R, TYPENAME_T0_HEAD(z,i) JSClass* CLS, typename TC, R (TC::*fptr) ( T0_MAYBE_REF(z,i) ) const> \
87 : static bool callMethodConst(JSContext* cx, uint argc, JS::Value* vp);
88 : BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
89 : #undef OVERLOADS
90 :
91 : // Argument-number counter
92 : template<typename... Ts>
93 : static size_t nargs() { return sizeof...(Ts); }
94 :
95 : // Call the named property on the given object
96 : template<typename R, typename... Ts>
97 : bool CallFunction(JS::HandleValue val, const char* name, R& ret, const Ts&... params) const;
98 :
99 : // Implicit conversion from JS::Rooted<R>* to JS::MutableHandle<R> does not work with template argument deduction
100 : // (only exact type matches allowed). We need this overload to allow passing Rooted<R>* using the & operator
101 : // (as people would expect it to work based on the SpiderMonkey rooting guide).
102 : template<typename R, typename... Ts>
103 : bool CallFunction(JS::HandleValue val, const char* name, JS::Rooted<R>* ret, const Ts&... params) const;
104 :
105 : // This overload is for the case when a JS::MutableHandle<R> type gets passed into CallFunction directly and
106 : // without requiring implicit conversion.
107 : template<typename R, typename... Ts>
108 : bool CallFunction(JS::HandleValue val, const char* name, JS::MutableHandle<R> ret, const Ts&... params) const;
109 :
110 : // Call the named property on the given object, with void return type
111 : template<typename... Ts> \
112 : bool CallFunctionVoid(JS::HandleValue val, const char* name, const Ts&... params) const;
|