[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:
wraitii 2020-11-30 09:03:20 +00:00
parent d8332a2938
commit 02578e46bf
25 changed files with 158 additions and 125 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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>

View File

@ -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));
}

View File

@ -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));

View File

@ -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);
}

View File

@ -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));
}

View File

@ -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;

View File

@ -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)
{

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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));

View File

@ -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"

View File

@ -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))

View File

@ -77,7 +77,7 @@ MESSAGE(SetViewParamS,
);
MESSAGE(JavaScript,
((std::wstring, command))
((std::string, command))
);
//////////////////////////////////////////////////////////////////////////