Pyrogenesis  trunk
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 
31 namespace Script
32 {
33 /**
34  * Get the named property on the given object.
35  */
36 template<typename PropType>
37 inline 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 
53 template<typename T, typename PropType>
54 inline 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 }
61 inline 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 
70 template<typename T>
71 inline bool GetPropertyInt(const ScriptRequest& rq, JS::HandleValue obj, int name, T& out)
72 {
73  return GetProperty(rq, obj, name, out);
74 }
75 inline 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  */
82 inline 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  */
97 template<typename PropType>
98 inline 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 
120 template<typename T, typename PropType>
121 inline 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 
128 template<typename T>
129 inline 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 
134 template<typename T>
135 inline 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  */
144 template<typename T>
145 inline 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 
153 inline 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  */
174 inline 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  */
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 
222 inline 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  */
233 template<typename T, typename... Args>
234 inline 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  */
244 inline 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
JSContext * cx
Definition: ScriptRequest.h:92
#define LOGERROR(...)
Definition: CLogger.h:36
static void out(const wchar_t *fmt,...)
Definition: wdbg_sym.cpp:421
bool FreezeObject(const ScriptRequest &rq, JS::HandleValue objVal, bool deep)
Definition: Object.h:153
Wraps SM APIs for manipulating JS objects.
Definition: JSON.h:34
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 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
Config::Value_type Value
Definition: json_spirit_value.h:182
void ToJSVal(const ScriptRequest &rq, JS::MutableHandleValue ret, T const &val)
Convert a C++ type to a JS::Value.
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
JS::Value CreateObject(const ScriptRequest &rq)
Create a plain object (i.e.
Definition: Object.h:214
bool GetPropertyInt(const ScriptRequest &rq, JS::HandleValue obj, int name, T &out)
Definition: Object.h:71
#define T(string_literal)
Definition: secure_crt.cpp:77
bool SetPropertyInt(const ScriptRequest &rq, JS::HandleValue obj, int name, const T &value, bool constant=false, bool enumerable=true)
Definition: Object.h:129
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
unsigned int uint
Definition: types.h:42
bool GetObjectClassName(const ScriptRequest &rq, JS::HandleObject obj, T &name)
Definition: Object.h:135
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
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.