Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ScriptInterface.h
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 #ifndef INCLUDED_SCRIPTINTERFACE
19 #define INCLUDED_SCRIPTINTERFACE
20 
21 #include <memory>
22 #include <vector>
23 #include <string>
24 
25 #include "ScriptTypes.h"
26 #include "ScriptVal.h"
27 
28 #include "js/jsapi.h"
29 
31 #include "ps/Profile.h"
32 #include "ps/utf16string.h"
33 
34 #include <boost/random/linear_congruential.hpp>
35 
37 
38 // Set the maximum number of function arguments that can be handled
39 // (This should be as small as possible (for compiler efficiency),
40 // but as large as necessary for all wrapped functions)
41 #define SCRIPT_INTERFACE_MAX_ARGS 8
42 
43 // TODO: what's a good default?
44 #define DEFAULT_RUNTIME_SIZE 16 * 1024 * 1024
45 
47 
48 class ScriptRuntime;
49 
50 class CDebuggingServer;
51 
52 /**
53  * Abstraction around a SpiderMonkey JSContext.
54  *
55  * Thread-safety:
56  * - May be used in non-main threads.
57  * - Each ScriptInterface must be created, used, and destroyed, all in a single thread
58  * (it must never be shared between threads).
59  */
61 {
62 public:
63 
64  /**
65  * Returns a runtime, which can used to initialise any number of
66  * ScriptInterfaces contexts. Values created in one context may be used
67  * in any other context from the same runtime (but not any other runtime).
68  * Each runtime should only ever be used on a single thread.
69  * @param runtimeSize Maximum size in bytes of the new runtime
70  */
71  static shared_ptr<ScriptRuntime> CreateRuntime(int runtimeSize = DEFAULT_RUNTIME_SIZE);
72 
73  /**
74  * Constructor.
75  * @param nativeScopeName Name of global object that functions (via RegisterFunction) will
76  * be placed into, as a scoping mechanism; typically "Engine"
77  * @param debugName Name of this interface for CScriptStats purposes.
78  * @param runtime ScriptRuntime to use when initializing this interface.
79  */
80  ScriptInterface(const char* nativeScopeName, const char* debugName, const shared_ptr<ScriptRuntime>& runtime);
81 
83 
84  /**
85  * Shut down the JS system to clean up memory. Must only be called when there
86  * are no ScriptInterfaces alive.
87  */
88  static void ShutDown();
89 
90  void SetCallbackData(void* cbdata);
91  static void* GetCallbackData(JSContext* cx);
92 
93  JSContext* GetContext() const;
94  JSRuntime* GetRuntime() const;
95 
96  /**
97  * Load global scripts that most script contexts need,
98  * located in the /globalscripts directory. VFS must be initialized.
99  */
100  bool LoadGlobalScripts();
101 
102  /**
103  * Replace the default JS random number geenrator with a seeded, network-sync'd one.
104  */
105  bool ReplaceNondeterministicRNG(boost::rand48& rng);
106 
107  /**
108  * Call a constructor function, equivalent to JS "new ctor(arg)".
109  * @return The new object; or JSVAL_VOID on failure, and logs an error message
110  */
111  jsval CallConstructor(jsval ctor, jsval arg);
112 
113  /**
114  * Create an object as with CallConstructor except don't actually execute the
115  * constructor function.
116  * @return The new object; or JSVAL_VOID on failure, and logs an error message
117  */
118  jsval NewObjectFromConstructor(jsval ctor);
119 
120  /**
121  * Call the named property on the given object, with void return type and 0 arguments
122  */
123  bool CallFunctionVoid(jsval val, const char* name);
124 
125  /**
126  * Call the named property on the given object, with void return type and 1 argument
127  */
128  template<typename T0>
129  bool CallFunctionVoid(jsval val, const char* name, const T0& a0);
130 
131  /**
132  * Call the named property on the given object, with void return type and 2 arguments
133  */
134  template<typename T0, typename T1>
135  bool CallFunctionVoid(jsval val, const char* name, const T0& a0, const T1& a1);
136 
137  /**
138  * Call the named property on the given object, with void return type and 3 arguments
139  */
140  template<typename T0, typename T1, typename T2>
141  bool CallFunctionVoid(jsval val, const char* name, const T0& a0, const T1& a1, const T2& a2);
142 
143  /**
144  * Call the named property on the given object, with return type R and 0 arguments
145  */
146  template<typename R>
147  bool CallFunction(jsval val, const char* name, R& ret);
148 
149  /**
150  * Call the named property on the given object, with return type R and 1 argument
151  */
152  template<typename T0, typename R>
153  bool CallFunction(jsval val, const char* name, const T0& a0, R& ret);
154 
155  /**
156  * Call the named property on the given object, with return type R and 2 arguments
157  */
158  template<typename T0, typename T1, typename R>
159  bool CallFunction(jsval val, const char* name, const T0& a0, const T1& a1, R& ret);
160 
161  /**
162  * Call the named property on the given object, with return type R and 3 arguments
163  */
164  template<typename T0, typename T1, typename T2, typename R>
165  bool CallFunction(jsval val, const char* name, const T0& a0, const T1& a1, const T2& a2, R& ret);
166 
167  /**
168  * Call the named property on the given object, with return type R and 4 arguments
169  */
170  template<typename T0, typename T1, typename T2, typename T3, typename R>
171  bool CallFunction(jsval val, const char* name, const T0& a0, const T1& a1, const T2& a2, const T3& a3, R& ret);
172 
173  jsval GetGlobalObject();
174 
175  JSClass* GetGlobalClass();
176 
177  /**
178  * Set the named property on the global object.
179  * If @p replace is true, an existing property will be overwritten; otherwise attempts
180  * to set an already-defined value will fail.
181  */
182  template<typename T>
183  bool SetGlobal(const char* name, const T& value, bool replace = false);
184 
185  /**
186  * Set the named property on the given object.
187  * Optionally makes it {ReadOnly, DontDelete, DontEnum}.
188  */
189  template<typename T>
190  bool SetProperty(jsval obj, const char* name, const T& value, bool constant = false, bool enumerate = true);
191 
192  /**
193  * Set the integer-named property on the given object.
194  * Optionally makes it {ReadOnly, DontDelete, DontEnum}.
195  */
196  template<typename T>
197  bool SetPropertyInt(jsval obj, int name, const T& value, bool constant = false, bool enumerate = true);
198 
199  /**
200  * Get the named property on the given object.
201  */
202  template<typename T>
203  bool GetProperty(jsval obj, const char* name, T& out);
204 
205  /**
206  * Get the integer-named property on the given object.
207  */
208  template<typename T>
209  bool GetPropertyInt(jsval obj, int name, T& out);
210 
211  /**
212  * Check the named property has been defined on the given object.
213  */
214  bool HasProperty(jsval obj, const char* name);
215 
216  bool EnumeratePropertyNamesWithPrefix(jsval obj, const char* prefix, std::vector<std::string>& out);
217 
218  bool SetPrototype(jsval obj, jsval proto);
219 
220  bool FreezeObject(jsval obj, bool deep);
221 
222  bool Eval(const char* code);
223 
224  template<typename T, typename CHAR> bool Eval(const CHAR* code, T& out);
225 
226  std::wstring ToString(jsval obj, bool pretty = false);
227 
228  /**
229  * Parse a UTF-8-encoded JSON string. Returns the undefined value on error.
230  */
231  CScriptValRooted ParseJSON(const std::string& string_utf8);
232 
233  /**
234  * Read a JSON file. Returns the undefined value on error.
235  */
237 
238  /**
239  * Stringify to a JSON string, UTF-8 encoded. Returns an empty string on error.
240  */
241  std::string StringifyJSON(jsval obj, bool indent = true);
242 
243  /**
244  * Report the given error message through the JS error reporting mechanism,
245  * and throw a JS exception. (Callers can check IsPendingException, and must
246  * return JS_FALSE in that case to propagate the exception.)
247  */
248  void ReportError(const char* msg);
249 
250  /**
251  * Load and execute the given script in a new function scope.
252  * @param filename Name for debugging purposes (not used to load the file)
253  * @param code JS code to execute
254  * @return true on successful compilation and execution; false otherwise
255  */
256  bool LoadScript(const VfsPath& filename, const std::string& code);
257 
258  /**
259  * Load and execute the given script in the global scope.
260  * @param filename Name for debugging purposes (not used to load the file)
261  * @param code JS code to execute
262  * @return true on successful compilation and execution; false otherwise
263  */
264  bool LoadGlobalScript(const VfsPath& filename, const std::string& code);
265 
266  /**
267  * Load and execute the given script in the global scope.
268  * @return true on successful compilation and execution; false otherwise
269  */
270  bool LoadGlobalScriptFile(const VfsPath& path);
271 
272  /**
273  * Construct a new value (usable in this ScriptInterface's context) by cloning
274  * a value from a different context.
275  * Complex values (functions, XML, etc) won't be cloned correctly, but basic
276  * types and cyclic references should be fine.
277  */
278  jsval CloneValueFromOtherContext(ScriptInterface& otherContext, jsval val);
279 
280  /**
281  * Convert a jsval to a C++ type. (This might trigger GC.)
282  */
283  template<typename T> static bool FromJSVal(JSContext* cx, jsval val, T& ret);
284 
285  /**
286  * Convert a C++ type to a jsval. (This might trigger GC. The return
287  * value must be rooted if you don't want it to be collected.)
288  */
289  template<typename T> static jsval ToJSVal(JSContext* cx, T const& val);
290 
292 
293  /**
294  * Dump some memory heap debugging information to stderr.
295  */
296  void DumpHeap();
297 
298  /**
299  * MaybeGC tries to determine whether garbage collection in cx's runtime would free up enough memory to be worth the amount of time it would take
300  */
301  void MaybeGC();
302 
303  /**
304  * Structured clones are a way to serialize 'simple' JS values into a buffer
305  * that can safely be passed between contexts and runtimes and threads.
306  * A StructuredClone can be stored and read multiple times if desired.
307  * We wrap them in shared_ptr so memory management is automatic and
308  * thread-safe.
309  */
311  {
313  public:
314  StructuredClone();
316  JSContext* m_Context;
317  uint64* m_Data;
318  size_t m_Size;
319  };
320 
321  shared_ptr<StructuredClone> WriteStructuredClone(jsval v);
322  jsval ReadStructuredClone(const shared_ptr<StructuredClone>& ptr);
323 
324 private:
325  bool CallFunction_(jsval val, const char* name, size_t argc, jsval* argv, jsval& ret);
326  bool Eval_(const char* code, jsval& ret);
327  bool Eval_(const wchar_t* code, jsval& ret);
328  bool SetGlobal_(const char* name, jsval value, bool replace);
329  bool SetProperty_(jsval obj, const char* name, jsval value, bool readonly, bool enumerate);
330  bool SetPropertyInt_(jsval obj, int name, jsval value, bool readonly, bool enumerate);
331  bool GetProperty_(jsval obj, const char* name, jsval& value);
332  bool GetPropertyInt_(jsval obj, int name, jsval& value);
333  static bool IsExceptionPending(JSContext* cx);
334  static JSClass* GetClass(JSContext* cx, JSObject* obj);
335  static void* GetPrivate(JSContext* cx, JSObject* obj);
336 
337  void Register(const char* name, JSNative fptr, size_t nargs);
338  std::auto_ptr<ScriptInterface_impl> m;
339 
340 // The nasty macro/template bits are split into a separate file so you don't have to look at them
341 public:
342  #include "NativeWrapperDecls.h"
343  // This declares:
344  //
345  // template <R, T0..., TR (*fptr) (void* cbdata, T0...)>
346  // void RegisterFunction(const char* functionName);
347  //
348  // template <R, T0..., TR (*fptr) (void* cbdata, T0...)>
349  // static JSNative call;
350  //
351  // template <R, T0..., JSClass*, TC, TR (TC:*fptr) (T0...)>
352  // static JSNative callMethod;
353  //
354  // template <dummy, T0...>
355  // static size_t nargs();
356 };
357 
358 // Implement those declared functions
359 #include "NativeWrapperDefns.h"
360 
361 template<typename R>
362 bool ScriptInterface::CallFunction(jsval val, const char* name, R& ret)
363 {
364  jsval jsRet;
365  bool ok = CallFunction_(val, name, 0, NULL, jsRet);
366  if (!ok)
367  return false;
368  return FromJSVal(GetContext(), jsRet, ret);
369 }
370 
371 template<typename T0>
372 bool ScriptInterface::CallFunctionVoid(jsval val, const char* name, const T0& a0)
373 {
374  jsval jsRet;
375  jsval argv[1];
376  argv[0] = ToJSVal(GetContext(), a0);
377  return CallFunction_(val, name, 1, argv, jsRet);
378 }
379 
380 template<typename T0, typename T1>
381 bool ScriptInterface::CallFunctionVoid(jsval val, const char* name, const T0& a0, const T1& a1)
382 {
383  jsval jsRet;
384  jsval argv[2];
385  argv[0] = ToJSVal(GetContext(), a0);
386  argv[1] = ToJSVal(GetContext(), a1);
387  return CallFunction_(val, name, 2, argv, jsRet);
388 }
389 
390 template<typename T0, typename T1, typename T2>
391 bool ScriptInterface::CallFunctionVoid(jsval val, const char* name, const T0& a0, const T1& a1, const T2& a2)
392 {
393  jsval jsRet;
394  jsval argv[3];
395  argv[0] = ToJSVal(GetContext(), a0);
396  argv[1] = ToJSVal(GetContext(), a1);
397  argv[2] = ToJSVal(GetContext(), a2);
398  return CallFunction_(val, name, 3, argv, jsRet);
399 }
400 
401 template<typename T0, typename R>
402 bool ScriptInterface::CallFunction(jsval val, const char* name, const T0& a0, R& ret)
403 {
404  jsval jsRet;
405  jsval argv[1];
406  argv[0] = ToJSVal(GetContext(), a0);
407  bool ok = CallFunction_(val, name, 1, argv, jsRet);
408  if (!ok)
409  return false;
410  return FromJSVal(GetContext(), jsRet, ret);
411 }
412 
413 template<typename T0, typename T1, typename R>
414 bool ScriptInterface::CallFunction(jsval val, const char* name, const T0& a0, const T1& a1, R& ret)
415 {
416  jsval jsRet;
417  jsval argv[2];
418  argv[0] = ToJSVal(GetContext(), a0);
419  argv[1] = ToJSVal(GetContext(), a1);
420  bool ok = CallFunction_(val, name, 2, argv, jsRet);
421  if (!ok)
422  return false;
423  return FromJSVal(GetContext(), jsRet, ret);
424 }
425 
426 template<typename T0, typename T1, typename T2, typename R>
427 bool ScriptInterface::CallFunction(jsval val, const char* name, const T0& a0, const T1& a1, const T2& a2, R& ret)
428 {
429  jsval jsRet;
430  jsval argv[3];
431  argv[0] = ToJSVal(GetContext(), a0);
432  argv[1] = ToJSVal(GetContext(), a1);
433  argv[2] = ToJSVal(GetContext(), a2);
434  bool ok = CallFunction_(val, name, 3, argv, jsRet);
435  if (!ok)
436  return false;
437  return FromJSVal(GetContext(), jsRet, ret);
438 }
439 
440 template<typename T0, typename T1, typename T2, typename T3, typename R>
441 bool ScriptInterface::CallFunction(jsval val, const char* name, const T0& a0, const T1& a1, const T2& a2, const T3& a3, R& ret)
442 {
443  jsval jsRet;
444  jsval argv[4];
445  argv[0] = ToJSVal(GetContext(), a0);
446  argv[1] = ToJSVal(GetContext(), a1);
447  argv[2] = ToJSVal(GetContext(), a2);
448  argv[3] = ToJSVal(GetContext(), a3);
449  bool ok = CallFunction_(val, name, 4, argv, jsRet);
450  if (!ok)
451  return false;
452  return FromJSVal(GetContext(), jsRet, ret);
453 }
454 
455 template<typename T>
456 bool ScriptInterface::SetGlobal(const char* name, const T& value, bool replace)
457 {
458  return SetGlobal_(name, ToJSVal(GetContext(), value), replace);
459 }
460 
461 template<typename T>
462 bool ScriptInterface::SetProperty(jsval obj, const char* name, const T& value, bool readonly, bool enumerate)
463 {
464  return SetProperty_(obj, name, ToJSVal(GetContext(), value), readonly, enumerate);
465 }
466 
467 template<typename T>
468 bool ScriptInterface::SetPropertyInt(jsval obj, int name, const T& value, bool readonly, bool enumerate)
469 {
470  return SetPropertyInt_(obj, name, ToJSVal(GetContext(), value), readonly, enumerate);
471 }
472 
473 template<typename T>
474 bool ScriptInterface::GetProperty(jsval obj, const char* name, T& out)
475 {
476  jsval val;
477  if (! GetProperty_(obj, name, val))
478  return false;
479  return FromJSVal(GetContext(), val, out);
480 }
481 
482 template<typename T>
483 bool ScriptInterface::GetPropertyInt(jsval obj, int name, T& out)
484 {
485  jsval val;
486  if (! GetPropertyInt_(obj, name, val))
487  return false;
488  return FromJSVal(GetContext(), val, out);
489 }
490 
491 template<typename T, typename CHAR>
492 bool ScriptInterface::Eval(const CHAR* code, T& ret)
493 {
494  jsval rval;
495  if (! Eval_(code, rval))
496  return false;
497  return FromJSVal(GetContext(), rval, ret);
498 }
499 
500 #endif // INCLUDED_SCRIPTINTERFACE
bool SetPrototype(jsval obj, jsval proto)
bool ReplaceNondeterministicRNG(boost::rand48 &rng)
Replace the default JS random number geenrator with a seeded, network-sync&#39;d one. ...
bool CallFunction_(jsval val, const char *name, size_t argc, jsval *argv, jsval &ret)
bool LoadGlobalScript(const VfsPath &filename, const std::string &code)
Load and execute the given script in the global scope.
bool SetGlobal_(const char *name, jsval value, bool replace)
bool HasProperty(jsval obj, const char *name)
Check the named property has been defined on the given object.
void SetCallbackData(void *cbdata)
#define T0(z, i)
static CStr prefix
Definition: DllLoader.cpp:47
static void out(const wchar_t *fmt,...)
Definition: wdbg_sym.cpp:419
bool CallFunctionVoid(jsval val, const char *name)
Call the named property on the given object, with void return type and 0 arguments.
void Register(const char *name, JSNative fptr, size_t nargs)
JSRuntime * GetRuntime() const
static void ShutDown()
Shut down the JS system to clean up memory.
bool GetProperty_(jsval obj, const char *name, jsval &value)
bool FreezeObject(jsval obj, bool deep)
bool LoadScript(const VfsPath &filename, const std::string &code)
Load and execute the given script in a new function scope.
bool LoadGlobalScripts()
Load global scripts that most script contexts need, located in the /globalscripts directory...
std::string StringifyJSON(jsval obj, bool indent=true)
Stringify to a JSON string, UTF-8 encoded.
CScriptValRooted ParseJSON(const std::string &string_utf8)
Parse a UTF-8-encoded JSON string.
bool SetPropertyInt(jsval obj, int name, const T &value, bool constant=false, bool enumerate=true)
Set the integer-named property on the given object.
bool SetPropertyInt_(jsval obj, int name, jsval value, bool readonly, bool enumerate)
char CHAR
Definition: wgl.h:60
Structured clones are a way to serialize &#39;simple&#39; JS values into a buffer that can safely be passed b...
AutoGCRooter * ReplaceAutoGCRooter(AutoGCRooter *rooter)
void DumpHeap()
Dump some memory heap debugging information to stderr.
static bool IsExceptionPending(JSContext *cx)
jsval NewObjectFromConstructor(jsval ctor)
Create an object as with CallConstructor except don&#39;t actually execute the constructor function...
ScriptInterface(const char *nativeScopeName, const char *debugName, const shared_ptr< ScriptRuntime > &runtime)
Constructor.
static bool FromJSVal(JSContext *cx, jsval val, T &ret)
Convert a jsval to a C++ type.
static void * GetCallbackData(JSContext *cx)
Definition: path.h:75
boost::mt19937 rng
Random number generator (Boost Mersenne Twister)
Definition: Noise.cpp:34
CScriptValRooted ReadJSONFile(const VfsPath &path)
Read a JSON file.
bool LoadGlobalScriptFile(const VfsPath &path)
Load and execute the given script in the global scope.
static JSClass * GetClass(JSContext *cx, JSObject *obj)
bool Eval(const char *code)
static jsval ToJSVal(JSContext *cx, T const &val)
Convert a C++ type to a jsval.
#define DEFAULT_RUNTIME_SIZE
#define T(string_literal)
Definition: secure_crt.cpp:70
jsval CloneValueFromOtherContext(ScriptInterface &otherContext, jsval val)
Construct a new value (usable in this ScriptInterface&#39;s context) by cloning a value from a different ...
bool GetProperty(jsval obj, const char *name, T &out)
Get the named property on the given object.
bool CallFunction(jsval val, const char *name, R &ret)
Call the named property on the given object, with return type R and 0 arguments.
bool SetProperty_(jsval obj, const char *name, jsval value, bool readonly, bool enumerate)
jsval CallConstructor(jsval ctor, jsval arg)
Call a constructor function, equivalent to JS &quot;new ctor(arg)&quot;.
bool SetGlobal(const char *name, const T &value, bool replace=false)
Set the named property on the global object.
bool GetPropertyInt(jsval obj, int name, T &out)
Get the integer-named property on the given object.
JSClass * GetGlobalClass()
static void * GetPrivate(JSContext *cx, JSObject *obj)
Abstraction around a SpiderMonkey JSContext.
std::wstring ToString(jsval obj, bool pretty=false)
bool GetPropertyInt_(jsval obj, int name, jsval &value)
bool Eval_(const char *code, jsval &ret)
bool SetProperty(jsval obj, const char *name, const T &value, bool constant=false, bool enumerate=true)
Set the named property on the given object.
Abstraction around a SpiderMonkey JSRuntime.
jsval ReadStructuredClone(const shared_ptr< StructuredClone > &ptr)
bool EnumeratePropertyNamesWithPrefix(jsval obj, const char *prefix, std::vector< std::string > &out)
static shared_ptr< ScriptRuntime > CreateRuntime(int runtimeSize=DEFAULT_RUNTIME_SIZE)
Returns a runtime, which can used to initialise any number of ScriptInterfaces contexts.
JSContext * GetContext() const
void MaybeGC()
MaybeGC tries to determine whether garbage collection in cx&#39;s runtime would free up enough memory to ...
Helper for rooting large groups of script values.
Definition: AutoRooters.h:31
void ReportError(const char *msg)
Report the given error message through the JS error reporting mechanism, and throw a JS exception...
std::auto_ptr< ScriptInterface_impl > m
shared_ptr< StructuredClone > WriteStructuredClone(jsval v)