1 /* Copyright (C) 2017 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 #include "ScriptRuntime.h"
22 #include "ScriptStats.h"
23
24 #include "lib/debug.h"
25 #include "lib/utf8.h"
26 #include "ps/CLogger.h"
27 #include "ps/Filesystem.h"
28 #include "ps/Profile.h"
29 #include "ps/utf16string.h"
30
31 #include <cassert>
32 #include <map>
33
34 #define BOOST_MULTI_INDEX_DISABLE_SERIALIZATION
35 #include <boost/preprocessor/punctuation/comma_if.hpp>
36 #include <boost/preprocessor/repetition/repeat.hpp>
37 #include <boost/random/linear_congruential.hpp>
38 #include <boost/flyweight.hpp>
39 #include <boost/flyweight/key_value.hpp>
40 #include <boost/flyweight/no_locking.hpp>
41 #include <boost/flyweight/no_tracking.hpp>
42
43 #include "valgrind.h"
44
45 #include "scriptinterface/ScriptExtraHeaders.h"
46
47 /**
48 * @file
49 * Abstractions of various SpiderMonkey features.
50 * Engine code should be using functions of these interfaces rather than
51 * directly accessing the underlying JS api.
52 */
53
54
55 struct ScriptInterface_impl
56 {
57 ScriptInterface_impl(const char* nativeScopeName, const shared_ptr<ScriptRuntime>& runtime);
58 ~ScriptInterface_impl();
59 void Register(const char* name, JSNative fptr, uint nargs) const;
60
61 // Take care to keep this declaration before heap rooted members. Destructors of heap rooted
62 // members have to be called before the runtime destructor.
63 shared_ptr<ScriptRuntime> m_runtime;
64
65 JSContext* m_cx;
66 JS::PersistentRootedObject m_glob; // global scope object
67 JSCompartment* m_comp;
68 boost::rand48* m_rng;
69 JS::PersistentRootedObject m_nativeScope; // native function scope object
70
71 typedef std::map<ScriptInterface::CACHED_VAL, JS::PersistentRootedValue> ScriptValCache;
72 ScriptValCache m_ScriptValCache;
73 };
74
75 namespace
76 {
77
78 JSClass global_class = {
79 "global", JSCLASS_GLOBAL_FLAGS,
80 nullptr, nullptr,
81 nullptr, nullptr,
82 nullptr, nullptr, nullptr,
83 nullptr, nullptr, nullptr, nullptr,
84 JS_GlobalObjectTraceHook
85 };
86
ErrorReporter(JSContext * cx,const char * message,JSErrorReport * report)87 void ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
88 {
89
90 std::stringstream msg;
91 bool isWarning = JSREPORT_IS_WARNING(report->flags);
92 msg << (isWarning ? "JavaScript warning: " : "JavaScript error: ");
93 if (report->filename)
94 {
95 msg << report->filename;
96 msg << " line " << report->lineno << "\n";
97 }
98
99 msg << message;
100
101 // If there is an exception, then print its stack trace
102 JS::RootedValue excn(cx);
103 if (JS_GetPendingException(cx, &excn) && excn.isObject())
104 {
105 JS::RootedObject excnObj(cx, &excn.toObject());
106 // TODO: this violates the docs ("The error reporter callback must not reenter the JSAPI.")
107
108 // Hide the exception from EvaluateScript
109 JSExceptionState* excnState = JS_SaveExceptionState(cx);
110 JS_ClearPendingException(cx);
111
112 JS::RootedValue rval(cx);
113 const char dumpStack[] = "this.stack.trimRight().replace(/^/mg, ' ')"; // indent each line
114 JS::CompileOptions opts(cx);
115 if (JS::Evaluate(cx, excnObj, opts.setFileAndLine("(eval)", 1), dumpStack, ARRAY_SIZE(dumpStack)-1, &rval))
116 {
117 std::string stackTrace;
118 if (ScriptInterface::FromJSVal(cx, rval, stackTrace))
119 msg << "\n" << stackTrace;
120
121 JS_RestoreExceptionState(cx, excnState);
122 }
123 else
124 {
125 // Error got replaced by new exception from EvaluateScript
126 JS_DropExceptionState(cx, excnState);
127 }
128 }
129
130 if (isWarning)
131 LOGWARNING("%s", msg.str().c_str());
132 else
133 LOGERROR("%s", msg.str().c_str());
134
135 // When running under Valgrind, print more information in the error message
136 // VALGRIND_PRINTF_BACKTRACE("->");
137 }
138
139 // Functions in the global namespace:
140
print(JSContext * cx,uint argc,JS::Value * vp)141 bool print(JSContext* cx, uint argc, JS::Value* vp)
142 {
143 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
144 for (uint i = 0; i < args.length(); ++i)
145 {
146 std::wstring str;
147 if (!ScriptInterface::FromJSVal(cx, args[i], str))
148 return false;
149 debug_printf("%s", utf8_from_wstring(str).c_str());
150 }
151 fflush(stdout);
152 args.rval().setUndefined();
153 return true;
154 }
155
logmsg(JSContext * cx,uint argc,JS::Value * vp)156 bool logmsg(JSContext* cx, uint argc, JS::Value* vp)
157 {
158 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
159 if (args.length() < 1)
160 {
161 args.rval().setUndefined();
162 return true;
163 }
164
165 std::wstring str;
166 if (!ScriptInterface::FromJSVal(cx, args[0], str))
167 return false;
168 LOGMESSAGE("%s", utf8_from_wstring(str));
169 args.rval().setUndefined();
170 return true;
171 }
172
warn(JSContext * cx,uint argc,JS::Value * vp)173 bool warn(JSContext* cx, uint argc, JS::Value* vp)
174 {
175 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
176 if (args.length() < 1)
177 {
178 args.rval().setUndefined();
179 return true;
180 }
181
182 std::wstring str;
183 if (!ScriptInterface::FromJSVal(cx, args[0], str))
184 return false;
185 LOGWARNING("%s", utf8_from_wstring(str));
186 args.rval().setUndefined();
187 return true;
188 }
189
error(JSContext * cx,uint argc,JS::Value * vp)190 bool error(JSContext* cx, uint argc, JS::Value* vp)
191 {
192 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
193 if (args.length() < 1)
194 {
195 args.rval().setUndefined();
196 return true;
197 }
198
199 std::wstring str;
200 if (!ScriptInterface::FromJSVal(cx, args[0], str))
201 return false;
202 LOGERROR("%s", utf8_from_wstring(str));
203 args.rval().setUndefined();
204 return true;
205 }
206
deepcopy(JSContext * cx,uint argc,JS::Value * vp)207 bool deepcopy(JSContext* cx, uint argc, JS::Value* vp)
208 {
209 JSAutoRequest rq(cx);
210
211 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
212 if (args.length() < 1)
213 {
214 args.rval().setUndefined();
215 return true;
216 }
217
218 JS::RootedValue ret(cx);
219 if (!JS_StructuredClone(cx, args[0], &ret, NULL, NULL))
220 return false;
221
222 args.rval().set(ret);
223 return true;
224 }
225
deepfreeze(JSContext * cx,uint argc,JS::Value * vp)226 bool deepfreeze(JSContext* cx, uint argc, JS::Value* vp)
227 {
228 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
229
230 if (args.length() != 1 || !args.get(0).isObject())
231 {
232 JS_ReportError(cx, "deepfreeze requires exactly one object as an argument.");
233 return false;
234 }
235
236 ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface->FreezeObject(args.get(0), true);
237 args.rval().set(args.get(0));
238 return true;
239 }
240
ProfileStart(JSContext * cx,uint argc,JS::Value * vp)241 bool ProfileStart(JSContext* cx, uint argc, JS::Value* vp)
242 {
243 const char* name = "(ProfileStart)";
244
245 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
246 if (args.length() >= 1)
247 {
248 std::string str;
249 if (!ScriptInterface::FromJSVal(cx, args[0], str))
250 return false;
251
252 typedef boost::flyweight<
253 std::string,
254 boost::flyweights::no_tracking,
255 boost::flyweights::no_locking
256 > StringFlyweight;
257
258 name = StringFlyweight(str).get().c_str();
259 }
260
261 if (CProfileManager::IsInitialised() && ThreadUtil::IsMainThread())
262 g_Profiler.StartScript(name);
263
264 g_Profiler2.RecordRegionEnter(name);
265
266 args.rval().setUndefined();
267 return true;
268 }
269
ProfileStop(JSContext * UNUSED (cx),uint UNUSED (argc),JS::Value * vp)270 bool ProfileStop(JSContext* UNUSED(cx), uint UNUSED(argc), JS::Value* vp)
271 {
272 JS::CallReceiver rec = JS::CallReceiverFromVp(vp);
273 if (CProfileManager::IsInitialised() && ThreadUtil::IsMainThread())
274 g_Profiler.Stop();
275
276 g_Profiler2.RecordRegionLeave();
277
278 rec.rval().setUndefined();
279 return true;
280 }
281
ProfileAttribute(JSContext * cx,uint argc,JS::Value * vp)282 bool ProfileAttribute(JSContext* cx, uint argc, JS::Value* vp)
283 {
284 const char* name = "(ProfileAttribute)";
285
286 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
287 if (args.length() >= 1)
288 {
289 std::string str;
290 if (!ScriptInterface::FromJSVal(cx, args[0], str))
291 return false;
292
293 typedef boost::flyweight<
294 std::string,
295 boost::flyweights::no_tracking,
296 boost::flyweights::no_locking
297 > StringFlyweight;
298
299 name = StringFlyweight(str).get().c_str();
300 }
301
302 g_Profiler2.RecordAttribute("%s", name);
303
304 args.rval().setUndefined();
305 return true;
306 }
307
308 // Math override functions:
309
310 // boost::uniform_real is apparently buggy in Boost pre-1.47 - for integer generators
311 // it returns [min,max], not [min,max). The bug was fixed in 1.47.
312 // We need consistent behaviour, so manually implement the correct version:
generate_uniform_real(boost::rand48 & rng,double min,double max)313 static double generate_uniform_real(boost::rand48& rng, double min, double max)
314 {
315 while (true)
316 {
317 double n = (double)(rng() - rng.min());
318 double d = (double)(rng.max() - rng.min()) + 1.0;
319 ENSURE(d > 0 && n >= 0 && n <= d);
320 double r = n / d * (max - min) + min;
321 if (r < max)
322 return r;
323 }
324 }
325
Math_random(JSContext * cx,uint UNUSED (argc),JS::Value * vp)326 bool Math_random(JSContext* cx, uint UNUSED(argc), JS::Value* vp)
327 {
328 JS::CallReceiver rec = JS::CallReceiverFromVp(vp);
329 double r;
330 if (!ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface->MathRandom(r))
331 return false;
332
333 rec.rval().setNumber(r);
334 return true;
335 }
336
337 } // anonymous namespace
338
MathRandom(double & nbr)339 bool ScriptInterface::MathRandom(double& nbr)
340 {
341 if (m->m_rng == NULL)
342 return false;
343 nbr = generate_uniform_real(*(m->m_rng), 0.0, 1.0);
344 return true;
345 }
346
ScriptInterface_impl(const char * nativeScopeName,const shared_ptr<ScriptRuntime> & runtime)347 ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, const shared_ptr<ScriptRuntime>& runtime) :
348 m_runtime(runtime), m_glob(runtime->m_rt), m_nativeScope(runtime->m_rt)
349 {
350 bool ok;
351
352 m_cx = JS_NewContext(m_runtime->m_rt, STACK_CHUNK_SIZE);
353 ENSURE(m_cx);
354
355 JS_SetOffthreadIonCompilationEnabled(m_runtime->m_rt, true);
356
357 // For GC debugging:
358 // JS_SetGCZeal(m_cx, 2, JS_DEFAULT_ZEAL_FREQ);
359
360 JS_SetContextPrivate(m_cx, NULL);
361
362 JS_SetErrorReporter(m_runtime->m_rt, ErrorReporter);
363
364 JS_SetGlobalJitCompilerOption(m_runtime->m_rt, JSJITCOMPILER_ION_ENABLE, 1);
365 JS_SetGlobalJitCompilerOption(m_runtime->m_rt, JSJITCOMPILER_BASELINE_ENABLE, 1);
366
367 JS::RuntimeOptionsRef(m_cx).setExtraWarnings(1)
368 .setWerror(0)
369 .setVarObjFix(1)
370 .setStrictMode(1);
371
372 JS::CompartmentOptions opt;
373 opt.setVersion(JSVERSION_LATEST);
374 // Keep JIT code during non-shrinking GCs. This brings a quite big performance improvement.
375 opt.setPreserveJitCode(true);
376
377 JSAutoRequest rq(m_cx);
378 JS::RootedObject globalRootedVal(m_cx, JS_NewGlobalObject(m_cx, &global_class, NULL, JS::OnNewGlobalHookOption::FireOnNewGlobalHook, opt));
379 m_comp = JS_EnterCompartment(m_cx, globalRootedVal);
380 ok = JS_InitStandardClasses(m_cx, globalRootedVal);
381 ENSURE(ok);
382 m_glob = globalRootedVal.get();
383
384 JS_DefineProperty(m_cx, m_glob, "global", globalRootedVal, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
385
386 m_nativeScope = JS_DefineObject(m_cx, m_glob, nativeScopeName, nullptr, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
387
388 JS_DefineFunction(m_cx, globalRootedVal, "print", ::print, 0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
389 JS_DefineFunction(m_cx, globalRootedVal, "log", ::logmsg, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
390 JS_DefineFunction(m_cx, globalRootedVal, "warn", ::warn, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
391 JS_DefineFunction(m_cx, globalRootedVal, "error", ::error, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
392 JS_DefineFunction(m_cx, globalRootedVal, "clone", ::deepcopy, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
393 JS_DefineFunction(m_cx, globalRootedVal, "deepfreeze", ::deepfreeze, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
394
395 Register("ProfileStart", ::ProfileStart, 1);
396 Register("ProfileStop", ::ProfileStop, 0);
397 Register("ProfileAttribute", ::ProfileAttribute, 1);
398
399 runtime->RegisterContext(m_cx);
400 }
401
~ScriptInterface_impl()402 ScriptInterface_impl::~ScriptInterface_impl()
403 {
404 m_runtime->UnRegisterContext(m_cx);
405 {
406 JSAutoRequest rq(m_cx);
407 JS_LeaveCompartment(m_cx, m_comp);
408 }
409 JS_DestroyContext(m_cx);
410 }
411
Register(const char * name,JSNative fptr,uint nargs) const412 void ScriptInterface_impl::Register(const char* name, JSNative fptr, uint nargs) const
413 {
414 JSAutoRequest rq(m_cx);
415 JS::RootedObject nativeScope(m_cx, m_nativeScope);
416 JS::RootedFunction func(m_cx, JS_DefineFunction(m_cx, nativeScope, name, fptr, nargs, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT));
417 }
418
ScriptInterface(const char * nativeScopeName,const char * debugName,const shared_ptr<ScriptRuntime> & runtime)419 ScriptInterface::ScriptInterface(const char* nativeScopeName, const char* debugName, const shared_ptr<ScriptRuntime>& runtime) :
420 m(new ScriptInterface_impl(nativeScopeName, runtime))
421 {
422 // Profiler stats table isn't thread-safe, so only enable this on the main thread
423 if (ThreadUtil::IsMainThread())
424 {
425 if (g_ScriptStatsTable)
426 g_ScriptStatsTable->Add(this, debugName);
427 }
428
429 m_CxPrivate.pScriptInterface = this;
430 JS_SetContextPrivate(m->m_cx, (void*)&m_CxPrivate);
431 }
432
~ScriptInterface()433 ScriptInterface::~ScriptInterface()
434 {
435 if (ThreadUtil::IsMainThread())
436 {
437 if (g_ScriptStatsTable)
438 g_ScriptStatsTable->Remove(this);
439 }
440 }
441
SetCallbackData(void * pCBData)442 void ScriptInterface::SetCallbackData(void* pCBData)
443 {
444 m_CxPrivate.pCBData = pCBData;
445 }
446
GetScriptInterfaceAndCBData(JSContext * cx)447 ScriptInterface::CxPrivate* ScriptInterface::GetScriptInterfaceAndCBData(JSContext* cx)
448 {
449 CxPrivate* pCxPrivate = (CxPrivate*)JS_GetContextPrivate(cx);
450 return pCxPrivate;
451 }
452
GetCachedValue(CACHED_VAL valueIdentifier) const453 JS::Value ScriptInterface::GetCachedValue(CACHED_VAL valueIdentifier) const
454 {
455 std::map<ScriptInterface::CACHED_VAL, JS::PersistentRootedValue>::const_iterator it = m->m_ScriptValCache.find(valueIdentifier);
456 ENSURE(it != m->m_ScriptValCache.end());
457 return it->second.get();
458 }
459
460
LoadGlobalScripts()461 bool ScriptInterface::LoadGlobalScripts()
462 {
463 // Ignore this failure in tests
464 if (!g_VFS)
465 return false;
466
467 // Load and execute *.js in the global scripts directory
468 VfsPaths pathnames;
469 vfs::GetPathnames(g_VFS, L"globalscripts/", L"*.js", pathnames);
470 for (const VfsPath& path : pathnames)
471 if (!LoadGlobalScriptFile(path))
472 {
473 LOGERROR("LoadGlobalScripts: Failed to load script %s", path.string8());
474 return false;
475 }
476
477 JSAutoRequest rq(m->m_cx);
478 JS::RootedValue proto(m->m_cx);
479 JS::RootedObject global(m->m_cx, m->m_glob);
480 if (JS_GetProperty(m->m_cx, global, "Vector2Dprototype", &proto))
481 m->m_ScriptValCache[CACHE_VECTOR2DPROTO].init(GetJSRuntime(), proto);
482 if (JS_GetProperty(m->m_cx, global, "Vector3Dprototype", &proto))
483 m->m_ScriptValCache[CACHE_VECTOR3DPROTO].init(GetJSRuntime(), proto);
484 return true;
485 }
486
ReplaceNondeterministicRNG(boost::rand48 & rng)487 bool ScriptInterface::ReplaceNondeterministicRNG(boost::rand48& rng)
488 {
489 JSAutoRequest rq(m->m_cx);
490 JS::RootedValue math(m->m_cx);
491 JS::RootedObject global(m->m_cx, m->m_glob);
492 if (JS_GetProperty(m->m_cx, global, "Math", &math) && math.isObject())
493 {
494 JS::RootedObject mathObj(m->m_cx, &math.toObject());
495 JS::RootedFunction random(m->m_cx, JS_DefineFunction(m->m_cx, mathObj, "random", Math_random, 0,
496 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT));
497 if (random)
498 {
499 m->m_rng = &rng;
500 return true;
501 }
502 }
503
504 LOGERROR("ReplaceNondeterministicRNG: failed to replace Math.random");
505 return false;
506 }
507
Register(const char * name,JSNative fptr,size_t nargs) const508 void ScriptInterface::Register(const char* name, JSNative fptr, size_t nargs) const
509 {
510 m->Register(name, fptr, (uint)nargs);
511 }
512
GetContext() const513 JSContext* ScriptInterface::GetContext() const
514 {
515 return m->m_cx;
516 }
517
GetJSRuntime() const518 JSRuntime* ScriptInterface::GetJSRuntime() const
519 {
520 return m->m_runtime->m_rt;
521 }
522
GetRuntime() const523 shared_ptr<ScriptRuntime> ScriptInterface::GetRuntime() const
524 {
525 return m->m_runtime;
526 }
527
CallConstructor(JS::HandleValue ctor,JS::HandleValueArray argv,JS::MutableHandleValue out) const528 void ScriptInterface::CallConstructor(JS::HandleValue ctor, JS::HandleValueArray argv, JS::MutableHandleValue out) const
529 {
530 JSAutoRequest rq(m->m_cx);
531 if (!ctor.isObject())
532 {
533 LOGERROR("CallConstructor: ctor is not an object");
534 out.setNull();
535 return;
536 }
537
538 JS::RootedObject ctorObj(m->m_cx, &ctor.toObject());
539 out.setObjectOrNull(JS_New(m->m_cx, ctorObj, argv));
540 }
541
DefineCustomObjectType(JSClass * clasp,JSNative constructor,uint minArgs,JSPropertySpec * ps,JSFunctionSpec * fs,JSPropertySpec * static_ps,JSFunctionSpec * static_fs)542 void ScriptInterface::DefineCustomObjectType(JSClass *clasp, JSNative constructor, uint minArgs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
543 {
544 JSAutoRequest rq(m->m_cx);
545 std::string typeName = clasp->name;
546
547 if (m_CustomObjectTypes.find(typeName) != m_CustomObjectTypes.end())
548 {
549 // This type already exists
550 throw PSERROR_Scripting_DefineType_AlreadyExists();
551 }
552
553 JS::RootedObject global(m->m_cx, m->m_glob);
554 JS::RootedObject obj(m->m_cx, JS_InitClass(m->m_cx, global, JS::NullPtr(),
555 clasp,
556 constructor, minArgs, // Constructor, min args
557 ps, fs, // Properties, methods
558 static_ps, static_fs)); // Constructor properties, methods
559
560 if (obj == NULL)
561 throw PSERROR_Scripting_DefineType_CreationFailed();
562
563 CustomType& type = m_CustomObjectTypes[typeName];
564
565 type.m_Prototype.init(m->m_cx, obj);
566 type.m_Class = clasp;
567 type.m_Constructor = constructor;
568 }
569
CreateCustomObject(const std::string & typeName) const570 JSObject* ScriptInterface::CreateCustomObject(const std::string& typeName) const
571 {
572 std::map<std::string, CustomType>::const_iterator it = m_CustomObjectTypes.find(typeName);
573
574 if (it == m_CustomObjectTypes.end())
575 throw PSERROR_Scripting_TypeDoesNotExist();
576
577 JS::RootedObject prototype(m->m_cx, it->second.m_Prototype.get());
578 return JS_NewObjectWithGivenProto(m->m_cx, it->second.m_Class, prototype);
579 }
580
CallFunction_(JS::HandleValue val,const char * name,JS::HandleValueArray argv,JS::MutableHandleValue ret) const581 bool ScriptInterface::CallFunction_(JS::HandleValue val, const char* name, JS::HandleValueArray argv, JS::MutableHandleValue ret) const
582 {
583 JSAutoRequest rq(m->m_cx);
584 JS::RootedObject obj(m->m_cx);
585 if (!JS_ValueToObject(m->m_cx, val, &obj) || !obj)
586 return false;
587
588 // Check that the named function actually exists, to avoid ugly JS error reports
589 // when calling an undefined value
590 bool found;
591 if (!JS_HasProperty(m->m_cx, obj, name, &found) || !found)
592 return false;
593
594 bool ok = JS_CallFunctionName(m->m_cx, obj, name, argv, ret);
595
596 return ok;
597 }
598
GetGlobalObject() const599 JS::Value ScriptInterface::GetGlobalObject() const
600 {
601 JSAutoRequest rq(m->m_cx);
602 return JS::ObjectValue(*JS::CurrentGlobalOrNull(m->m_cx));
603 }
604
SetGlobal_(const char * name,JS::HandleValue value,bool replace)605 bool ScriptInterface::SetGlobal_(const char* name, JS::HandleValue value, bool replace)
606 {
607 JSAutoRequest rq(m->m_cx);
608 JS::RootedObject global(m->m_cx, m->m_glob);
609 if (!replace)
610 {
611 bool found;
612 if (!JS_HasProperty(m->m_cx, global, name, &found))
613 return false;
614 if (found)
615 {
616 JS_ReportError(m->m_cx, "SetGlobal \"%s\" called multiple times", name);
617 return false;
618 }
619 }
620
621 bool ok = JS_DefineProperty(m->m_cx, global, name, value, JSPROP_ENUMERATE | JSPROP_READONLY
622 | JSPROP_PERMANENT);
623 return ok;
624 }
625
SetProperty_(JS::HandleValue obj,const char * name,JS::HandleValue value,bool constant,bool enumerate) const626 bool ScriptInterface::SetProperty_(JS::HandleValue obj, const char* name, JS::HandleValue value, bool constant, bool enumerate) const
627 {
628 JSAutoRequest rq(m->m_cx);
629 uint attrs = 0;
630 if (constant)
631 attrs |= JSPROP_READONLY | JSPROP_PERMANENT;
632 if (enumerate)
633 attrs |= JSPROP_ENUMERATE;
634
635 if (!obj.isObject())
636 return false;
637 JS::RootedObject object(m->m_cx, &obj.toObject());
638
639 if (!JS_DefineProperty(m->m_cx, object, name, value, attrs))
640 return false;
641 return true;
642 }
643
SetProperty_(JS::HandleValue obj,const wchar_t * name,JS::HandleValue value,bool constant,bool enumerate) const644 bool ScriptInterface::SetProperty_(JS::HandleValue obj, const wchar_t* name, JS::HandleValue value, bool constant, bool enumerate) const
645 {
646 JSAutoRequest rq(m->m_cx);
647 uint attrs = 0;
648 if (constant)
649 attrs |= JSPROP_READONLY | JSPROP_PERMANENT;
650 if (enumerate)
651 attrs |= JSPROP_ENUMERATE;
652
653 if (!obj.isObject())
654 return false;
655 JS::RootedObject object(m->m_cx, &obj.toObject());
656
657 utf16string name16(name, name + wcslen(name));
658 if (!JS_DefineUCProperty(m->m_cx, object, reinterpret_cast<const char16_t*>(name16.c_str()), name16.length(), value, attrs))
659 return false;
660 return true;
661 }
662
SetPropertyInt_(JS::HandleValue obj,int name,JS::HandleValue value,bool constant,bool enumerate) const663 bool ScriptInterface::SetPropertyInt_(JS::HandleValue obj, int name, JS::HandleValue value, bool constant, bool enumerate) const
664 {
665 JSAutoRequest rq(m->m_cx);
666 uint attrs = 0;
667 if (constant)
668 attrs |= JSPROP_READONLY | JSPROP_PERMANENT;
669 if (enumerate)
670 attrs |= JSPROP_ENUMERATE;
671
672 if (!obj.isObject())
673 return false;
674 JS::RootedObject object(m->m_cx, &obj.toObject());
675
676 JS::RootedId id(m->m_cx, INT_TO_JSID(name));
677 if (!JS_DefinePropertyById(m->m_cx, object, id, value, attrs))
678 return false;
679 return true;
680 }
681
GetProperty(JS::HandleValue obj,const char * name,JS::MutableHandleValue out) const682 bool ScriptInterface::GetProperty(JS::HandleValue obj, const char* name, JS::MutableHandleValue out) const
683 {
684 return GetProperty_(obj, name, out);
685 }
686
GetProperty(JS::HandleValue obj,const char * name,JS::MutableHandleObject out) const687 bool ScriptInterface::GetProperty(JS::HandleValue obj, const char* name, JS::MutableHandleObject out) const
688 {
689 JSContext* cx = GetContext();
690 JSAutoRequest rq(cx);
691 JS::RootedValue val(cx);
692 if (!GetProperty_(obj, name, &val))
693 return false;
694 if (!val.isObject())
695 {
696 LOGERROR("GetProperty failed: trying to get an object, but the property is not an object!");
697 return false;
698 }
699
700 out.set(&val.toObject());
701 return true;
702 }
703
GetPropertyInt(JS::HandleValue obj,int name,JS::MutableHandleValue out) const704 bool ScriptInterface::GetPropertyInt(JS::HandleValue obj, int name, JS::MutableHandleValue out) const
705 {
706 return GetPropertyInt_(obj, name, out);
707 }
708
GetProperty_(JS::HandleValue obj,const char * name,JS::MutableHandleValue out) const709 bool ScriptInterface::GetProperty_(JS::HandleValue obj, const char* name, JS::MutableHandleValue out) const
710 {
711 JSAutoRequest rq(m->m_cx);
712 if (!obj.isObject())
713 return false;
714 JS::RootedObject object(m->m_cx, &obj.toObject());
715
716 if (!JS_GetProperty(m->m_cx, object, name, out))
717 return false;
718 return true;
719 }
720
GetPropertyInt_(JS::HandleValue obj,int name,JS::MutableHandleValue out) const721 bool ScriptInterface::GetPropertyInt_(JS::HandleValue obj, int name, JS::MutableHandleValue out) const
722 {
723 JSAutoRequest rq(m->m_cx);
724 JS::RootedId nameId(m->m_cx, INT_TO_JSID(name));
725 if (!obj.isObject())
726 return false;
727 JS::RootedObject object(m->m_cx, &obj.toObject());
728
729 if (!JS_GetPropertyById(m->m_cx, object, nameId, out))
730 return false;
731 return true;
732 }
733
HasProperty(JS::HandleValue obj,const char * name) const734 bool ScriptInterface::HasProperty(JS::HandleValue obj, const char* name) const
735 {
736 // TODO: proper errorhandling
737 JSAutoRequest rq(m->m_cx);
738 if (!obj.isObject())
739 return false;
740 JS::RootedObject object(m->m_cx, &obj.toObject());
741
742 bool found;
743 if (!JS_HasProperty(m->m_cx, object, name, &found))
744 return false;
745 return found;
746 }
747
EnumeratePropertyNamesWithPrefix(JS::HandleValue objVal,const char * prefix,std::vector<std::string> & out) const748 bool ScriptInterface::EnumeratePropertyNamesWithPrefix(JS::HandleValue objVal, const char* prefix, std::vector<std::string>& out) const
749 {
750 JSAutoRequest rq(m->m_cx);
751
752 if (!objVal.isObjectOrNull())
753 {
754 LOGERROR("EnumeratePropertyNamesWithPrefix expected object type!");
755 return false;
756 }
757
758 if (objVal.isNull())
759 return true; // reached the end of the prototype chain
760
761 JS::RootedObject obj(m->m_cx, &objVal.toObject());
762 JS::AutoIdArray props(m->m_cx, JS_Enumerate(m->m_cx, obj));
763 if (!props)
764 return false;
765
766 for (size_t i = 0; i < props.length(); ++i)
767 {
768 JS::RootedId id(m->m_cx, props[i]);
769 JS::RootedValue val(m->m_cx);
770 if (!JS_IdToValue(m->m_cx, id, &val))
771 return false;
772
773 if (!val.isString())
774 continue; // ignore integer properties
775
776 JS::RootedString name(m->m_cx, val.toString());
777 size_t len = strlen(prefix)+1;
778 std::vector<char> buf(len);
779 size_t prefixLen = strlen(prefix) * sizeof(char);
780 JS_EncodeStringToBuffer(m->m_cx, name, &buf[0], prefixLen);
781 buf[len-1]= '\0';
782 if (0 == strcmp(&buf[0], prefix))
783 {
784 if (JS_StringHasLatin1Chars(name))
785 {
786 size_t length;
787 JS::AutoCheckCannotGC nogc;
788 const JS::Latin1Char* chars = JS_GetLatin1StringCharsAndLength(m->m_cx, nogc, name, &length);
789 if (chars)
790 out.push_back(std::string(chars, chars+length));
791 }
792 else
793 {
794 size_t length;
795 JS::AutoCheckCannotGC nogc;
796 const char16_t* chars = JS_GetTwoByteStringCharsAndLength(m->m_cx, nogc, name, &length);
797 if (chars)
798 out.push_back(std::string(chars, chars+length));
799 }
800 }
801 }
802
803 // Recurse up the prototype chain
804 JS::RootedObject prototype(m->m_cx);
805 if (JS_GetPrototype(m->m_cx, obj, &prototype))
806 {
807 JS::RootedValue prototypeVal(m->m_cx, JS::ObjectOrNullValue(prototype));
808 if (!EnumeratePropertyNamesWithPrefix(prototypeVal, prefix, out))
809 return false;
810 }
811
812 return true;
813 }
814
SetPrototype(JS::HandleValue objVal,JS::HandleValue protoVal)815 bool ScriptInterface::SetPrototype(JS::HandleValue objVal, JS::HandleValue protoVal)
816 {
817 JSAutoRequest rq(m->m_cx);
818 if (!objVal.isObject() || !protoVal.isObject())
819 return false;
820 JS::RootedObject obj(m->m_cx, &objVal.toObject());
821 JS::RootedObject proto(m->m_cx, &protoVal.toObject());
822 return JS_SetPrototype(m->m_cx, obj, proto);
823 }
824
FreezeObject(JS::HandleValue objVal,bool deep) const825 bool ScriptInterface::FreezeObject(JS::HandleValue objVal, bool deep) const
826 {
827 JSAutoRequest rq(m->m_cx);
828 if (!objVal.isObject())
829 return false;
830
831 JS::RootedObject obj(m->m_cx, &objVal.toObject());
832
833 if (deep)
834 return JS_DeepFreezeObject(m->m_cx, obj);
835 else
836 return JS_FreezeObject(m->m_cx, obj);
837 }
838
LoadScript(const VfsPath & filename,const std::string & code) const839 bool ScriptInterface::LoadScript(const VfsPath& filename, const std::string& code) const
840 {
841 JSAutoRequest rq(m->m_cx);
842 JS::RootedObject global(m->m_cx, m->m_glob);
843 utf16string codeUtf16(code.begin(), code.end());
844 uint lineNo = 1;
845 // CompileOptions does not copy the contents of the filename string pointer.
846 // Passing a temporary string there will cause undefined behaviour, so we create a separate string to avoid the temporary.
847 std::string filenameStr = filename.string8();
848
849 JS::CompileOptions options(m->m_cx);
850 options.setFileAndLine(filenameStr.c_str(), lineNo);
851 options.setCompileAndGo(true);
852
853 JS::RootedFunction func(m->m_cx);
854 JS::AutoObjectVector emptyScopeChain(m->m_cx);
855 if (!JS::CompileFunction(m->m_cx, emptyScopeChain, options, NULL, 0, NULL,
856 reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)(codeUtf16.length()), &func))
857 return false;
858
859 JS::RootedValue rval(m->m_cx);
860 return JS_CallFunction(m->m_cx, JS::NullPtr(), func, JS::HandleValueArray::empty(), &rval);
861 }
862
CreateRuntime(shared_ptr<ScriptRuntime> parentRuntime,int runtimeSize,int heapGrowthBytesGCTrigger)863 shared_ptr<ScriptRuntime> ScriptInterface::CreateRuntime(shared_ptr<ScriptRuntime> parentRuntime, int runtimeSize, int heapGrowthBytesGCTrigger)
864 {
865 return shared_ptr<ScriptRuntime>(new ScriptRuntime(parentRuntime, runtimeSize, heapGrowthBytesGCTrigger));
866 }
867
LoadGlobalScript(const VfsPath & filename,const std::wstring & code) const868 bool ScriptInterface::LoadGlobalScript(const VfsPath& filename, const std::wstring& code) const
869 {
870 JSAutoRequest rq(m->m_cx);
871 JS::RootedObject global(m->m_cx, m->m_glob);
872 utf16string codeUtf16(code.begin(), code.end());
873 uint lineNo = 1;
874 // CompileOptions does not copy the contents of the filename string pointer.
875 // Passing a temporary string there will cause undefined behaviour, so we create a separate string to avoid the temporary.
876 std::string filenameStr = filename.string8();
877
878 JS::RootedValue rval(m->m_cx);
879 JS::CompileOptions opts(m->m_cx);
880 opts.setFileAndLine(filenameStr.c_str(), lineNo);
881 return JS::Evaluate(m->m_cx, global, opts,
882 reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)(codeUtf16.length()), &rval);
883 }
884
LoadGlobalScriptFile(const VfsPath & path) const885 bool ScriptInterface::LoadGlobalScriptFile(const VfsPath& path) const
886 {
887 JSAutoRequest rq(m->m_cx);
888 JS::RootedObject global(m->m_cx, m->m_glob);
889 if (!VfsFileExists(path))
890 {
891 LOGERROR("File '%s' does not exist", path.string8());
892 return false;
893 }
894
895 CVFSFile file;
896
897 PSRETURN ret = file.Load(g_VFS, path);
898
899 if (ret != PSRETURN_OK)
900 {
901 LOGERROR("Failed to load file '%s': %s", path.string8(), GetErrorString(ret));
902 return false;
903 }
904
905 std::wstring code = wstring_from_utf8(file.DecodeUTF8()); // assume it's UTF-8
906
907 utf16string codeUtf16(code.begin(), code.end());
908 uint lineNo = 1;
909 // CompileOptions does not copy the contents of the filename string pointer.
910 // Passing a temporary string there will cause undefined behaviour, so we create a separate string to avoid the temporary.
911 std::string filenameStr = path.string8();
912
913 JS::RootedValue rval(m->m_cx);
914 JS::CompileOptions opts(m->m_cx);
915 opts.setFileAndLine(filenameStr.c_str(), lineNo);
916 return JS::Evaluate(m->m_cx, global, opts,
917 reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)(codeUtf16.length()), &rval);
918 }
919
Eval(const char * code) const920 bool ScriptInterface::Eval(const char* code) const
921 {
922 JSAutoRequest rq(m->m_cx);
923 JS::RootedValue rval(m->m_cx);
924 return Eval_(code, &rval);
925 }
926
Eval_(const char * code,JS::MutableHandleValue rval) const927 bool ScriptInterface::Eval_(const char* code, JS::MutableHandleValue rval) const
928 {
929 JSAutoRequest rq(m->m_cx);
930 JS::RootedObject global(m->m_cx, m->m_glob);
931 utf16string codeUtf16(code, code+strlen(code));
932
933 JS::CompileOptions opts(m->m_cx);
934 opts.setFileAndLine("(eval)", 1);
935 return JS::Evaluate(m->m_cx, global, opts, reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)codeUtf16.length(), rval);
936 }
937
Eval_(const wchar_t * code,JS::MutableHandleValue rval) const938 bool ScriptInterface::Eval_(const wchar_t* code, JS::MutableHandleValue rval) const
939 {
940 JSAutoRequest rq(m->m_cx);
941 JS::RootedObject global(m->m_cx, m->m_glob);
942 utf16string codeUtf16(code, code+wcslen(code));
943
944 JS::CompileOptions opts(m->m_cx);
945 opts.setFileAndLine("(eval)", 1);
946 return JS::Evaluate(m->m_cx, global, opts, reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)codeUtf16.length(), rval);
947 }
948
ParseJSON(const std::string & string_utf8,JS::MutableHandleValue out) const949 bool ScriptInterface::ParseJSON(const std::string& string_utf8, JS::MutableHandleValue out) const
950 {
951 JSAutoRequest rq(m->m_cx);
952 std::wstring attrsW = wstring_from_utf8(string_utf8);
953 utf16string string(attrsW.begin(), attrsW.end());
954 if (JS_ParseJSON(m->m_cx, reinterpret_cast<const char16_t*>(string.c_str()), (u32)string.size(), out))
955 return true;
956
957 LOGERROR("JS_ParseJSON failed!");
958 if (!JS_IsExceptionPending(m->m_cx))
959 return false;
960
961 JS::RootedValue exc(m->m_cx);
962 if (!JS_GetPendingException(m->m_cx, &exc))
963 return false;
964
965 JS_ClearPendingException(m->m_cx);
966 // We expect an object of type SyntaxError
967 if (!exc.isObject())
968 return false;
969
970 JS::RootedValue rval(m->m_cx);
971 JS::RootedObject excObj(m->m_cx, &exc.toObject());
972 if (!JS_CallFunctionName(m->m_cx, excObj, "toString", JS::HandleValueArray::empty(), &rval))
973 return false;
974
975 std::wstring error;
976 ScriptInterface::FromJSVal(m->m_cx, rval, error);
977 LOGERROR("%s", utf8_from_wstring(error));
978 return false;
979 }
980
ReadJSONFile(const VfsPath & path,JS::MutableHandleValue out) const981 void ScriptInterface::ReadJSONFile(const VfsPath& path, JS::MutableHandleValue out) const
982 {
983 if (!VfsFileExists(path))
984 {
985 LOGERROR("File '%s' does not exist", path.string8());
986 return;
987 }
988
989 CVFSFile file;
990
991 PSRETURN ret = file.Load(g_VFS, path);
992
993 if (ret != PSRETURN_OK)
994 {
995 LOGERROR("Failed to load file '%s': %s", path.string8(), GetErrorString(ret));
996 return;
997 }
998
999 std::string content(file.DecodeUTF8()); // assume it's UTF-8
1000
1001 if (!ParseJSON(content, out))
1002 LOGERROR("Failed to parse '%s'", path.string8());
1003 }
1004
1005 struct Stringifier
1006 {
callbackStringifier1007 static bool callback(const char16_t* buf, u32 len, void* data)
1008 {
1009 utf16string str(buf, buf+len);
1010 std::wstring strw(str.begin(), str.end());
1011
1012 Status err; // ignore Unicode errors
1013 static_cast<Stringifier*>(data)->stream << utf8_from_wstring(strw, &err);
1014 return true;
1015 }
1016
1017 std::stringstream stream;
1018 };
1019
1020 // TODO: It's not quite clear why JS_Stringify needs JS::MutableHandleValue. |obj| should not get modified.
1021 // It probably has historical reasons and could be changed by SpiderMonkey in the future.
StringifyJSON(JS::MutableHandleValue obj,bool indent) const1022 std::string ScriptInterface::StringifyJSON(JS::MutableHandleValue obj, bool indent) const
1023 {
1024 JSAutoRequest rq(m->m_cx);
1025 Stringifier str;
1026 JS::RootedValue indentVal(m->m_cx, indent ? JS::Int32Value(2) : JS::UndefinedValue());
1027 if (!JS_Stringify(m->m_cx, obj, JS::NullPtr(), indentVal, &Stringifier::callback, &str))
1028 {
1029 JS_ClearPendingException(m->m_cx);
1030 LOGERROR("StringifyJSON failed");
1031 return std::string();
1032 }
1033
1034 return str.stream.str();
1035 }
1036
1037
ToString(JS::MutableHandleValue obj,bool pretty) const1038 std::string ScriptInterface::ToString(JS::MutableHandleValue obj, bool pretty) const
1039 {
1040 JSAutoRequest rq(m->m_cx);
1041
1042 if (obj.isUndefined())
1043 return "(void 0)";
1044
1045 // Try to stringify as JSON if possible
1046 // (TODO: this is maybe a bad idea since it'll drop 'undefined' values silently)
1047 if (pretty)
1048 {
1049 Stringifier str;
1050 JS::RootedValue indentVal(m->m_cx, JS::Int32Value(2));
1051
1052 // Temporary disable the error reporter, so we don't print complaints about cyclic values
1053 JSErrorReporter er = JS_SetErrorReporter(m->m_runtime->m_rt, NULL);
1054
1055 bool ok = JS_Stringify(m->m_cx, obj, JS::NullPtr(), indentVal, &Stringifier::callback, &str);
1056
1057 // Restore error reporter
1058 JS_SetErrorReporter(m->m_runtime->m_rt, er);
1059
1060 if (ok)
1061 return str.stream.str();
1062
1063 // Clear the exception set when Stringify failed
1064 JS_ClearPendingException(m->m_cx);
1065 }
1066
1067 // Caller didn't want pretty output, or JSON conversion failed (e.g. due to cycles),
1068 // so fall back to obj.toSource()
1069
1070 std::wstring source = L"(error)";
1071 CallFunction(obj, "toSource", source);
1072 return utf8_from_wstring(source);
1073 }
1074
ReportError(const char * msg) const1075 void ScriptInterface::ReportError(const char* msg) const
1076 {
1077 JSAutoRequest rq(m->m_cx);
1078 // JS_ReportError by itself doesn't seem to set a JS-style exception, and so
1079 // script callers will be unable to catch anything. So use JS_SetPendingException
1080 // to make sure there really is a script-level exception. But just set it to undefined
1081 // because there's not much value yet in throwing a real exception object.
1082 JS_SetPendingException(m->m_cx, JS::UndefinedHandleValue);
1083 // And report the actual error
1084 JS_ReportError(m->m_cx, "%s", msg);
1085
1086 // TODO: Why doesn't JS_ReportPendingException(m->m_cx); work?
1087 }
1088
IsExceptionPending(JSContext * cx)1089 bool ScriptInterface::IsExceptionPending(JSContext* cx)
1090 {
1091 JSAutoRequest rq(cx);
1092 return JS_IsExceptionPending(cx) ? true : false;
1093 }
1094
GetClass(JS::HandleObject obj)1095 const JSClass* ScriptInterface::GetClass(JS::HandleObject obj)
1096 {
1097 return JS_GetClass(obj);
1098 }
1099
GetPrivate(JS::HandleObject obj)1100 void* ScriptInterface::GetPrivate(JS::HandleObject obj)
1101 {
1102 // TODO: use JS_GetInstancePrivate
1103 return JS_GetPrivate(obj);
1104 }
1105
CloneValueFromOtherContext(const ScriptInterface & otherContext,JS::HandleValue val) const1106 JS::Value ScriptInterface::CloneValueFromOtherContext(const ScriptInterface& otherContext, JS::HandleValue val) const
1107 {
1108 PROFILE("CloneValueFromOtherContext");
1109 JSAutoRequest rq(m->m_cx);
1110 JS::RootedValue out(m->m_cx);
1111 shared_ptr<StructuredClone> structuredClone = otherContext.WriteStructuredClone(val);
1112 ReadStructuredClone(structuredClone, &out);
1113 return out.get();
1114 }
1115
StructuredClone()1116 ScriptInterface::StructuredClone::StructuredClone() :
1117 m_Data(NULL), m_Size(0)
1118 {
1119 }
1120
~StructuredClone()1121 ScriptInterface::StructuredClone::~StructuredClone()
1122 {
1123 if (m_Data)
1124 JS_ClearStructuredClone(m_Data, m_Size, NULL, NULL);
1125 }
1126
WriteStructuredClone(JS::HandleValue v) const1127 shared_ptr<ScriptInterface::StructuredClone> ScriptInterface::WriteStructuredClone(JS::HandleValue v) const
1128 {
1129 JSAutoRequest rq(m->m_cx);
1130 u64* data = NULL;
1131 size_t nbytes = 0;
1132 if (!JS_WriteStructuredClone(m->m_cx, v, &data, &nbytes, NULL, NULL, JS::UndefinedHandleValue))
1133 {
1134 debug_warn(L"Writing a structured clone with JS_WriteStructuredClone failed!");
1135 return shared_ptr<StructuredClone>();
1136 }
1137
1138 shared_ptr<StructuredClone> ret(new StructuredClone);
1139 ret->m_Data = data;
1140 ret->m_Size = nbytes;
1141 return ret;
1142 }
1143
ReadStructuredClone(const shared_ptr<ScriptInterface::StructuredClone> & ptr,JS::MutableHandleValue ret) const1144 void ScriptInterface::ReadStructuredClone(const shared_ptr<ScriptInterface::StructuredClone>& ptr, JS::MutableHandleValue ret) const
1145 {
1146 JSAutoRequest rq(m->m_cx);
1147 JS_ReadStructuredClone(m->m_cx, ptr->m_Data, ptr->m_Size, JS_STRUCTURED_CLONE_VERSION, ret, NULL, NULL);
1148 }
1149