1 /* Copyright (C) 2023 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
18 #include "precompiled.h"
20 #include "ScriptConversions.h"
21 #include "ScriptExceptions.h"
22 #include "ScriptExtraHeaders.h"
24 #include "graphics/Entity.h"
25 #include "lib/file/vfs/vfs_path.h"
26 #include "maths/Vector2D.h"
27 #include "ps/CLogger.h"
32 // Catch the raised exception right away to ensure the stack trace gets printed.
33 #define FAIL(msg) STMT(ScriptException::Raise(rq, msg); ScriptException::CatchPending(rq); return false)
35 // Implicit type conversions often hide bugs, so fail.
36 #define FAIL_IF_NOT(c, v) STMT(if (!(c)) { \
37 ScriptException::Raise(rq, "Script value conversion check failed: %s (got type %s)", #c, JS::InformalValueTypeName(v)); \
38 ScriptException::CatchPending(rq); \
42 template<> bool Script::FromJSVal
<bool>(const ScriptRequest
& rq
, JS::HandleValue v
, bool& out
)
44 FAIL_IF_NOT(v
.isBoolean(), v
);
45 out
= JS::ToBoolean(v
);
49 template<> bool Script::FromJSVal
<float>(const ScriptRequest
& rq
, JS::HandleValue v
, float& out
)
52 FAIL_IF_NOT(v
.isNumber(), v
);
53 if (!JS::ToNumber(rq
.cx
, v
, &tmp
))
59 template<> bool Script::FromJSVal
<double>(const ScriptRequest
& rq
, JS::HandleValue v
, double& out
)
61 FAIL_IF_NOT(v
.isNumber(), v
);
62 if (!JS::ToNumber(rq
.cx
, v
, &out
))
67 template<> bool Script::FromJSVal
<i32
>(const ScriptRequest
& rq
, JS::HandleValue v
, i32
& out
)
69 FAIL_IF_NOT(v
.isNumber(), v
);
70 if (!JS::ToInt32(rq
.cx
, v
, &out
))
75 template<> bool Script::FromJSVal
<u32
>(const ScriptRequest
& rq
, JS::HandleValue v
, u32
& out
)
77 FAIL_IF_NOT(v
.isNumber(), v
);
78 if (!JS::ToUint32(rq
.cx
, v
, &out
))
83 template<> bool Script::FromJSVal
<u16
>(const ScriptRequest
& rq
, JS::HandleValue v
, u16
& out
)
85 FAIL_IF_NOT(v
.isNumber(), v
);
86 if (!JS::ToUint16(rq
.cx
, v
, &out
))
91 template<> bool Script::FromJSVal
<u8
>(const ScriptRequest
& rq
, JS::HandleValue v
, u8
& out
)
94 FAIL_IF_NOT(v
.isNumber(), v
);
95 if (!JS::ToUint16(rq
.cx
, v
, &tmp
))
101 template<> bool Script::FromJSVal
<std::wstring
>(const ScriptRequest
& rq
, JS::HandleValue v
, std::wstring
& out
)
103 FAIL_IF_NOT(v
.isString() || v
.isNumber() || v
.isBoolean(), v
); // allow implicit boolean/number conversions
104 JS::RootedString
str(rq
.cx
, JS::ToString(rq
.cx
, v
));
106 FAIL("Argument must be convertible to a string");
108 if (JS::StringHasLatin1Chars(str
))
111 JS::AutoCheckCannotGC nogc
;
112 const JS::Latin1Char
* ch
= JS_GetLatin1StringCharsAndLength(rq
.cx
, nogc
, str
, &length
);
114 FAIL("JS_GetLatin1StringCharsAndLength failed");
116 out
.assign(ch
, ch
+ length
);
121 JS::AutoCheckCannotGC nogc
;
122 const char16_t
* ch
= JS_GetTwoByteStringCharsAndLength(rq
.cx
, nogc
, str
, &length
);
124 FAIL("JS_GetTwoByteStringsCharsAndLength failed"); // out of memory
126 out
.assign(ch
, ch
+ length
);
131 template<> bool Script::FromJSVal
<Path
>(const ScriptRequest
& rq
, JS::HandleValue v
, Path
& out
)
134 if (!FromJSVal(rq
, v
, string
))
140 template<> bool Script::FromJSVal
<std::string
>(const ScriptRequest
& rq
, JS::HandleValue v
, std::string
& out
)
142 std::wstring wideout
;
143 if (!FromJSVal(rq
, v
, wideout
))
145 out
= CStrW(wideout
).ToUTF8();
149 template<> bool Script::FromJSVal
<CStr8
>(const ScriptRequest
& rq
, JS::HandleValue v
, CStr8
& out
)
151 return Script::FromJSVal(rq
, v
, static_cast<std::string
&>(out
));
154 template<> bool Script::FromJSVal
<CStrW
>(const ScriptRequest
& rq
, JS::HandleValue v
, CStrW
& out
)
156 return Script::FromJSVal(rq
, v
, static_cast<std::wstring
&>(out
));
159 template<> bool Script::FromJSVal
<Entity
>(const ScriptRequest
& rq
, JS::HandleValue v
, Entity
& out
)
162 FAIL("Argument must be an object");
164 JS::RootedObject
obj(rq
.cx
, &v
.toObject());
165 JS::RootedValue
templateName(rq
.cx
);
166 JS::RootedValue
id(rq
.cx
);
167 JS::RootedValue
player(rq
.cx
);
168 JS::RootedValue
position(rq
.cx
);
169 JS::RootedValue
rotation(rq
.cx
);
171 // TODO: Report type errors
172 if (!JS_GetProperty(rq
.cx
, obj
, "player", &player
) || !FromJSVal(rq
, player
, out
.playerID
))
173 FAIL("Failed to read Entity.player property");
174 if (!JS_GetProperty(rq
.cx
, obj
, "templateName", &templateName
) || !FromJSVal(rq
, templateName
, out
.templateName
))
175 FAIL("Failed to read Entity.templateName property");
176 if (!JS_GetProperty(rq
.cx
, obj
, "id", &id
) || !FromJSVal(rq
, id
, out
.entityID
))
177 FAIL("Failed to read Entity.id property");
178 if (!JS_GetProperty(rq
.cx
, obj
, "position", &position
) || !FromJSVal(rq
, position
, out
.position
))
179 FAIL("Failed to read Entity.position property");
180 if (!JS_GetProperty(rq
.cx
, obj
, "rotation", &rotation
) || !FromJSVal(rq
, rotation
, out
.rotation
))
181 FAIL("Failed to read Entity.rotation property");
186 ////////////////////////////////////////////////////////////////
189 template<> void Script::ToJSVal
<bool>(const ScriptRequest
& UNUSED(rq
), JS::MutableHandleValue ret
, const bool& val
)
194 template<> void Script::ToJSVal
<float>(const ScriptRequest
& UNUSED(rq
), JS::MutableHandleValue ret
, const float& val
)
196 ret
.set(JS::NumberValue(val
));
199 template<> void Script::ToJSVal
<double>(const ScriptRequest
& UNUSED(rq
), JS::MutableHandleValue ret
, const double& val
)
201 ret
.set(JS::NumberValue(val
));
204 template<> void Script::ToJSVal
<i32
>(const ScriptRequest
& UNUSED(rq
), JS::MutableHandleValue ret
, const i32
& val
)
206 ret
.set(JS::NumberValue(val
));
209 template<> void Script::ToJSVal
<u16
>(const ScriptRequest
& UNUSED(rq
), JS::MutableHandleValue ret
, const u16
& val
)
211 ret
.set(JS::NumberValue(val
));
214 template<> void Script::ToJSVal
<u8
>(const ScriptRequest
& UNUSED(rq
), JS::MutableHandleValue ret
, const u8
& val
)
216 ret
.set(JS::NumberValue(val
));
219 template<> void Script::ToJSVal
<u32
>(const ScriptRequest
& UNUSED(rq
), JS::MutableHandleValue ret
, const u32
& val
)
221 ret
.set(JS::NumberValue(val
));
224 template<> void Script::ToJSVal
<std::wstring
>(const ScriptRequest
& rq
, JS::MutableHandleValue ret
, const std::wstring
& val
)
226 std::u16string
utf16(val
.begin(), val
.end());
227 JS::RootedString
str(rq
.cx
, JS_NewUCStringCopyN(rq
.cx
, utf16
.c_str(), utf16
.length()));
234 template<> void Script::ToJSVal
<Path
>(const ScriptRequest
& rq
, JS::MutableHandleValue ret
, const Path
& val
)
236 ToJSVal(rq
, ret
, val
.string());
239 template<> void Script::ToJSVal
<std::string
>(const ScriptRequest
& rq
, JS::MutableHandleValue ret
, const std::string
& val
)
241 ToJSVal(rq
, ret
, static_cast<const std::wstring
>(CStr(val
).FromUTF8()));
244 template<> void Script::ToJSVal
<const wchar_t*>(const ScriptRequest
& rq
, JS::MutableHandleValue ret
, const wchar_t* const& val
)
246 ToJSVal(rq
, ret
, std::wstring(val
));
249 template<> void Script::ToJSVal
<const char*>(const ScriptRequest
& rq
, JS::MutableHandleValue ret
, const char* const& val
)
251 JS::RootedString
str(rq
.cx
, JS_NewStringCopyZ(rq
.cx
, val
));
258 #define TOJSVAL_CHAR(N) \
259 template<> void Script::ToJSVal<wchar_t[N]>(const ScriptRequest& rq, JS::MutableHandleValue ret, const wchar_t (&val)[N]) \
261 ToJSVal(rq, ret, static_cast<const wchar_t*>(val)); \
263 template<> void Script::ToJSVal<char[N]>(const ScriptRequest& rq, JS::MutableHandleValue ret, const char (&val)[N]) \
265 ToJSVal(rq, ret, static_cast<const char*>(val)); \
292 template<> void Script::ToJSVal
<CStrW
>(const ScriptRequest
& rq
, JS::MutableHandleValue ret
, const CStrW
& val
)
294 ToJSVal(rq
, ret
, static_cast<const std::wstring
&>(val
));
297 template<> void Script::ToJSVal
<CStr8
>(const ScriptRequest
& rq
, JS::MutableHandleValue ret
, const CStr8
& val
)
299 ToJSVal(rq
, ret
, static_cast<const std::string
&>(val
));
302 ////////////////////////////////////////////////////////////////
304 // Instantiate various vector types:
309 JSVAL_VECTOR(std::string
)
310 JSVAL_VECTOR(std::wstring
)
311 JSVAL_VECTOR(std::vector
<std::wstring
>)
314 JSVAL_VECTOR(std::vector
<CStr8
>)
315 JSVAL_VECTOR(std::vector
<std::string
>)
319 template<> void Script::ToJSVal
<std::vector
<IComponent
*>>(const ScriptRequest
& rq
, JS::MutableHandleValue ret
, const std::vector
<IComponent
*>& val
)
321 ToJSVal_vector(rq
, ret
, val
);
324 template<> bool Script::FromJSVal
<std::vector
<Entity
>>(const ScriptRequest
& rq
, JS::HandleValue v
, std::vector
<Entity
>& out
)
326 return FromJSVal_vector(rq
, v
, out
);