forked from 0ad/0ad
[SM68 2/2] Update to Spidermonkey 68 APIs
No noteworthy API changes.
Details:
- Remove UTF16 script execution since UTF8 is supported in SM68 and
going forward
- Several new headers includes are required
- Realms replace Compartments as "global holders" (see meta-Bug 1357862)
- JSRequests are removed entirely (Bug 722345), see also aae417bd29
- Trivial API updates in ProxyHandlers, ArrayBuffer, Warnings, GC
reasons, Context options, ObjectIsFunction, ValueVectors and
JSCompartment
See also the migration guide:
https://github.com/mozilla-spidermonkey/spidermonkey-embedding-examples/blob/esr78/docs/Migration%20Guide.md
Tested by: Freagarach, Stan, Subitaneo
Fixes #5860
Differential Revision: https://code.wildfiregames.com/D3144
This was SVN commit r24297.
This commit is contained in:
parent
d8332a2938
commit
02578e46bf
@ -441,7 +441,7 @@ void CGUI::SetGlobalHotkey(const CStr& hotkeyTag, const CStr& eventName, JS::Han
|
||||
return;
|
||||
}
|
||||
|
||||
if (!function.isObject() || !JS_ObjectIsFunction(rq.cx, &function.toObject()))
|
||||
if (!function.isObject() || !JS_ObjectIsFunction(&function.toObject()))
|
||||
{
|
||||
ScriptException::Raise(rq, "Cannot assign non-function value to global hotkey '%s'", hotkeyTag.c_str());
|
||||
return;
|
||||
@ -923,7 +923,7 @@ void CGUI::Xeromyces_ReadScript(XMBElement Element, CXeromyces* pFile, std::unor
|
||||
|
||||
CStr code(Element.GetText());
|
||||
if (!code.empty())
|
||||
m_ScriptInterface->LoadGlobalScript(L"Some XML file", code.FromUTF8());
|
||||
m_ScriptInterface->LoadGlobalScript(L"Some XML file", code);
|
||||
}
|
||||
|
||||
void CGUI::Xeromyces_ReadSprite(XMBElement Element, CXeromyces* pFile)
|
||||
|
@ -226,7 +226,7 @@ void CGUIManager::SGUIPage::SetCallbackFunction(ScriptInterface& scriptInterface
|
||||
|
||||
ScriptRequest rq(scriptInterface);
|
||||
|
||||
if (!JS_ObjectIsFunction(rq.cx, &callbackFunc.toObject()))
|
||||
if (!JS_ObjectIsFunction(&callbackFunc.toObject()))
|
||||
{
|
||||
LOGERROR("Given callback handler is not a function!");
|
||||
return;
|
||||
@ -254,7 +254,7 @@ void CGUIManager::SGUIPage::PerformCallbackFunction(ScriptInterface::StructuredC
|
||||
if (args)
|
||||
scriptInterface->ReadStructuredClone(args, &argVal);
|
||||
|
||||
JS::AutoValueVector paramData(rq.cx);
|
||||
JS::RootedValueVector paramData(rq.cx);
|
||||
DISCARD paramData.append(argVal);
|
||||
|
||||
JS::RootedValue result(rq.cx);
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "ps/GameSetup/Config.h"
|
||||
#include "ps/Profile.h"
|
||||
#include "scriptinterface/ScriptContext.h"
|
||||
#include "scriptinterface/ScriptExtraHeaders.h"
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
#include "soundmanager/ISoundManager.h"
|
||||
|
||||
@ -320,13 +321,16 @@ void IGUIObject::RegisterScriptHandler(const CStr& eventName, const CStr& Code,
|
||||
char buf[64];
|
||||
sprintf_s(buf, ARRAY_SIZE(buf), "__eventhandler%d (%s)", x++, eventName.c_str());
|
||||
|
||||
// TODO: this is essentially the same code as ScriptInterface::LoadScript (with a tweak for the argument).
|
||||
JS::CompileOptions options(rq.cx);
|
||||
options.setFileAndLine(CodeName.c_str(), 0);
|
||||
options.setIsRunOnce(false);
|
||||
|
||||
JS::RootedFunction func(rq.cx);
|
||||
JS::AutoObjectVector emptyScopeChain(rq.cx);
|
||||
if (!JS::CompileFunction(rq.cx, emptyScopeChain, options, buf, paramCount, paramNames, Code.c_str(), Code.length(), &func))
|
||||
JS::SourceText<mozilla::Utf8Unit> src;
|
||||
ENSURE(src.init(rq.cx, Code.c_str(), Code.length(), JS::SourceOwnership::Borrowed));
|
||||
JS::RootedObjectVector emptyScopeChain(rq.cx);
|
||||
JS::RootedFunction func(rq.cx, JS::CompileFunction(rq.cx, emptyScopeChain, options, buf, paramCount, paramNames, src));
|
||||
if (func == nullptr)
|
||||
{
|
||||
LOGERROR("RegisterScriptHandler: Failed to compile the script for %s", eventName.c_str());
|
||||
return;
|
||||
@ -408,7 +412,7 @@ InReaction IGUIObject::SendMouseEvent(EGUIMessageType type, const CStr& eventNam
|
||||
"x", mousePos.x,
|
||||
"y", mousePos.y,
|
||||
"buttons", m_pGUI.GetMouseButtons());
|
||||
JS::AutoValueVector paramData(rq.cx);
|
||||
JS::RootedValueVector paramData(rq.cx);
|
||||
DISCARD paramData.append(mouse);
|
||||
ScriptEvent(eventName, paramData);
|
||||
|
||||
@ -426,7 +430,7 @@ bool IGUIObject::ScriptEventWithReturn(const CStr& eventName)
|
||||
return false;
|
||||
|
||||
ScriptRequest rq(m_pGUI.GetScriptInterface());
|
||||
JS::AutoValueVector paramData(rq.cx);
|
||||
JS::RootedValueVector paramData(rq.cx);
|
||||
return ScriptEventWithReturn(eventName, paramData);
|
||||
}
|
||||
|
||||
|
@ -250,7 +250,7 @@ bool CMiniMap::FireWorldClickEvent(int button, int UNUSED(clicks))
|
||||
JS::RootedValue buttonJs(rq.cx);
|
||||
ScriptInterface::ToJSVal(rq, &buttonJs, button);
|
||||
|
||||
JS::AutoValueVector paramData(rq.cx);
|
||||
JS::RootedValueVector paramData(rq.cx);
|
||||
DISCARD paramData.append(coords);
|
||||
DISCARD paramData.append(buttonJs);
|
||||
|
||||
|
@ -91,15 +91,15 @@ protected:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Return nothing.
|
||||
virtual bool ownPropertyKeys(JSContext* UNUSED(cx), JS::HandleObject UNUSED(proxy), JS::AutoIdVector& UNUSED(props)) const override
|
||||
// No accessible properties.
|
||||
virtual bool ownPropertyKeys(JSContext* UNUSED(cx), JS::HandleObject UNUSED(proxy), JS::MutableHandleIdVector UNUSED(props)) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Return nothing.
|
||||
virtual JSObject* enumerate(JSContext* UNUSED(cx), JS::HandleObject UNUSED(proxy)) const override
|
||||
// Nothing to enumerate.
|
||||
virtual bool enumerate(JSContext* UNUSED(cx), JS::HandleObject UNUSED(proxy), JS::MutableHandleIdVector UNUSED(props)) const override
|
||||
{
|
||||
return nullptr;
|
||||
return true;
|
||||
}
|
||||
// Throw an exception is JS attempts to query the prototype.
|
||||
virtual bool getPrototypeIfOrdinary(JSContext* UNUSED(cx), JS::HandleObject UNUSED(proxy), bool* UNUSED(isOrdinary), JS::MutableHandleObject UNUSED(protop)) const override
|
||||
|
@ -151,7 +151,7 @@ bool JSI_GUIProxy<T>::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id
|
||||
// Use onWhatever to set event handlers
|
||||
if (propName.substr(0, 2) == "on")
|
||||
{
|
||||
if (vp.isPrimitive() || vp.isNull() || !JS_ObjectIsFunction(rq.cx, &vp.toObject()))
|
||||
if (vp.isPrimitive() || vp.isNull() || !JS_ObjectIsFunction(&vp.toObject()))
|
||||
{
|
||||
LOGERROR("on- event-handlers must be functions");
|
||||
return result.fail(JSMSG_NOT_FUNCTION);
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/ConfigDB.h"
|
||||
#include "ps/Pyrogenesis.h"
|
||||
#include "scriptinterface/ScriptExtraHeaders.h" // StructuredClone
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
|
||||
#include <gloox/gloox.h>
|
||||
|
@ -558,7 +558,7 @@ void CConsole::ProcessBuffer(const wchar_t* szLine)
|
||||
ScriptRequest rq(*pScriptInterface);
|
||||
|
||||
JS::RootedValue rval(rq.cx);
|
||||
pScriptInterface->Eval(szLine, &rval);
|
||||
pScriptInterface->Eval(CStrW(szLine).ToUTF8().c_str(), &rval);
|
||||
if (!rval.isUndefined())
|
||||
InsertMessage(pScriptInterface->ToString(&rval));
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ void GUI_DisplayLoadProgress(int percent, const wchar_t* pending_task)
|
||||
const ScriptInterface& scriptInterface = *(g_GUI->GetActiveGUI()->GetScriptInterface());
|
||||
ScriptRequest rq(scriptInterface);
|
||||
|
||||
JS::AutoValueVector paramData(rq.cx);
|
||||
JS::RootedValueVector paramData(rq.cx);
|
||||
|
||||
DISCARD paramData.append(JS::NumberValue(percent));
|
||||
|
||||
|
@ -161,14 +161,14 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
|
||||
#undef OVERLOADS
|
||||
|
||||
template<int i, typename T, typename... Ts>
|
||||
static void AssignOrToJSValHelper(const ScriptRequest& rq, JS::AutoValueVector& argv, const T& a, const Ts&... params)
|
||||
static void AssignOrToJSValHelper(const ScriptRequest& rq, JS::MutableHandleValueVector argv, const T& a, const Ts&... params)
|
||||
{
|
||||
ScriptInterface::AssignOrToJSVal(rq, argv[i], a);
|
||||
AssignOrToJSValHelper<i+1>(rq, argv, params...);
|
||||
}
|
||||
|
||||
template<int i, typename... Ts>
|
||||
static void AssignOrToJSValHelper(const ScriptRequest& UNUSED(rq), JS::AutoValueVector& UNUSED(argv))
|
||||
static void AssignOrToJSValHelper(const ScriptRequest& UNUSED(rq), JS::MutableHandleValueVector UNUSED(argv))
|
||||
{
|
||||
cassert(sizeof...(Ts) == 0);
|
||||
// Nop, for terminating the template recursion.
|
||||
@ -179,9 +179,9 @@ bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, R& ret
|
||||
{
|
||||
ScriptRequest rq(this);
|
||||
JS::RootedValue jsRet(rq.cx);
|
||||
JS::AutoValueVector argv(rq.cx);
|
||||
JS::RootedValueVector argv(rq.cx);
|
||||
DISCARD argv.resize(sizeof...(Ts));
|
||||
AssignOrToJSValHelper<0>(rq, argv, params...);
|
||||
AssignOrToJSValHelper<0>(rq, &argv, params...);
|
||||
if (!CallFunction_(val, name, argv, &jsRet))
|
||||
return false;
|
||||
return FromJSVal(rq, jsRet, ret);
|
||||
@ -192,9 +192,9 @@ bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, JS::Ro
|
||||
{
|
||||
ScriptRequest rq(this);
|
||||
JS::MutableHandle<R> jsRet(ret);
|
||||
JS::AutoValueVector argv(rq.cx);
|
||||
JS::RootedValueVector argv(rq.cx);
|
||||
DISCARD argv.resize(sizeof...(Ts));
|
||||
AssignOrToJSValHelper<0>(rq, argv, params...);
|
||||
AssignOrToJSValHelper<0>(rq, &argv, params...);
|
||||
return CallFunction_(val, name, argv, jsRet);
|
||||
}
|
||||
|
||||
@ -202,9 +202,9 @@ template<typename R, typename... Ts>
|
||||
bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, JS::MutableHandle<R> ret, const Ts&... params) const
|
||||
{
|
||||
ScriptRequest rq(this);
|
||||
JS::AutoValueVector argv(rq.cx);
|
||||
JS::RootedValueVector argv(rq.cx);
|
||||
DISCARD argv.resize(sizeof...(Ts));
|
||||
AssignOrToJSValHelper<0>(rq, argv, params...);
|
||||
AssignOrToJSValHelper<0>(rq, &argv, params...);
|
||||
return CallFunction_(val, name, argv, ret);
|
||||
}
|
||||
|
||||
@ -214,9 +214,9 @@ bool ScriptInterface::CallFunctionVoid(JS::HandleValue val, const char* name, co
|
||||
{
|
||||
ScriptRequest rq(this);
|
||||
JS::RootedValue jsRet(rq.cx);
|
||||
JS::AutoValueVector argv(rq.cx);
|
||||
JS::RootedValueVector argv(rq.cx);
|
||||
DISCARD argv.resize(sizeof...(Ts));
|
||||
AssignOrToJSValHelper<0>(rq, argv, params...);
|
||||
AssignOrToJSValHelper<0>(rq, &argv, params...);
|
||||
return CallFunction_(val, name, argv, &jsRet);
|
||||
}
|
||||
|
||||
|
@ -21,10 +21,10 @@
|
||||
|
||||
#include "ps/GameSetup/Config.h"
|
||||
#include "ps/Profile.h"
|
||||
#include "scriptinterface/ScriptExtraHeaders.h"
|
||||
#include "scriptinterface/ScriptEngine.h"
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
|
||||
|
||||
void GCSliceCallbackHook(JSContext* UNUSED(cx), JS::GCProgress progress, const JS::GCDescription& UNUSED(desc))
|
||||
{
|
||||
/**
|
||||
@ -130,15 +130,15 @@ ScriptContext::~ScriptContext()
|
||||
ScriptEngine::GetSingleton().UnRegisterContext(m_cx);
|
||||
}
|
||||
|
||||
void ScriptContext::RegisterCompartment(JSCompartment* cmpt)
|
||||
void ScriptContext::RegisterRealm(JS::Realm* realm)
|
||||
{
|
||||
ENSURE(cmpt);
|
||||
m_Compartments.push_back(cmpt);
|
||||
ENSURE(realm);
|
||||
m_Realms.push_back(realm);
|
||||
}
|
||||
|
||||
void ScriptContext::UnRegisterCompartment(JSCompartment* cmpt)
|
||||
void ScriptContext::UnRegisterRealm(JS::Realm* realm)
|
||||
{
|
||||
m_Compartments.remove(cmpt);
|
||||
m_Realms.remove(realm);
|
||||
}
|
||||
|
||||
#define GC_DEBUG_PRINT 0
|
||||
@ -204,8 +204,8 @@ void ScriptContext::MaybeIncrementalGC(double delay)
|
||||
#if GC_DEBUG_PRINT
|
||||
printf("Finishing incremental GC because gcBytes > m_ContextSize / 2. \n");
|
||||
#endif
|
||||
PrepareCompartmentsForIncrementalGC();
|
||||
JS::FinishIncrementalGC(m_cx, JS::gcreason::API);
|
||||
PrepareZonesForIncrementalGC();
|
||||
JS::FinishIncrementalGC(m_cx, JS::GCReason::API);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -233,11 +233,11 @@ void ScriptContext::MaybeIncrementalGC(double delay)
|
||||
else
|
||||
printf("Running incremental GC slice \n");
|
||||
#endif
|
||||
PrepareCompartmentsForIncrementalGC();
|
||||
PrepareZonesForIncrementalGC();
|
||||
if (!JS::IsIncrementalGCInProgress(m_cx))
|
||||
JS::StartIncrementalGC(m_cx, GC_NORMAL, JS::gcreason::API, GCSliceTimeBudget);
|
||||
JS::StartIncrementalGC(m_cx, GC_NORMAL, JS::GCReason::API, GCSliceTimeBudget);
|
||||
else
|
||||
JS::IncrementalGCSlice(m_cx, JS::gcreason::API, GCSliceTimeBudget);
|
||||
JS::IncrementalGCSlice(m_cx, JS::GCReason::API, GCSliceTimeBudget);
|
||||
}
|
||||
m_LastGCBytes = gcBytes;
|
||||
}
|
||||
@ -248,12 +248,12 @@ void ScriptContext::ShrinkingGC()
|
||||
{
|
||||
JS_SetGCParameter(m_cx, JSGC_MODE, JSGC_MODE_ZONE);
|
||||
JS::PrepareForFullGC(m_cx);
|
||||
JS::GCForReason(m_cx, GC_SHRINK, JS::gcreason::API);
|
||||
JS::NonIncrementalGC(m_cx, GC_SHRINK, JS::GCReason::API);
|
||||
JS_SetGCParameter(m_cx, JSGC_MODE, JSGC_MODE_INCREMENTAL);
|
||||
}
|
||||
|
||||
void ScriptContext::PrepareCompartmentsForIncrementalGC() const
|
||||
void ScriptContext::PrepareZonesForIncrementalGC() const
|
||||
{
|
||||
for (JSCompartment* const& cmpt : m_Compartments)
|
||||
JS::PrepareZoneForGC(js::GetCompartmentZone(cmpt));
|
||||
for (JS::Realm* const& realm : m_Realms)
|
||||
JS::PrepareZoneForGC(js::GetRealmZone(realm));
|
||||
}
|
||||
|
@ -69,10 +69,10 @@ public:
|
||||
void ShrinkingGC();
|
||||
|
||||
/**
|
||||
* This is used to keep track of compartments which should be prepared for a GC.
|
||||
* This is used to keep track of realms which should be prepared for a GC.
|
||||
*/
|
||||
void RegisterCompartment(JSCompartment* cmpt);
|
||||
void UnRegisterCompartment(JSCompartment* cmpt);
|
||||
void RegisterRealm(JS::Realm* realm);
|
||||
void UnRegisterRealm(JS::Realm* realm);
|
||||
|
||||
/**
|
||||
* GetGeneralJSContext returns the context without starting a GC request and without
|
||||
@ -87,8 +87,8 @@ private:
|
||||
|
||||
JSContext* m_cx;
|
||||
|
||||
void PrepareCompartmentsForIncrementalGC() const;
|
||||
std::list<JSCompartment*> m_Compartments;
|
||||
void PrepareZonesForIncrementalGC() const;
|
||||
std::list<JS::Realm*> m_Realms;
|
||||
|
||||
int m_ContextSize;
|
||||
int m_HeapGrowthBytesGCTrigger;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "ScriptConversions.h"
|
||||
#include "ScriptExtraHeaders.h"
|
||||
|
||||
#include "graphics/Entity.h"
|
||||
#include "maths/Vector2D.h"
|
||||
@ -28,7 +29,7 @@
|
||||
#define FAIL(msg) STMT(LOGERROR(msg); return false)
|
||||
|
||||
// Implicit type conversions often hide bugs, so warn about them
|
||||
#define WARN_IF_NOT(c, v) STMT(if (!(c)) { JS_ReportWarningUTF8(rq.cx, "Script value conversion check failed: %s (got type %s)", #c, JS::InformalValueTypeName(v)); })
|
||||
#define WARN_IF_NOT(c, v) STMT(if (!(c)) { JS::WarnUTF8(rq.cx, "Script value conversion check failed: %s (got type %s)", #c, JS::InformalValueTypeName(v)); })
|
||||
|
||||
template<> bool ScriptInterface::FromJSVal<bool>(const ScriptRequest& rq, JS::HandleValue v, bool& out)
|
||||
{
|
||||
|
@ -46,10 +46,20 @@
|
||||
#endif
|
||||
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
#include "js/ArrayBuffer.h"
|
||||
#include "js/Conversions.h"
|
||||
#include "js/ContextOptions.h"
|
||||
#include "js/ForOfIterator.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "js/JSON.h"
|
||||
#include "js/StructuredClone.h"
|
||||
#include "js/Proxy.h"
|
||||
#include "js/Warnings.h"
|
||||
|
||||
// CompileFunction & Evaluate
|
||||
#include "js/CompilationAndEvaluation.h"
|
||||
#include "js/SourceText.h"
|
||||
|
||||
#undef signbit
|
||||
|
||||
|
@ -18,9 +18,36 @@
|
||||
#ifndef INCLUDED_SCRIPTFORWARD
|
||||
#define INCLUDED_SCRIPTFORWARD
|
||||
|
||||
|
||||
// Ignore some harmless warnings
|
||||
#if GCC_VERSION
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#endif
|
||||
#if CLANG_VERSION
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
#endif
|
||||
#if MSC_VERSION
|
||||
# pragma warning(push, 1)
|
||||
# pragma warning(disable: 4100)
|
||||
#endif
|
||||
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
class ScriptInterface;
|
||||
class ScriptRequest;
|
||||
|
||||
#if GCC_VERSION
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
#if CLANG_VERSION
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
#if MSC_VERSION
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
#endif // INCLUDED_SCRIPTFORWARD
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "ScriptContext.h"
|
||||
#include "ScriptExtraHeaders.h"
|
||||
#include "ScriptInterface.h"
|
||||
#include "ScriptStats.h"
|
||||
|
||||
@ -42,8 +43,6 @@
|
||||
|
||||
#include "valgrind.h"
|
||||
|
||||
#include "scriptinterface/ScriptExtraHeaders.h"
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Abstractions of various SpiderMonkey features.
|
||||
@ -75,8 +74,7 @@ struct ScriptInterface_impl
|
||||
ScriptRequest::ScriptRequest(const ScriptInterface& scriptInterface) :
|
||||
cx(scriptInterface.m->m_cx)
|
||||
{
|
||||
JS_BeginRequest(cx);
|
||||
m_formerCompartment = JS_EnterCompartment(cx, scriptInterface.m->m_glob);
|
||||
m_formerRealm = JS::EnterRealm(cx, scriptInterface.m->m_glob);
|
||||
glob = JS::CurrentGlobalOrNull(cx);
|
||||
}
|
||||
|
||||
@ -87,8 +85,7 @@ JS::Value ScriptRequest::globalValue() const
|
||||
|
||||
ScriptRequest::~ScriptRequest()
|
||||
{
|
||||
JS_LeaveCompartment(cx, m_formerCompartment);
|
||||
JS_EndRequest(cx);
|
||||
JS::LeaveRealm(cx, m_formerRealm);
|
||||
}
|
||||
|
||||
namespace
|
||||
@ -324,17 +321,16 @@ bool ScriptInterface::MathRandom(double& nbr)
|
||||
ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, const shared_ptr<ScriptContext>& context) :
|
||||
m_context(context), m_cx(context->GetGeneralJSContext()), m_glob(context->GetGeneralJSContext()), m_nativeScope(context->GetGeneralJSContext())
|
||||
{
|
||||
JS::CompartmentCreationOptions creationOpt;
|
||||
JS::RealmCreationOptions creationOpt;
|
||||
// Keep JIT code during non-shrinking GCs. This brings a quite big performance improvement.
|
||||
creationOpt.setPreserveJitCode(true);
|
||||
JS::CompartmentOptions opt(creationOpt, JS::CompartmentBehaviors{});
|
||||
JS::RealmOptions opt(creationOpt, JS::RealmBehaviors{});
|
||||
|
||||
JSAutoRequest rq(m_cx);
|
||||
m_glob = JS_NewGlobalObject(m_cx, &global_class, nullptr, JS::OnNewGlobalHookOption::FireOnNewGlobalHook, opt);
|
||||
|
||||
JSAutoCompartment autoCmpt(m_cx, m_glob);
|
||||
JSAutoRealm autoRealm(m_cx, m_glob);
|
||||
|
||||
ENSURE(JS_InitStandardClasses(m_cx, m_glob));
|
||||
ENSURE(JS::InitRealmStandardClasses(m_cx));
|
||||
|
||||
JS_DefineProperty(m_cx, m_glob, "global", m_glob, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
||||
|
||||
@ -351,18 +347,17 @@ ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, const sh
|
||||
Register("ProfileStop", ::ProfileStop, 0);
|
||||
Register("ProfileAttribute", ::ProfileAttribute, 1);
|
||||
|
||||
m_context->RegisterCompartment(js::GetObjectCompartment(m_glob));
|
||||
m_context->RegisterRealm(JS::GetObjectRealmOrNull(m_glob));
|
||||
}
|
||||
|
||||
ScriptInterface_impl::~ScriptInterface_impl()
|
||||
{
|
||||
m_context->UnRegisterCompartment(js::GetObjectCompartment(m_glob));
|
||||
m_context->UnRegisterRealm(JS::GetObjectRealmOrNull(m_glob));
|
||||
}
|
||||
|
||||
void ScriptInterface_impl::Register(const char* name, JSNative fptr, uint nargs) const
|
||||
{
|
||||
JSAutoRequest rq(m_cx);
|
||||
JSAutoCompartment autoCmpt(m_cx, m_glob);
|
||||
JSAutoRealm autoRealm(m_cx, m_glob);
|
||||
JS::RootedObject nativeScope(m_cx, m_nativeScope);
|
||||
JS::RootedFunction func(m_cx, JS_DefineFunction(m_cx, nativeScope, name, fptr, nargs, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT));
|
||||
}
|
||||
@ -718,7 +713,7 @@ bool ScriptInterface::EnumeratePropertyNames(JS::HandleValue objVal, bool enumer
|
||||
}
|
||||
|
||||
JS::RootedObject obj(rq.cx, &objVal.toObject());
|
||||
JS::AutoIdVector props(rq.cx);
|
||||
JS::RootedIdVector props(rq.cx);
|
||||
// This recurses up the prototype chain on its own.
|
||||
if (!js::GetPropertyKeys(rq.cx, obj, enumerableOnly? 0 : JSITER_HIDDEN, &props))
|
||||
return false;
|
||||
@ -774,20 +769,20 @@ bool ScriptInterface::LoadScript(const VfsPath& filename, const std::string& cod
|
||||
{
|
||||
ScriptRequest rq(this);
|
||||
JS::RootedObject global(rq.cx, rq.glob);
|
||||
utf16string codeUtf16(code.begin(), code.end());
|
||||
uint lineNo = 1;
|
||||
|
||||
// CompileOptions does not copy the contents of the filename string pointer.
|
||||
// Passing a temporary string there will cause undefined behaviour, so we create a separate string to avoid the temporary.
|
||||
std::string filenameStr = filename.string8();
|
||||
|
||||
JS::CompileOptions options(rq.cx);
|
||||
options.setFileAndLine(filenameStr.c_str(), lineNo);
|
||||
options.setFileAndLine(filenameStr.c_str(), 1);
|
||||
options.setIsRunOnce(false);
|
||||
|
||||
JS::RootedFunction func(rq.cx);
|
||||
JS::AutoObjectVector emptyScopeChain(rq.cx);
|
||||
if (!JS::CompileFunction(rq.cx, emptyScopeChain, options, NULL, 0, NULL,
|
||||
reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)(codeUtf16.length()), &func))
|
||||
JS::SourceText<mozilla::Utf8Unit> src;
|
||||
ENSURE(src.init(rq.cx, code.c_str(), code.length(), JS::SourceOwnership::Borrowed));
|
||||
JS::RootedObjectVector emptyScopeChain(rq.cx);
|
||||
JS::RootedFunction func(rq.cx, JS::CompileFunction(rq.cx, emptyScopeChain, options, NULL, 0, NULL, src));
|
||||
if (func == nullptr)
|
||||
{
|
||||
ScriptException::CatchPending(rq);
|
||||
return false;
|
||||
@ -801,19 +796,20 @@ bool ScriptInterface::LoadScript(const VfsPath& filename, const std::string& cod
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScriptInterface::LoadGlobalScript(const VfsPath& filename, const std::wstring& code) const
|
||||
bool ScriptInterface::LoadGlobalScript(const VfsPath& filename, const std::string& code) const
|
||||
{
|
||||
ScriptRequest rq(this);
|
||||
utf16string codeUtf16(code.begin(), code.end());
|
||||
uint lineNo = 1;
|
||||
// CompileOptions does not copy the contents of the filename string pointer.
|
||||
// Passing a temporary string there will cause undefined behaviour, so we create a separate string to avoid the temporary.
|
||||
std::string filenameStr = filename.string8();
|
||||
|
||||
JS::RootedValue rval(rq.cx);
|
||||
JS::CompileOptions opts(rq.cx);
|
||||
opts.setFileAndLine(filenameStr.c_str(), lineNo);
|
||||
if (JS::Evaluate(rq.cx, opts, reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)(codeUtf16.length()), &rval))
|
||||
opts.setFileAndLine(filenameStr.c_str(), 1);
|
||||
|
||||
JS::SourceText<mozilla::Utf8Unit> src;
|
||||
ENSURE(src.init(rq.cx, code.c_str(), code.length(), JS::SourceOwnership::Borrowed));
|
||||
if (JS::Evaluate(rq.cx, opts, src, &rval))
|
||||
return true;
|
||||
|
||||
ScriptException::CatchPending(rq);
|
||||
@ -839,9 +835,8 @@ bool ScriptInterface::LoadGlobalScriptFile(const VfsPath& path) const
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring code = wstring_from_utf8(file.DecodeUTF8()); // assume it's UTF-8
|
||||
CStr code = file.DecodeUTF8(); // assume it's UTF-8
|
||||
|
||||
utf16string codeUtf16(code.begin(), code.end());
|
||||
uint lineNo = 1;
|
||||
// CompileOptions does not copy the contents of the filename string pointer.
|
||||
// Passing a temporary string there will cause undefined behaviour, so we create a separate string to avoid the temporary.
|
||||
@ -850,7 +845,9 @@ bool ScriptInterface::LoadGlobalScriptFile(const VfsPath& path) const
|
||||
JS::RootedValue rval(rq.cx);
|
||||
JS::CompileOptions opts(rq.cx);
|
||||
opts.setFileAndLine(filenameStr.c_str(), lineNo);
|
||||
if (JS::Evaluate(rq.cx, opts, reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)(codeUtf16.length()), &rval))
|
||||
JS::SourceText<mozilla::Utf8Unit> src;
|
||||
ENSURE(src.init(rq.cx, code.c_str(), code.length(), JS::SourceOwnership::Borrowed));
|
||||
if (JS::Evaluate(rq.cx, opts, src, &rval))
|
||||
return true;
|
||||
|
||||
ScriptException::CatchPending(rq);
|
||||
@ -861,31 +858,27 @@ bool ScriptInterface::Eval(const char* code) const
|
||||
{
|
||||
ScriptRequest rq(this);
|
||||
JS::RootedValue rval(rq.cx);
|
||||
return Eval_(code, &rval);
|
||||
}
|
||||
|
||||
bool ScriptInterface::Eval_(const char* code, JS::MutableHandleValue rval) const
|
||||
{
|
||||
ScriptRequest rq(this);
|
||||
utf16string codeUtf16(code, code+strlen(code));
|
||||
|
||||
JS::CompileOptions opts(rq.cx);
|
||||
opts.setFileAndLine("(eval)", 1);
|
||||
if (JS::Evaluate(rq.cx, opts, reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)codeUtf16.length(), rval))
|
||||
JS::SourceText<mozilla::Utf8Unit> src;
|
||||
ENSURE(src.init(rq.cx, code, strlen(code), JS::SourceOwnership::Borrowed));
|
||||
if (JS::Evaluate(rq.cx, opts, src, &rval))
|
||||
return true;
|
||||
|
||||
ScriptException::CatchPending(rq);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScriptInterface::Eval_(const wchar_t* code, JS::MutableHandleValue rval) const
|
||||
bool ScriptInterface::Eval(const char* code, JS::MutableHandleValue rval) const
|
||||
{
|
||||
ScriptRequest rq(this);
|
||||
utf16string codeUtf16(code, code+wcslen(code));
|
||||
|
||||
JS::CompileOptions opts(rq.cx);
|
||||
opts.setFileAndLine("(eval)", 1);
|
||||
if (JS::Evaluate(rq.cx, opts, reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)codeUtf16.length(), rval))
|
||||
JS::SourceText<mozilla::Utf8Unit> src;
|
||||
ENSURE(src.init(rq.cx, code, strlen(code), JS::SourceOwnership::Borrowed));
|
||||
if (JS::Evaluate(rq.cx, opts, src, rval))
|
||||
return true;
|
||||
|
||||
ScriptException::CatchPending(rq);
|
||||
|
@ -49,6 +49,8 @@ ERROR_TYPE(Scripting_DefineType, CreationFailed);
|
||||
// but as large as necessary for all wrapped functions)
|
||||
#define SCRIPT_INTERFACE_MAX_ARGS 8
|
||||
|
||||
class JSStructuredCloneData;
|
||||
|
||||
class ScriptInterface;
|
||||
struct ScriptInterface_impl;
|
||||
|
||||
@ -61,7 +63,7 @@ extern thread_local shared_ptr<ScriptContext> g_ScriptContext;
|
||||
* RAII structure which encapsulates an access to the context and compartment of a ScriptInterface.
|
||||
* This struct provides:
|
||||
* - a pointer to the context, while acting like JSAutoRequest
|
||||
* - a pointer to the global object of the compartment, while acting like JSAutoCompartment
|
||||
* - a pointer to the global object of the compartment, while acting like JSAutoRealm
|
||||
*
|
||||
* This way, getting and using those pointers is safe with respect to the GC
|
||||
* and to the separation of compartments.
|
||||
@ -81,11 +83,11 @@ public:
|
||||
JSContext* cx;
|
||||
JSObject* glob;
|
||||
private:
|
||||
JSCompartment* m_formerCompartment;
|
||||
JS::Realm* m_formerRealm;
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstraction around a SpiderMonkey JSCompartment.
|
||||
* Abstraction around a SpiderMonkey JS::Realm.
|
||||
*
|
||||
* Thread-safety:
|
||||
* - May be used in non-main threads.
|
||||
@ -244,11 +246,6 @@ public:
|
||||
|
||||
bool FreezeObject(JS::HandleValue objVal, bool deep) const;
|
||||
|
||||
bool Eval(const char* code) const;
|
||||
|
||||
template<typename CHAR> bool Eval(const CHAR* code, JS::MutableHandleValue out) const;
|
||||
template<typename T, typename CHAR> bool Eval(const CHAR* code, T& out) const;
|
||||
|
||||
/**
|
||||
* Convert an object to a UTF-8 encoded string, either with JSON
|
||||
* (if pretty == true and there is no JSON error) or with toSource().
|
||||
@ -288,7 +285,7 @@ public:
|
||||
* @param code JS code to execute
|
||||
* @return true on successful compilation and execution; false otherwise
|
||||
*/
|
||||
bool LoadGlobalScript(const VfsPath& filename, const std::wstring& code) const;
|
||||
bool LoadGlobalScript(const VfsPath& filename, const std::string& code) const;
|
||||
|
||||
/**
|
||||
* Load and execute the given script in the global scope.
|
||||
@ -296,6 +293,14 @@ public:
|
||||
*/
|
||||
bool LoadGlobalScriptFile(const VfsPath& path) const;
|
||||
|
||||
/**
|
||||
* Evaluate some JS code in the global scope.
|
||||
* @return true on successful compilation and execution; false otherwise
|
||||
*/
|
||||
bool Eval(const char* code) const;
|
||||
bool Eval(const char* code, JS::MutableHandleValue out) const;
|
||||
template<typename T> bool Eval(const char* code, T& out) const;
|
||||
|
||||
/**
|
||||
* Convert a JS::Value to a C++ type. (This might trigger GC.)
|
||||
*/
|
||||
@ -425,8 +430,6 @@ private:
|
||||
}
|
||||
|
||||
bool CallFunction_(JS::HandleValue val, const char* name, JS::HandleValueArray argv, JS::MutableHandleValue ret) const;
|
||||
bool Eval_(const char* code, JS::MutableHandleValue ret) const;
|
||||
bool Eval_(const wchar_t* code, JS::MutableHandleValue ret) const;
|
||||
bool SetGlobal_(const char* name, JS::HandleValue value, bool replace, bool constant, bool enumerate);
|
||||
bool SetProperty_(JS::HandleValue obj, const char* name, JS::HandleValue value, bool constant, bool enumerate) const;
|
||||
bool SetProperty_(JS::HandleValue obj, const wchar_t* name, JS::HandleValue value, bool constant, bool enumerate) const;
|
||||
@ -592,20 +595,13 @@ bool ScriptInterface::GetPropertyInt(JS::HandleValue obj, int name, T& out) cons
|
||||
return FromJSVal(rq, val, out);
|
||||
}
|
||||
|
||||
template<typename CHAR>
|
||||
bool ScriptInterface::Eval(const CHAR* code, JS::MutableHandleValue ret) const
|
||||
{
|
||||
if (!Eval_(code, ret))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T, typename CHAR>
|
||||
bool ScriptInterface::Eval(const CHAR* code, T& ret) const
|
||||
template<typename T>
|
||||
bool ScriptInterface::Eval(const char* code, T& ret) const
|
||||
{
|
||||
ScriptRequest rq(this);
|
||||
JS::RootedValue rval(rq.cx);
|
||||
if (!Eval_(code, &rval))
|
||||
if (!Eval(code, &rval))
|
||||
return false;
|
||||
return FromJSVal(rq, rval, ret);
|
||||
}
|
||||
|
@ -243,10 +243,10 @@ public:
|
||||
TS_ASSERT(script.Eval(input.c_str(), &val));
|
||||
|
||||
std::string stringified = script.StringifyJSON(&val);
|
||||
TS_ASSERT_STR_EQUALS(stringified, "{\n \"x\": 1,\n \"z\": [\n 2,\n \"3\xE2\x98\xBA\xEF\xBF\xBD\"\n ],\n \"y\": true\n}");
|
||||
TS_ASSERT_STR_EQUALS(stringified, "{\n \"x\": 1,\n \"z\": [\n 2,\n \"3\u263A\\ud800\"\n ],\n \"y\": true\n}");
|
||||
|
||||
TS_ASSERT(script.ParseJSON(stringified, &val));
|
||||
TS_ASSERT_STR_EQUALS(script.ToString(&val), "({x:1, z:[2, \"3\\u263A\\uFFFD\"], y:true})");
|
||||
TS_ASSERT_STR_EQUALS(script.ToString(&val), "({x:1, z:[2, \"3\\u263A\\uD800\"], y:true})");
|
||||
}
|
||||
|
||||
// This function tests a common way to mod functions, by crating a wrapper that
|
||||
|
@ -158,7 +158,7 @@ private:
|
||||
m_ScriptInterface->SetProperty(settings, "templates", m_Worker.m_EntityTemplates, false);
|
||||
}
|
||||
|
||||
JS::AutoValueVector argv(rq.cx);
|
||||
JS::RootedValueVector argv(rq.cx);
|
||||
DISCARD argv.append(settings.get());
|
||||
m_ScriptInterface->CallConstructor(ctor, argv, &m_Obj);
|
||||
|
||||
@ -457,7 +457,7 @@ public:
|
||||
"players", playersID,
|
||||
"templates", m_EntityTemplates);
|
||||
|
||||
JS::AutoValueVector argv(rq.cx);
|
||||
JS::RootedValueVector argv(rq.cx);
|
||||
DISCARD argv.append(settings);
|
||||
m_ScriptInterface->CallConstructor(ctor, argv, &m_SharedAIObj);
|
||||
|
||||
|
@ -129,7 +129,7 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
|
||||
HandleScriptVal(bufferVal);
|
||||
break;
|
||||
}
|
||||
else if (JS_IsArrayBufferObject(obj))
|
||||
else if (JS::IsArrayBufferObject(obj))
|
||||
{
|
||||
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_ARRAY_BUFFER);
|
||||
|
||||
@ -137,11 +137,11 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
|
||||
#error TODO: need to convert JS ArrayBuffer data to little-endian
|
||||
#endif
|
||||
|
||||
u32 length = JS_GetArrayBufferByteLength(obj);
|
||||
u32 length = JS::GetArrayBufferByteLength(obj);
|
||||
m_Serializer.NumberU32_Unbounded("buffer length", length);
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
bool sharedMemory;
|
||||
m_Serializer.RawBytes("buffer data", (const u8*)JS_GetArrayBufferData(obj, &sharedMemory, nogc), length);
|
||||
m_Serializer.RawBytes("buffer data", (const u8*)JS::GetArrayBufferData(obj, &sharedMemory, nogc), length);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include "StdSerializer.h" // for DEBUG_SERIALIZER_ANNOTATE
|
||||
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
#include "scriptinterface/ScriptExtraHeaders.h" // for typed arrays
|
||||
#include "scriptinterface/ScriptExtraHeaders.h" // For typed arrays and ArrayBuffer
|
||||
|
||||
#include "lib/byte_order.h"
|
||||
|
||||
@ -284,7 +284,7 @@ JS::Value CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleOb
|
||||
throw PSERROR_Deserialize_ScriptError();
|
||||
|
||||
JS::RootedObject bufferObj(rq.cx, &bufferVal.toObject());
|
||||
if (!JS_IsArrayBufferObject(bufferObj))
|
||||
if (!JS::IsArrayBufferObject(bufferObj))
|
||||
throw PSERROR_Deserialize_ScriptError("js_IsArrayBuffer failed");
|
||||
|
||||
switch(arrayType)
|
||||
@ -335,7 +335,7 @@ JS::Value CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleOb
|
||||
void* contents = malloc(length);
|
||||
ENSURE(contents);
|
||||
RawBytes("buffer data", (u8*)contents, length);
|
||||
JS::RootedObject bufferObj(rq.cx, JS_NewArrayBufferWithContents(rq.cx, length, contents));
|
||||
JS::RootedObject bufferObj(rq.cx, JS::NewArrayBufferWithContents(rq.cx, length, contents));
|
||||
AddScriptBackref(bufferObj);
|
||||
|
||||
return JS::ObjectValue(*bufferObj);
|
||||
|
@ -89,7 +89,7 @@ void CReplayTurnManager::NotifyFinishedOwnCommands(u32 turn)
|
||||
const ScriptInterface& scriptInterface = m_Simulation2.GetScriptInterface();
|
||||
ScriptRequest rq(scriptInterface);
|
||||
|
||||
JS::AutoValueVector paramData(rq.cx);
|
||||
JS::RootedValueVector paramData(rq.cx);
|
||||
|
||||
DISCARD paramData.append(JS::NumberValue(turn));
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/Replay.h"
|
||||
#include "ps/Util.h"
|
||||
#include "scriptinterface/ScriptExtraHeaders.h" // StructuredClone
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
|
||||
|
@ -916,7 +916,7 @@ void ScenarioEditor::OnJavaScript(wxCommandEvent& WXUNUSED(event))
|
||||
wxString cmd = ::wxGetTextFromUser(_T(""), _("JS command"), _T(""), this);
|
||||
if (cmd.IsEmpty())
|
||||
return;
|
||||
POST_MESSAGE(JavaScript, ((std::wstring)cmd.wc_str()));
|
||||
POST_MESSAGE(JavaScript, (std::string(cmd.ToUTF8())));
|
||||
}
|
||||
|
||||
void ScenarioEditor::OnCameraReset(wxCommandEvent& WXUNUSED(event))
|
||||
|
@ -77,7 +77,7 @@ MESSAGE(SetViewParamS,
|
||||
);
|
||||
|
||||
MESSAGE(JavaScript,
|
||||
((std::wstring, command))
|
||||
((std::string, command))
|
||||
);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
Loading…
Reference in New Issue
Block a user