Pyrogenesis  trunk
ScriptConversions.h
Go to the documentation of this file.
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 inline void ToJSVal<JS::PersistentRootedValue>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue handle, const JS::PersistentRootedValue& a)
46 {
47  handle.set(a);
48 }
49 
50 template<>
51 inline void ToJSVal<JS::Heap<JS::Value> >(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue handle, const JS::Heap<JS::Value>& a)
52 {
53  handle.set(a);
54 }
55 
56 template<>
57 inline void ToJSVal<JS::RootedValue>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue handle, const JS::RootedValue& a)
58 {
59  handle.set(a);
60 }
61 
62 template <>
63 inline void ToJSVal<JS::HandleValue>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue handle, const JS::HandleValue& a)
64 {
65  handle.set(a);
66 }
67 
68 /**
69  * Convert a named property of an object to a C++ type.
70  */
71 template<typename T> inline bool FromJSProperty(const ScriptRequest& rq, const JS::HandleValue val, const char* name, T& ret, bool strict = false)
72 {
73  if (!val.isObject())
74  return false;
75 
76  JS::RootedObject obj(rq.cx, &val.toObject());
77 
78  bool hasProperty;
79  if (!JS_HasProperty(rq.cx, obj, name, &hasProperty) || !hasProperty)
80  return false;
81 
82  JS::RootedValue value(rq.cx);
83  if (!JS_GetProperty(rq.cx, obj, name, &value))
84  return false;
85 
86  if (strict && value.isNull())
87  return false;
88 
89  return FromJSVal(rq, value, ret);
90 }
91 
92 template<typename T> inline void ToJSVal_vector(const ScriptRequest& rq, JS::MutableHandleValue ret, const std::vector<T>& val)
93 {
94  JS::RootedObject obj(rq.cx, JS::NewArrayObject(rq.cx, 0));
95  if (!obj)
96  {
97  ret.setUndefined();
98  return;
99  }
100 
101  ENSURE(val.size() <= std::numeric_limits<u32>::max());
102  for (u32 i = 0; i < val.size(); ++i)
103  {
104  JS::RootedValue el(rq.cx);
105  Script::ToJSVal<T>(rq, &el, val[i]);
106  JS_SetElement(rq.cx, obj, i, el);
107  }
108  ret.setObject(*obj);
109 }
110 
111 #define FAIL(msg) STMT(ScriptException::Raise(rq, msg); return false)
112 
113 template<typename T> inline bool FromJSVal_vector(const ScriptRequest& rq, JS::HandleValue v, std::vector<T>& out)
114 {
115  JS::RootedObject obj(rq.cx);
116  if (!v.isObject())
117  FAIL("Argument must be an array");
118 
119  bool isArray;
120  obj = &v.toObject();
121  if ((!JS::IsArrayObject(rq.cx, obj, &isArray) || !isArray) && !JS_IsTypedArrayObject(obj))
122  FAIL("Argument must be an array");
123 
124  u32 length;
125  if (!JS::GetArrayLength(rq.cx, obj, &length))
126  FAIL("Failed to get array length");
127 
128  out.clear();
129  out.reserve(length);
130  for (u32 i = 0; i < length; ++i)
131  {
132  JS::RootedValue el(rq.cx);
133  if (!JS_GetElement(rq.cx, obj, i, &el))
134  FAIL("Failed to read array element");
135  T el2;
136  if (!Script::FromJSVal<T>(rq, el, el2))
137  return false;
138  out.push_back(el2);
139  }
140  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
JSContext * cx
Definition: ScriptRequest.h:92
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
Definition: code_annotation.h:38
void ToJSVal_vector(const ScriptRequest &rq, JS::MutableHandleValue ret, const std::vector< T > &val)
Definition: ScriptConversions.h:92
static void out(const wchar_t *fmt,...)
Definition: wdbg_sym.cpp:421
Wraps SM APIs for manipulating JS objects.
Definition: JSON.h:34
bool FromJSProperty(const ScriptRequest &rq, const JS::HandleValue val, const char *name, T &ret, bool strict=false)
Convert a named property of an object to a C++ type.
Definition: ScriptConversions.h:71
#define ENSURE(expr)
ensure the expression <expr> evaluates to non-zero.
Definition: debug.h:290
uint32_t u32
Definition: types.h:39
#define FAIL(msg)
Definition: ScriptConversions.h:111
void ToJSVal(const ScriptRequest &rq, JS::MutableHandleValue ret, T const &val)
Convert a C++ type to a JS::Value.
bool FromJSVal_vector(const ScriptRequest &rq, JS::HandleValue v, std::vector< T > &out)
Definition: ScriptConversions.h:113
#define T(string_literal)
Definition: secure_crt.cpp:77
Spidermonkey maintains some &#39;local&#39; state via the JSContext* object.
Definition: ScriptRequest.h:59
bool FromJSVal(const ScriptRequest &rq, const JS::HandleValue val, T &ret)
Convert a JS::Value to a C++ type.