Line data Source code
1 : /* Copyright (C) 2021 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 : #ifndef INCLUDED_SCRIPTCONVERSIONS
19 : #define INCLUDED_SCRIPTCONVERSIONS
20 :
21 : #include "ScriptRequest.h"
22 : #include "ScriptExceptions.h"
23 : #include "ScriptExtraHeaders.h" // for typed arrays
24 :
25 : #include <limits>
26 : #include <vector>
27 :
28 : namespace Script
29 : {
30 : /**
31 : * Convert a JS::Value to a C++ type. (This might trigger GC.)
32 : */
33 : template<typename T> bool FromJSVal(const ScriptRequest& rq, const JS::HandleValue val, T& ret);
34 :
35 : /**
36 : * Convert a C++ type to a JS::Value. (This might trigger GC. The return
37 : * value must be rooted if you don't want it to be collected.)
38 : * NOTE: We are passing the JS::Value by reference instead of returning it by value.
39 : * The reason is a memory corruption problem that appears to be caused by a bug in Visual Studio.
40 : * Details here: http://www.wildfiregames.com/forum/index.php?showtopic=17289&p=285921
41 : */
42 : template<typename T> void ToJSVal(const ScriptRequest& rq, JS::MutableHandleValue ret, T const& val);
43 :
44 : template<>
45 3 : inline void ToJSVal<JS::PersistentRootedValue>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue handle, const JS::PersistentRootedValue& a)
46 : {
47 3 : handle.set(a);
48 3 : }
49 :
50 : template<>
51 0 : inline void ToJSVal<JS::Heap<JS::Value> >(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue handle, const JS::Heap<JS::Value>& a)
52 : {
53 0 : handle.set(a);
54 0 : }
55 :
56 : template<>
57 69 : inline void ToJSVal<JS::RootedValue>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue handle, const JS::RootedValue& a)
58 : {
59 69 : handle.set(a);
60 69 : }
61 :
62 : template <>
63 84 : inline void ToJSVal<JS::HandleValue>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue handle, const JS::HandleValue& a)
64 : {
65 84 : handle.set(a);
66 84 : }
67 :
68 : /**
69 : * Convert a named property of an object to a C++ type.
70 : */
71 170 : template<typename T> inline bool FromJSProperty(const ScriptRequest& rq, const JS::HandleValue val, const char* name, T& ret, bool strict = false)
72 : {
73 170 : if (!val.isObject())
74 4 : return false;
75 :
76 332 : JS::RootedObject obj(rq.cx, &val.toObject());
77 :
78 : bool hasProperty;
79 166 : if (!JS_HasProperty(rq.cx, obj, name, &hasProperty) || !hasProperty)
80 13 : return false;
81 :
82 306 : JS::RootedValue value(rq.cx);
83 153 : if (!JS_GetProperty(rq.cx, obj, name, &value))
84 0 : return false;
85 :
86 153 : if (strict && value.isNull())
87 7 : return false;
88 :
89 146 : return FromJSVal(rq, value, ret);
90 : }
91 :
92 0 : template<typename T> inline void ToJSVal_vector(const ScriptRequest& rq, JS::MutableHandleValue ret, const std::vector<T>& val)
93 : {
94 0 : JS::RootedObject obj(rq.cx, JS::NewArrayObject(rq.cx, 0));
95 0 : if (!obj)
96 : {
97 0 : ret.setUndefined();
98 0 : return;
99 : }
100 :
101 0 : ENSURE(val.size() <= std::numeric_limits<u32>::max());
102 0 : for (u32 i = 0; i < val.size(); ++i)
103 : {
104 0 : JS::RootedValue el(rq.cx);
105 0 : Script::ToJSVal<T>(rq, &el, val[i]);
106 0 : JS_SetElement(rq.cx, obj, i, el);
107 : }
108 0 : ret.setObject(*obj);
109 : }
110 :
111 : #define FAIL(msg) STMT(ScriptException::Raise(rq, msg); return false)
112 :
113 6 : template<typename T> inline bool FromJSVal_vector(const ScriptRequest& rq, JS::HandleValue v, std::vector<T>& out)
114 : {
115 12 : JS::RootedObject obj(rq.cx);
116 6 : if (!v.isObject())
117 0 : FAIL("Argument must be an array");
118 :
119 : bool isArray;
120 6 : obj = &v.toObject();
121 6 : if ((!JS::IsArrayObject(rq.cx, obj, &isArray) || !isArray) && !JS_IsTypedArrayObject(obj))
122 0 : FAIL("Argument must be an array");
123 :
124 : u32 length;
125 6 : if (!JS::GetArrayLength(rq.cx, obj, &length))
126 0 : FAIL("Failed to get array length");
127 :
128 6 : out.clear();
129 6 : out.reserve(length);
130 7 : for (u32 i = 0; i < length; ++i)
131 : {
132 2 : JS::RootedValue el(rq.cx);
133 1 : if (!JS_GetElement(rq.cx, obj, i, &el))
134 0 : FAIL("Failed to read array element");
135 2 : T el2;
136 1 : if (!Script::FromJSVal<T>(rq, el, el2))
137 0 : return false;
138 1 : out.push_back(el2);
139 : }
140 6 : return true;
141 : }
142 :
143 : #undef FAIL
144 :
145 : #define JSVAL_VECTOR(T) \
146 : template<> void Script::ToJSVal<std::vector<T> >(const ScriptRequest& rq, JS::MutableHandleValue ret, const std::vector<T>& val) \
147 : { \
148 : ToJSVal_vector(rq, ret, val); \
149 : } \
150 : template<> bool Script::FromJSVal<std::vector<T> >(const ScriptRequest& rq, JS::HandleValue v, std::vector<T>& out) \
151 : { \
152 : return FromJSVal_vector(rq, v, out); \
153 : }
154 :
155 : } // namespace Script
156 :
157 : #endif //INCLUDED_SCRIPTCONVERSIONS
|