Pyrogenesis HEAD
Pyrogenesis, a RTS Engine
Object.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_SCRIPTINTERFACE_OBJECT
19#define INCLUDED_SCRIPTINTERFACE_OBJECT
20
21#include "ScriptConversions.h"
22#include "ScriptRequest.h"
23#include "ScriptTypes.h"
24
25#include "ps/CLogger.h"
26
27/**
28 * Wraps SM APIs for manipulating JS objects.
29 */
30
31namespace Script
32{
33/**
34 * Get the named property on the given object.
35 */
36template<typename PropType>
37inline bool GetProperty(const ScriptRequest& rq, JS::HandleValue obj, PropType name, JS::MutableHandleValue out)
38{
39 if (!obj.isObject())
40 return false;
41 JS::RootedObject object(rq.cx, &obj.toObject());
42 if constexpr (std::is_same_v<int, PropType>)
43 {
44 JS::RootedId id(rq.cx, INT_TO_JSID(name));
45 return JS_GetPropertyById(rq.cx, object, id, out);
46 }
47 else if constexpr (std::is_same_v<const char*, PropType>)
48 return JS_GetProperty(rq.cx, object, name, out);
49 else
50 return JS_GetUCProperty(rq.cx, object, name, wcslen(name), out);
51}
52
53template<typename T, typename PropType>
54inline bool GetProperty(const ScriptRequest& rq, JS::HandleValue obj, PropType name, T& out)
55{
56 JS::RootedValue val(rq.cx);
57 if (!GetProperty<PropType>(rq, obj, name, &val))
58 return false;
59 return FromJSVal(rq, val, out);
60}
61inline bool GetProperty(const ScriptRequest& rq, JS::HandleValue obj, const char* name, JS::MutableHandleObject out)
62{
63 JS::RootedValue val(rq.cx, JS::ObjectValue(*out.get()));
64 if (!GetProperty(rq, obj, name, &val))
65 return false;
66 out.set(val.toObjectOrNull());
67 return true;
68}
69
70template<typename T>
71inline bool GetPropertyInt(const ScriptRequest& rq, JS::HandleValue obj, int name, T& out)
72{
73 return GetProperty(rq, obj, name, out);
74}
75inline bool GetPropertyInt(const ScriptRequest& rq, JS::HandleValue obj, int name, JS::MutableHandleValue out)
76{
77 return GetProperty(rq, obj, name, out);
78}
79/**
80 * Check the named property has been defined on the given object.
81 */
82inline bool HasProperty(const ScriptRequest& rq, JS::HandleValue obj, const char* name)
83{
84 if (!obj.isObject())
85 return false;
86 JS::RootedObject object(rq.cx, &obj.toObject());
87
88 bool found;
89 if (!JS_HasProperty(rq.cx, object, name, &found))
90 return false;
91 return found;
92}
93
94/**
95 * Set the named property on the given object.
96 */
97template<typename PropType>
98inline bool SetProperty(const ScriptRequest& rq, JS::HandleValue obj, PropType name, JS::HandleValue value, bool constant = false, bool enumerable = true)
99{
100 uint attrs = 0;
101 if (constant)
102 attrs |= JSPROP_READONLY | JSPROP_PERMANENT;
103 if (enumerable)
104 attrs |= JSPROP_ENUMERATE;
105
106 if (!obj.isObject())
107 return false;
108 JS::RootedObject object(rq.cx, &obj.toObject());
109 if constexpr (std::is_same_v<int, PropType>)
110 {
111 JS::RootedId id(rq.cx, INT_TO_JSID(name));
112 return JS_DefinePropertyById(rq.cx, object, id, value, attrs);
113 }
114 else if constexpr (std::is_same_v<const char*, PropType>)
115 return JS_DefineProperty(rq.cx, object, name, value, attrs);
116 else
117 return JS_DefineUCProperty(rq.cx, object, name, value, attrs);
118}
119
120template<typename T, typename PropType>
121inline bool SetProperty(const ScriptRequest& rq, JS::HandleValue obj, PropType name, const T& value, bool constant = false, bool enumerable = true)
122{
123 JS::RootedValue val(rq.cx);
124 Script::ToJSVal(rq, &val, value);
125 return SetProperty<PropType>(rq, obj, name, val, constant, enumerable);
126}
127
128template<typename T>
129inline bool SetPropertyInt(const ScriptRequest& rq, JS::HandleValue obj, int name, const T& value, bool constant = false, bool enumerable = true)
130{
131 return SetProperty<T, int>(rq, obj, name, value, constant, enumerable);
132}
133
134template<typename T>
135inline bool GetObjectClassName(const ScriptRequest& rq, JS::HandleObject obj, T& name)
136{
137 JS::RootedValue constructor(rq.cx, JS::ObjectOrNullValue(JS_GetConstructor(rq.cx, obj)));
138 return constructor.isObject() && Script::HasProperty(rq, constructor, "name") && Script::GetProperty(rq, constructor, "name", name);
139}
140
141/**
142 * Get the name of the object's class. Note that inheritance may lead to unexpected results.
143 */
144template<typename T>
145inline bool GetObjectClassName(const ScriptRequest& rq, JS::HandleValue val, T& name)
146{
147 JS::RootedObject obj(rq.cx, val.toObjectOrNull());
148 if (!obj)
149 return false;
150 return GetObjectClassName(rq, obj, name);
151}
152
153inline bool FreezeObject(const ScriptRequest& rq, JS::HandleValue objVal, bool deep)
154{
155 if (!objVal.isObject())
156 return false;
157
158 JS::RootedObject obj(rq.cx, &objVal.toObject());
159
160 if (deep)
161 return JS_DeepFreezeObject(rq.cx, obj);
162 else
163 return JS_FreezeObject(rq.cx, obj);
164}
165
166/**
167 * Returns all properties of the object, both own properties and inherited.
168 * This is essentially equivalent to calling Object.getOwnPropertyNames()
169 * and recursing up the prototype chain.
170 * NB: this does not return properties with symbol or numeric keys, as that would
171 * require a variant in the vector, and it's not useful for now.
172 * @param enumerableOnly - only return enumerable properties.
173 */
174inline bool EnumeratePropertyNames(const ScriptRequest& rq, JS::HandleValue objVal, bool enumerableOnly, std::vector<std::string>& out)
175{
176 if (!objVal.isObjectOrNull())
177 {
178 LOGERROR("EnumeratePropertyNames expected object type!");
179 return false;
180 }
181
182 JS::RootedObject obj(rq.cx, &objVal.toObject());
183 JS::RootedIdVector props(rq.cx);
184 // This recurses up the prototype chain on its own.
185 if (!js::GetPropertyKeys(rq.cx, obj, enumerableOnly? 0 : JSITER_HIDDEN, &props))
186 return false;
187
188 out.reserve(out.size() + props.length());
189 for (size_t i = 0; i < props.length(); ++i)
190 {
191 JS::RootedId id(rq.cx, props[i]);
192 JS::RootedValue val(rq.cx);
193 if (!JS_IdToValue(rq.cx, id, &val))
194 return false;
195
196 // Ignore integer properties for now.
197 // TODO: is this actually a thing in ECMAScript 6?
198 if (!val.isString())
199 continue;
200
201 std::string propName;
202 if (!FromJSVal(rq, val, propName))
203 return false;
204
205 out.emplace_back(std::move(propName));
206 }
207
208 return true;
209}
210
211/**
212 * Create a plain object (i.e. {}). If it fails, returns undefined.
213 */
214inline JS::Value CreateObject(const ScriptRequest& rq)
215{
216 JS::RootedObject obj(rq.cx, JS_NewPlainObject(rq.cx));
217 if (!obj)
218 return JS::UndefinedValue();
219 return JS::ObjectValue(*obj.get());
220}
221
222inline bool CreateObject(const ScriptRequest& rq, JS::MutableHandleValue objectValue)
223{
224 objectValue.set(CreateObject(rq));
225 return !objectValue.isNullOrUndefined();
226}
227
228/**
229 * Sets the given value to a new plain JS::Object, converts the arguments to JS::Values and sets them as properties.
230 * This is static so that callers like ToJSVal can use it with the JSContext directly instead of having to obtain the instance using GetScriptInterfaceAndCBData.
231 * Can throw an exception.
232 */
233template<typename T, typename... Args>
234inline bool CreateObject(const ScriptRequest& rq, JS::MutableHandleValue objectValue, const char* propertyName, const T& propertyValue, Args const&... args)
235{
236 JS::RootedValue val(rq.cx);
237 ToJSVal(rq, &val, propertyValue);
238 return CreateObject(rq, objectValue, args...) && SetProperty(rq, objectValue, propertyName, val, false, true);
239}
240
241/**
242 * Sets the given value to a new JS object or Null Value in case of out-of-memory.
243 */
244inline bool CreateArray(const ScriptRequest& rq, JS::MutableHandleValue objectValue, size_t length = 0)
245{
246 objectValue.setObjectOrNull(JS::NewArrayObject(rq.cx, length));
247 return !objectValue.isNullOrUndefined();
248}
249
250} // namespace Script
251
252#endif // INCLUDED_SCRIPTINTERFACE_Object
#define LOGERROR(...)
Definition: CLogger.h:37
Spidermonkey maintains some 'local' state via the JSContext* object.
Definition: ScriptRequest.h:60
JSContext * cx
Definition: ScriptRequest.h:92
Wraps SM APIs for manipulating JS objects.
Definition: JSON.h:35
bool FromJSVal(const ScriptRequest &rq, const JS::HandleValue val, T &ret)
Convert a JS::Value to a C++ type.
bool EnumeratePropertyNames(const ScriptRequest &rq, JS::HandleValue objVal, bool enumerableOnly, std::vector< std::string > &out)
Returns all properties of the object, both own properties and inherited.
Definition: Object.h:174
bool SetProperty(const ScriptRequest &rq, JS::HandleValue obj, PropType name, JS::HandleValue value, bool constant=false, bool enumerable=true)
Set the named property on the given object.
Definition: Object.h:98
bool FreezeObject(const ScriptRequest &rq, JS::HandleValue objVal, bool deep)
Definition: Object.h:153
void ToJSVal(const ScriptRequest &rq, JS::MutableHandleValue ret, T const &val)
Convert a C++ type to a JS::Value.
bool GetPropertyInt(const ScriptRequest &rq, JS::HandleValue obj, int name, T &out)
Definition: Object.h:71
bool CreateArray(const ScriptRequest &rq, JS::MutableHandleValue objectValue, size_t length=0)
Sets the given value to a new JS object or Null Value in case of out-of-memory.
Definition: Object.h:244
bool GetObjectClassName(const ScriptRequest &rq, JS::HandleObject obj, T &name)
Definition: Object.h:135
bool SetPropertyInt(const ScriptRequest &rq, JS::HandleValue obj, int name, const T &value, bool constant=false, bool enumerable=true)
Definition: Object.h:129
bool HasProperty(const ScriptRequest &rq, JS::HandleValue obj, const char *name)
Check the named property has been defined on the given object.
Definition: Object.h:82
JS::Value CreateObject(const ScriptRequest &rq)
Create a plain object (i.e.
Definition: Object.h:214
bool GetProperty(const ScriptRequest &rq, JS::HandleValue obj, PropType name, JS::MutableHandleValue out)
Get the named property on the given object.
Definition: Object.h:37
#define T(string_literal)
Definition: secure_crt.cpp:77
unsigned int uint
Definition: types.h:42
static void out(const wchar_t *fmt,...)
Definition: wdbg_sym.cpp:407