Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ScriptConversions.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2012 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 "precompiled.h"
19 
20 #include "ScriptInterface.h"
21 
22 #include "graphics/Entity.h"
23 #include "ps/utf16string.h"
24 #include "ps/CLogger.h"
25 #include "ps/CStr.h"
26 #include "scriptinterface/ScriptExtraHeaders.h" // for typed arrays
27 
28 #define FAIL(msg) STMT(JS_ReportError(cx, msg); return false)
29 
30 // Implicit type conversions often hide bugs, so warn about them
31 #define WARN_IF_NOT(c, v) STMT(if (!(c)) { JS_ReportWarning(cx, "Script value conversion check failed: %s (got type %s)", #c, JS_GetTypeName(cx, JS_TypeOfValue(cx, v))); })
32 
33 template<> bool ScriptInterface::FromJSVal<bool>(JSContext* cx, jsval v, bool& out)
34 {
35  JSBool ret;
36  WARN_IF_NOT(JSVAL_IS_BOOLEAN(v), v);
37  if (!JS_ValueToBoolean(cx, v, &ret))
38  return false;
39  out = (ret ? true : false);
40  return true;
41 }
42 
43 template<> bool ScriptInterface::FromJSVal<float>(JSContext* cx, jsval v, float& out)
44 {
45  jsdouble ret;
46  WARN_IF_NOT(JSVAL_IS_NUMBER(v), v);
47  if (!JS_ValueToNumber(cx, v, &ret))
48  return false;
49  out = ret;
50  return true;
51 }
52 
53 template<> bool ScriptInterface::FromJSVal<double>(JSContext* cx, jsval v, double& out)
54 {
55  jsdouble ret;
56  WARN_IF_NOT(JSVAL_IS_NUMBER(v), v);
57  if (!JS_ValueToNumber(cx, v, &ret))
58  return false;
59  out = ret;
60  return true;
61 }
62 
63 template<> bool ScriptInterface::FromJSVal<i32>(JSContext* cx, jsval v, i32& out)
64 {
65  int32 ret;
66  WARN_IF_NOT(JSVAL_IS_NUMBER(v), v);
67  if (!JS_ValueToECMAInt32(cx, v, &ret))
68  return false;
69  out = ret;
70  return true;
71 }
72 
73 template<> bool ScriptInterface::FromJSVal<u32>(JSContext* cx, jsval v, u32& out)
74 {
75  uint32 ret;
76  WARN_IF_NOT(JSVAL_IS_NUMBER(v), v);
77  if (!JS_ValueToECMAUint32(cx, v, &ret))
78  return false;
79  out = ret;
80  return true;
81 }
82 
83 template<> bool ScriptInterface::FromJSVal<u16>(JSContext* cx, jsval v, u16& out)
84 {
85  uint16 ret;
86  WARN_IF_NOT(JSVAL_IS_NUMBER(v), v);
87  if (!JS_ValueToUint16(cx, v, &ret))
88  return false;
89  out = ret;
90  return true;
91 }
92 
93 template<> bool ScriptInterface::FromJSVal<u8>(JSContext* cx, jsval v, u8& out)
94 {
95  uint16 ret;
96  WARN_IF_NOT(JSVAL_IS_NUMBER(v), v);
97  if (!JS_ValueToUint16(cx, v, &ret))
98  return false;
99  out = (u8)ret;
100  return true;
101 }
102 
103 // NOTE: we can't define a jsval specialisation, because that conflicts with integer types
104 template<> bool ScriptInterface::FromJSVal<CScriptVal>(JSContext* UNUSED(cx), jsval v, CScriptVal& out)
105 {
106  out = v;
107  return true;
108 }
109 
110 template<> bool ScriptInterface::FromJSVal<CScriptValRooted>(JSContext* cx, jsval v, CScriptValRooted& out)
111 {
112  out = CScriptValRooted(cx, v);
113  return true;
114 }
115 
116 template<> bool ScriptInterface::FromJSVal<std::wstring>(JSContext* cx, jsval v, std::wstring& out)
117 {
118  WARN_IF_NOT(JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v), v); // allow implicit number conversions
119  JSString* ret = JS_ValueToString(cx, v);
120  if (!ret)
121  FAIL("Argument must be convertible to a string");
122  size_t length;
123  const jschar* ch = JS_GetStringCharsAndLength(cx, ret, &length);
124  if (!ch)
125  FAIL("JS_GetStringsCharsAndLength failed"); // out of memory
126  out = std::wstring(ch, ch + length);
127  return true;
128 }
129 
130 template<> bool ScriptInterface::FromJSVal<Path>(JSContext* cx, jsval v, Path& out)
131 {
132  std::wstring string;
133  if (!FromJSVal(cx, v, string))
134  return false;
135  out = string;
136  return true;
137 }
138 
139 template<> bool ScriptInterface::FromJSVal<std::string>(JSContext* cx, jsval v, std::string& out)
140 {
141  WARN_IF_NOT(JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v), v); // allow implicit number conversions
142  JSString* ret = JS_ValueToString(cx, v);
143  if (!ret)
144  FAIL("Argument must be convertible to a string");
145  char* ch = JS_EncodeString(cx, ret); // chops off high byte of each jschar
146  if (!ch)
147  FAIL("JS_EncodeString failed"); // out of memory
148  out = std::string(ch, ch + JS_GetStringLength(ret));
149  JS_free(cx, ch);
150  return true;
151 }
152 
153 template<> bool ScriptInterface::FromJSVal<Entity>(JSContext* cx, jsval v, Entity& out)
154 {
155  JSObject* obj;
156  if (!JS_ValueToObject(cx, v, &obj) || obj == NULL)
157  FAIL("Argument must be an object");
158 
159  jsval templateName, id, player, position, rotation;
160 
161  // TODO: Report type errors
162  if(!JS_GetProperty(cx, obj, "player", &player) || !FromJSVal(cx, player, out.playerID))
163  FAIL("Failed to read Entity.player property");
164  if (!JS_GetProperty(cx, obj, "templateName", &templateName) || !FromJSVal(cx, templateName, out.templateName))
165  FAIL("Failed to read Entity.templateName property");
166  if (!JS_GetProperty(cx, obj, "id", &id) || !FromJSVal(cx, id, out.entityID))
167  FAIL("Failed to read Entity.id property");
168  if (!JS_GetProperty(cx, obj, "position", &position) || !FromJSVal(cx, position, out.position))
169  FAIL("Failed to read Entity.position property");
170  if (!JS_GetProperty(cx, obj, "rotation", &rotation) || !FromJSVal(cx, rotation, out.rotation))
171  FAIL("Failed to read Entity.rotation property");
172 
173  return true;
174 }
175 
176 ////////////////////////////////////////////////////////////////
177 // Primitive types:
178 
179 template<> jsval ScriptInterface::ToJSVal<bool>(JSContext* UNUSED(cx), const bool& val)
180 {
181  return val ? JSVAL_TRUE : JSVAL_FALSE;
182 }
183 
184 template<> jsval ScriptInterface::ToJSVal<float>(JSContext* cx, const float& val)
185 {
186  jsval rval = JSVAL_VOID;
187  JS_NewNumberValue(cx, val, &rval); // ignore return value
188  return rval;
189 }
190 
191 template<> jsval ScriptInterface::ToJSVal<double>(JSContext* cx, const double& val)
192 {
193  jsval rval = JSVAL_VOID;
194  JS_NewNumberValue(cx, val, &rval); // ignore return value
195  return rval;
196 }
197 
198 template<> jsval ScriptInterface::ToJSVal<i32>(JSContext* UNUSED(cx), const i32& val)
199 {
200  cassert(JSVAL_INT_BITS == 32);
201  return INT_TO_JSVAL(val);
202 }
203 
204 template<> jsval ScriptInterface::ToJSVal<u16>(JSContext* UNUSED(cx), const u16& val)
205 {
206  return INT_TO_JSVAL(val);
207 }
208 
209 template<> jsval ScriptInterface::ToJSVal<u8>(JSContext* UNUSED(cx), const u8& val)
210 {
211  return INT_TO_JSVAL(val);
212 }
213 
214 template<> jsval ScriptInterface::ToJSVal<u32>(JSContext* cx, const u32& val)
215 {
216  if (val <= JSVAL_INT_MAX)
217  return INT_TO_JSVAL(val);
218  jsval rval = JSVAL_VOID;
219  JS_NewNumberValue(cx, val, &rval); // ignore return value
220  return rval;
221 }
222 
223 // NOTE: we can't define a jsval specialisation, because that conflicts with integer types
224 template<> jsval ScriptInterface::ToJSVal<CScriptVal>(JSContext* UNUSED(cx), const CScriptVal& val)
225 {
226  return val.get();
227 }
228 
229 template<> jsval ScriptInterface::ToJSVal<CScriptValRooted>(JSContext* UNUSED(cx), const CScriptValRooted& val)
230 {
231  return val.get();
232 }
233 
234 template<> jsval ScriptInterface::ToJSVal<std::wstring>(JSContext* cx, const std::wstring& val)
235 {
236  utf16string utf16(val.begin(), val.end());
237  JSString* str = JS_NewUCStringCopyN(cx, reinterpret_cast<const jschar*> (utf16.c_str()), utf16.length());
238  if (str)
239  return STRING_TO_JSVAL(str);
240  return JSVAL_VOID;
241 }
242 
243 template<> jsval ScriptInterface::ToJSVal<Path>(JSContext* cx, const Path& val)
244 {
245  return ToJSVal(cx, val.string());
246 }
247 
248 template<> jsval ScriptInterface::ToJSVal<std::string>(JSContext* cx, const std::string& val)
249 {
250  JSString* str = JS_NewStringCopyN(cx, val.c_str(), val.length());
251  if (str)
252  return STRING_TO_JSVAL(str);
253  return JSVAL_VOID;
254 }
255 
256 template<> jsval ScriptInterface::ToJSVal<const wchar_t*>(JSContext* cx, const wchar_t* const& val)
257 {
258  return ToJSVal(cx, std::wstring(val));
259 }
260 
261 template<> jsval ScriptInterface::ToJSVal<const char*>(JSContext* cx, const char* const& val)
262 {
263  JSString* str = JS_NewStringCopyZ(cx, val);
264  if (str)
265  return STRING_TO_JSVAL(str);
266  return JSVAL_VOID;
267 }
268 
269 template<> jsval ScriptInterface::ToJSVal<CStrW>(JSContext* cx, const CStrW& val)
270 {
271  return ToJSVal(cx, static_cast<const std::wstring&>(val));
272 }
273 
274 template<> jsval ScriptInterface::ToJSVal<CStr8>(JSContext* cx, const CStr8& val)
275 {
276  return ToJSVal(cx, static_cast<const std::string&>(val));
277 }
278 
279 ////////////////////////////////////////////////////////////////
280 // Compound types:
281 
282 template<typename T> static jsval ToJSVal_vector(JSContext* cx, const std::vector<T>& val)
283 {
284  JSObject* obj = JS_NewArrayObject(cx, val.size(), NULL);
285  if (!obj)
286  return JSVAL_VOID;
287  for (size_t i = 0; i < val.size(); ++i)
288  {
289  jsval el = ScriptInterface::ToJSVal<T>(cx, val[i]);
290  JS_SetElement(cx, obj, (jsint)i, &el);
291  }
292  return OBJECT_TO_JSVAL(obj);
293 }
294 
295 template<typename T> static bool FromJSVal_vector(JSContext* cx, jsval v, std::vector<T>& out)
296 {
297  JSObject* obj;
298  if (!JS_ValueToObject(cx, v, &obj) || obj == NULL || !(JS_IsArrayObject(cx, obj) || js_IsTypedArray(obj)))
299  FAIL("Argument must be an array");
300  jsuint length;
301  if (!JS_GetArrayLength(cx, obj, &length))
302  FAIL("Failed to get array length");
303  out.reserve(length);
304  for (jsuint i = 0; i < length; ++i)
305  {
306  jsval el;
307  if (!JS_GetElement(cx, obj, i, &el))
308  FAIL("Failed to read array element");
309  T el2;
310  if (!ScriptInterface::FromJSVal<T>(cx, el, el2))
311  return false;
312  out.push_back(el2);
313  }
314  return true;
315 }
316 
317 // Instantiate various vector types:
318 
319 #define VECTOR(T) \
320  template<> jsval ScriptInterface::ToJSVal<std::vector<T> >(JSContext* cx, const std::vector<T>& val) \
321  { \
322  return ToJSVal_vector(cx, val); \
323  } \
324  template<> bool ScriptInterface::FromJSVal<std::vector<T> >(JSContext* cx, jsval v, std::vector<T>& out) \
325  { \
326  return FromJSVal_vector(cx, v, out); \
327  }
328 
329 VECTOR(int)
330 VECTOR(u32)
331 VECTOR(u16)
332 VECTOR(std::string)
333 VECTOR(std::wstring)
335 
336 
337 class IComponent;
338 template<> jsval ScriptInterface::ToJSVal<std::vector<IComponent*> >(JSContext* cx, const std::vector<IComponent*>& val)
339 {
340  return ToJSVal_vector(cx, val);
341 }
342 
343 template<> bool ScriptInterface::FromJSVal<std::vector<Entity> >(JSContext* cx, jsval v, std::vector<Entity>& out)
344 {
345  return FromJSVal_vector(cx, v, out);
346 }
#define u8
Definition: types.h:39
jsval ToJSVal< float >(const float &Native)
jsval ToJSVal< double >(const double &Native)
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
#define i32
Definition: types.h:36
static void out(const wchar_t *fmt,...)
Definition: wdbg_sym.cpp:419
#define WARN_IF_NOT(c, v)
A trivial wrapper around a jsval.
Definition: ScriptVal.h:29
static bool FromJSVal_vector(JSContext *cx, jsval v, std::vector< T > &out)
#define FAIL(msg)
#define VECTOR(T)
static jsval ToJSVal_vector(JSContext *cx, const std::vector< T > &val)
Definition: path.h:75
jsval ToJSVal< CStrW >(const CStrW &Native)
#define T(string_literal)
Definition: secure_crt.cpp:70
jsval ToJSVal< CStr8 >(const CStr8 &Native)
#define u16
Definition: types.h:40
std::basic_string< utf16_t, utf16_traits > utf16string
Definition: utf16string.h:109
#define u32
Definition: types.h:41
jsval ToJSVal(T &Native)
Definition: JSConversions.h:87
Definition: Entity.h:24
Abstraction around a SpiderMonkey JSContext.
#define cassert(expr)
Compile-time assertion.
jsval ToJSVal< bool >(const bool &Native)